Home | History | Annotate | Download | only in v8
      1 /*
      2  * Copyright (C) 2008, 2009 Google Inc. All rights reserved.
      3  *
      4  * Redistribution and use in source and binary forms, with or without
      5  * modification, are permitted provided that the following conditions are
      6  * met:
      7  *
      8  *     * Redistributions of source code must retain the above copyright
      9  * notice, this list of conditions and the following disclaimer.
     10  *     * Redistributions in binary form must reproduce the above
     11  * copyright notice, this list of conditions and the following disclaimer
     12  * in the documentation and/or other materials provided with the
     13  * distribution.
     14  *     * Neither the name of Google Inc. nor the names of its
     15  * contributors may be used to endorse or promote products derived from
     16  * this software without specific prior written permission.
     17  *
     18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     19  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     21  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     22  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     23  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     24  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     28  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     29  */
     30 
     31 #include "config.h"
     32 #include "V8DOMWindowShell.h"
     33 
     34 #include "CString.h"
     35 #include "PlatformBridge.h"
     36 #include "CSSMutableStyleDeclaration.h"
     37 #include "DateExtension.h"
     38 #include "DocumentLoader.h"
     39 #include "DOMObjectsInclude.h"
     40 #include "Frame.h"
     41 #include "FrameLoaderClient.h"
     42 #include "InspectorTimelineAgent.h"
     43 #include "Page.h"
     44 #include "PageGroup.h"
     45 #include "ScriptController.h"
     46 #include "StorageNamespace.h"
     47 #include "V8Binding.h"
     48 #include "V8BindingState.h"
     49 #include "V8Collection.h"
     50 #include "V8ConsoleMessage.h"
     51 #include "V8DOMMap.h"
     52 #include "V8DOMWindow.h"
     53 #include "V8Document.h"
     54 #include "V8HiddenPropertyName.h"
     55 #include "V8History.h"
     56 #include "V8Index.h"
     57 #include "V8Location.h"
     58 #include "V8Proxy.h"
     59 #include "WorkerContextExecutionProxy.h"
     60 
     61 #include <algorithm>
     62 #include <stdio.h>
     63 #include <utility>
     64 #include <v8-debug.h>
     65 #include <v8.h>
     66 #include <wtf/Assertions.h>
     67 #include <wtf/OwnArrayPtr.h>
     68 #include <wtf/StdLibExtras.h>
     69 #include <wtf/StringExtras.h>
     70 #include <wtf/UnusedParam.h>
     71 
     72 #ifdef ANDROID_INSTRUMENT
     73 #include "TimeCounter.h"
     74 #endif
     75 
     76 namespace WebCore {
     77 
     78 static void handleFatalErrorInV8()
     79 {
     80     // FIXME: We temporarily deal with V8 internal error situations
     81     // such as out-of-memory by crashing the renderer.
     82     CRASH();
     83 }
     84 
     85 static void reportFatalErrorInV8(const char* location, const char* message)
     86 {
     87     // V8 is shutdown, we cannot use V8 api.
     88     // The only thing we can do is to disable JavaScript.
     89     // FIXME: clean up V8Proxy and disable JavaScript.
     90     printf("V8 error: %s (%s)\n", message, location);
     91     handleFatalErrorInV8();
     92 }
     93 
     94 // Returns the owner frame pointer of a DOM wrapper object. It only works for
     95 // these DOM objects requiring cross-domain access check.
     96 static Frame* getTargetFrame(v8::Local<v8::Object> host, v8::Local<v8::Value> data)
     97 {
     98     Frame* target = 0;
     99     switch (V8ClassIndex::FromInt(data->Int32Value())) {
    100     case V8ClassIndex::DOMWINDOW: {
    101         v8::Handle<v8::Object> window = V8DOMWrapper::lookupDOMWrapper(V8DOMWindow::GetTemplate(), host);
    102         if (window.IsEmpty())
    103             return target;
    104 
    105         DOMWindow* targetWindow = V8DOMWindow::toNative(window);
    106         target = targetWindow->frame();
    107         break;
    108     }
    109     case V8ClassIndex::LOCATION: {
    110         History* history = V8History::toNative(host);
    111         target = history->frame();
    112         break;
    113     }
    114     case V8ClassIndex::HISTORY: {
    115         Location* location = V8Location::toNative(host);
    116         target = location->frame();
    117         break;
    118     }
    119     default:
    120         break;
    121     }
    122     return target;
    123 }
    124 
    125 static void reportUnsafeJavaScriptAccess(v8::Local<v8::Object> host, v8::AccessType type, v8::Local<v8::Value> data)
    126 {
    127     Frame* target = getTargetFrame(host, data);
    128     if (target)
    129         V8Proxy::reportUnsafeAccessTo(target, V8Proxy::ReportLater);
    130 }
    131 
    132 PassRefPtr<V8DOMWindowShell> V8DOMWindowShell::create(Frame* frame)
    133 {
    134     return adoptRef(new V8DOMWindowShell(frame));
    135 }
    136 
    137 V8DOMWindowShell::V8DOMWindowShell(Frame* frame)
    138     : m_frame(frame)
    139 {
    140 }
    141 
    142 bool V8DOMWindowShell::isContextInitialized()
    143 {
    144     // m_context, m_global, and m_wrapperBoilerplates should
    145     // all be non-empty if if m_context is non-empty.
    146     ASSERT(m_context.IsEmpty() || !m_global.IsEmpty());
    147     ASSERT(m_context.IsEmpty() || !m_wrapperBoilerplates.IsEmpty());
    148     return !m_context.IsEmpty();
    149 }
    150 
    151 void V8DOMWindowShell::disposeContextHandles()
    152 {
    153     if (!m_context.IsEmpty()) {
    154         m_frame->loader()->client()->didDestroyScriptContextForFrame();
    155         m_context.Dispose();
    156         m_context.Clear();
    157     }
    158 
    159     if (!m_wrapperBoilerplates.IsEmpty()) {
    160 #ifndef NDEBUG
    161         V8GCController::unregisterGlobalHandle(this, m_wrapperBoilerplates);
    162 #endif
    163         m_wrapperBoilerplates.Dispose();
    164         m_wrapperBoilerplates.Clear();
    165     }
    166 }
    167 
    168 void V8DOMWindowShell::destroyGlobal()
    169 {
    170     if (!m_global.IsEmpty()) {
    171 #ifndef NDEBUG
    172         V8GCController::unregisterGlobalHandle(this, m_global);
    173 #endif
    174         m_global.Dispose();
    175         m_global.Clear();
    176     }
    177 }
    178 
    179 void V8DOMWindowShell::clearForClose()
    180 {
    181     if (!m_context.IsEmpty()) {
    182         v8::HandleScope handleScope;
    183 
    184         clearDocumentWrapper();
    185         disposeContextHandles();
    186     }
    187 }
    188 
    189 void V8DOMWindowShell::clearForNavigation()
    190 {
    191     if (!m_context.IsEmpty()) {
    192         v8::HandleScope handle;
    193         clearDocumentWrapper();
    194 
    195         v8::Context::Scope contextScope(m_context);
    196 
    197         // Clear the document wrapper cache before turning on access checks on
    198         // the old DOMWindow wrapper. This way, access to the document wrapper
    199         // will be protected by the security checks on the DOMWindow wrapper.
    200         clearDocumentWrapperCache();
    201 
    202         // Turn on access check on the old DOMWindow wrapper.
    203         v8::Handle<v8::Object> wrapper = V8DOMWrapper::lookupDOMWrapper(V8DOMWindow::GetTemplate(), m_global);
    204         ASSERT(!wrapper.IsEmpty());
    205         wrapper->TurnOnAccessCheck();
    206 
    207         // Separate the context from its global object.
    208         m_context->DetachGlobal();
    209 
    210         disposeContextHandles();
    211     }
    212 }
    213 
    214 // Create a new environment and setup the global object.
    215 //
    216 // The global object corresponds to a DOMWindow instance. However, to
    217 // allow properties of the JS DOMWindow instance to be shadowed, we
    218 // use a shadow object as the global object and use the JS DOMWindow
    219 // instance as the prototype for that shadow object. The JS DOMWindow
    220 // instance is undetectable from javascript code because the __proto__
    221 // accessors skip that object.
    222 //
    223 // The shadow object and the DOMWindow instance are seen as one object
    224 // from javascript. The javascript object that corresponds to a
    225 // DOMWindow instance is the shadow object. When mapping a DOMWindow
    226 // instance to a V8 object, we return the shadow object.
    227 //
    228 // To implement split-window, see
    229 //   1) https://bugs.webkit.org/show_bug.cgi?id=17249
    230 //   2) https://wiki.mozilla.org/Gecko:SplitWindow
    231 //   3) https://bugzilla.mozilla.org/show_bug.cgi?id=296639
    232 // we need to split the shadow object further into two objects:
    233 // an outer window and an inner window. The inner window is the hidden
    234 // prototype of the outer window. The inner window is the default
    235 // global object of the context. A variable declared in the global
    236 // scope is a property of the inner window.
    237 //
    238 // The outer window sticks to a Frame, it is exposed to JavaScript
    239 // via window.window, window.self, window.parent, etc. The outer window
    240 // has a security token which is the domain. The outer window cannot
    241 // have its own properties. window.foo = 'x' is delegated to the
    242 // inner window.
    243 //
    244 // When a frame navigates to a new page, the inner window is cut off
    245 // the outer window, and the outer window identify is preserved for
    246 // the frame. However, a new inner window is created for the new page.
    247 // If there are JS code holds a closure to the old inner window,
    248 // it won't be able to reach the outer window via its global object.
    249 void V8DOMWindowShell::initContextIfNeeded()
    250 {
    251     // Bail out if the context has already been initialized.
    252     if (!m_context.IsEmpty())
    253         return;
    254 
    255 #ifdef ANDROID_INSTRUMENT
    256     android::TimeCounter::start(android::TimeCounter::JavaScriptInitTimeCounter);
    257 #endif
    258 
    259     // Create a handle scope for all local handles.
    260     v8::HandleScope handleScope;
    261 
    262     // Setup the security handlers and message listener. This only has
    263     // to be done once.
    264     static bool isV8Initialized = false;
    265     if (!isV8Initialized) {
    266         // Tells V8 not to call the default OOM handler, binding code
    267         // will handle it.
    268         v8::V8::IgnoreOutOfMemoryException();
    269         v8::V8::SetFatalErrorHandler(reportFatalErrorInV8);
    270 
    271         v8::V8::SetGlobalGCPrologueCallback(&V8GCController::gcPrologue);
    272         v8::V8::SetGlobalGCEpilogueCallback(&V8GCController::gcEpilogue);
    273 
    274         v8::V8::AddMessageListener(&V8ConsoleMessage::handler);
    275 
    276         v8::V8::SetFailedAccessCheckCallbackFunction(reportUnsafeJavaScriptAccess);
    277 
    278         isV8Initialized = true;
    279     }
    280 
    281 
    282     m_context = createNewContext(m_global, 0);
    283     if (m_context.IsEmpty())
    284         return;
    285 
    286     v8::Local<v8::Context> v8Context = v8::Local<v8::Context>::New(m_context);
    287     v8::Context::Scope contextScope(v8Context);
    288 
    289     // Store the first global object created so we can reuse it.
    290     if (m_global.IsEmpty()) {
    291         m_global = v8::Persistent<v8::Object>::New(v8Context->Global());
    292         // Bail out if allocation of the first global objects fails.
    293         if (m_global.IsEmpty()) {
    294             disposeContextHandles();
    295             return;
    296         }
    297 #ifndef NDEBUG
    298         V8GCController::registerGlobalHandle(PROXY, this, m_global);
    299 #endif
    300     }
    301 
    302     installHiddenObjectPrototype(v8Context);
    303     m_wrapperBoilerplates = v8::Persistent<v8::Array>::New(v8::Array::New(V8ClassIndex::WRAPPER_TYPE_COUNT));
    304     // Bail out if allocation failed.
    305     if (m_wrapperBoilerplates.IsEmpty()) {
    306         disposeContextHandles();
    307         return;
    308     }
    309 #ifndef NDEBUG
    310     V8GCController::registerGlobalHandle(PROXY, this, m_wrapperBoilerplates);
    311 #endif
    312 
    313     if (!installDOMWindow(v8Context, m_frame->domWindow()))
    314         disposeContextHandles();
    315 
    316     updateDocument();
    317 
    318     setSecurityToken();
    319 
    320     m_frame->loader()->client()->didCreateScriptContextForFrame();
    321 
    322     // FIXME: This is wrong. We should actually do this for the proper world once
    323     // we do isolated worlds the WebCore way.
    324     m_frame->loader()->dispatchDidClearWindowObjectInWorld(0);
    325 
    326 #ifdef ANDROID_INSTRUMENT
    327     android::TimeCounter::record(android::TimeCounter::JavaScriptInitTimeCounter, __FUNCTION__);
    328 #endif
    329 }
    330 
    331 v8::Persistent<v8::Context> V8DOMWindowShell::createNewContext(v8::Handle<v8::Object> global, int extensionGroup)
    332 {
    333     v8::Persistent<v8::Context> result;
    334 
    335     // The activeDocumentLoader pointer could be 0 during frame shutdown.
    336     if (!m_frame->loader()->activeDocumentLoader())
    337         return result;
    338 
    339     // Create a new environment using an empty template for the shadow
    340     // object. Reuse the global object if one has been created earlier.
    341     v8::Persistent<v8::ObjectTemplate> globalTemplate = V8DOMWindow::GetShadowObjectTemplate();
    342     if (globalTemplate.IsEmpty())
    343         return result;
    344 
    345     // Used to avoid sleep calls in unload handlers.
    346     if (!V8Proxy::registeredExtensionWithV8(DateExtension::get()))
    347         V8Proxy::registerExtension(DateExtension::get(), String());
    348 
    349     // Dynamically tell v8 about our extensions now.
    350     const V8Extensions& extensions = V8Proxy::extensions();
    351     OwnArrayPtr<const char*> extensionNames(new const char*[extensions.size()]);
    352     int index = 0;
    353     for (size_t i = 0; i < extensions.size(); ++i) {
    354         if (extensions[i].group && extensions[i].group != extensionGroup)
    355             continue;
    356 
    357         // Note: we check the loader URL here instead of the document URL
    358         // because we might be currently loading an URL into a blank page.
    359         // See http://code.google.com/p/chromium/issues/detail?id=10924
    360         if (extensions[i].scheme.length() > 0 && (extensions[i].scheme != m_frame->loader()->activeDocumentLoader()->url().protocol() || extensions[i].scheme != m_frame->page()->mainFrame()->loader()->activeDocumentLoader()->url().protocol()))
    361             continue;
    362 
    363         extensionNames[index++] = extensions[i].extension->name();
    364     }
    365     v8::ExtensionConfiguration extensionConfiguration(index, extensionNames.get());
    366     result = v8::Context::New(&extensionConfiguration, globalTemplate, global);
    367 
    368     return result;
    369 }
    370 
    371 void V8DOMWindowShell::setContext(v8::Handle<v8::Context> context)
    372 {
    373     // if we already have a context, clear it before setting the new one.
    374     if (!m_context.IsEmpty()) {
    375         m_context.Dispose();
    376         m_context.Clear();
    377     }
    378     m_context = v8::Persistent<v8::Context>::New(context);
    379 }
    380 
    381 bool V8DOMWindowShell::installDOMWindow(v8::Handle<v8::Context> context, DOMWindow* window)
    382 {
    383     v8::Handle<v8::String> implicitProtoString = v8::String::New("__proto__");
    384     if (implicitProtoString.IsEmpty())
    385         return false;
    386 
    387     // Create a new JS window object and use it as the prototype for the  shadow global object.
    388     v8::Handle<v8::Function> windowConstructor = V8DOMWrapper::getConstructor(V8ClassIndex::DOMWINDOW, getHiddenObjectPrototype(context));
    389     v8::Local<v8::Object> jsWindow = SafeAllocation::newInstance(windowConstructor);
    390     // Bail out if allocation failed.
    391     if (jsWindow.IsEmpty())
    392         return false;
    393 
    394     // Wrap the window.
    395     V8DOMWrapper::setDOMWrapper(jsWindow, V8ClassIndex::ToInt(V8ClassIndex::DOMWINDOW), window);
    396     V8DOMWrapper::setDOMWrapper(v8::Handle<v8::Object>::Cast(jsWindow->GetPrototype()), V8ClassIndex::ToInt(V8ClassIndex::DOMWINDOW), window);
    397 
    398     window->ref();
    399     V8DOMWrapper::setJSWrapperForDOMObject(window, v8::Persistent<v8::Object>::New(jsWindow));
    400 
    401     // Insert the window instance as the prototype of the shadow object.
    402     v8::Handle<v8::Object> v8Global = context->Global();
    403     V8DOMWrapper::setDOMWrapper(v8::Handle<v8::Object>::Cast(v8Global->GetPrototype()), V8ClassIndex::ToInt(V8ClassIndex::DOMWINDOW), window);
    404     v8Global->Set(implicitProtoString, jsWindow);
    405     return true;
    406 }
    407 
    408 void V8DOMWindowShell::updateDocumentWrapper(v8::Handle<v8::Object> wrapper)
    409 {
    410     clearDocumentWrapper();
    411 
    412     ASSERT(m_document.IsEmpty());
    413     m_document = v8::Persistent<v8::Object>::New(wrapper);
    414 #ifndef NDEBUG
    415     V8GCController::registerGlobalHandle(PROXY, this, m_document);
    416 #endif
    417 }
    418 
    419 void V8DOMWindowShell::clearDocumentWrapper()
    420 {
    421     if (!m_document.IsEmpty()) {
    422 #ifndef NDEBUG
    423         V8GCController::unregisterGlobalHandle(this, m_document);
    424 #endif
    425         m_document.Dispose();
    426         m_document.Clear();
    427     }
    428 }
    429 
    430 void V8DOMWindowShell::updateDocumentWrapperCache()
    431 {
    432     v8::HandleScope handleScope;
    433     v8::Context::Scope contextScope(m_context);
    434 
    435     // If the document has no frame, NodeToV8Object might get the
    436     // document wrapper for a document that is about to be deleted.
    437     // If the ForceSet below causes a garbage collection, the document
    438     // might get deleted and the global handle for the document
    439     // wrapper cleared. Using the cleared global handle will lead to
    440     // crashes. In this case we clear the cache and let the DOMWindow
    441     // accessor handle access to the document.
    442     if (!m_frame->document()->frame()) {
    443         clearDocumentWrapperCache();
    444         return;
    445     }
    446 
    447     v8::Handle<v8::Value> documentWrapper = toV8(m_frame->document());
    448 
    449     // If instantiation of the document wrapper fails, clear the cache
    450     // and let the DOMWindow accessor handle access to the document.
    451     if (documentWrapper.IsEmpty()) {
    452         clearDocumentWrapperCache();
    453         return;
    454     }
    455     ASSERT(documentWrapper->IsObject());
    456     m_context->Global()->ForceSet(v8::String::New("document"), documentWrapper, static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontDelete));
    457 }
    458 
    459 void V8DOMWindowShell::clearDocumentWrapperCache()
    460 {
    461     ASSERT(!m_context.IsEmpty());
    462     m_context->Global()->ForceDelete(v8::String::New("document"));
    463 }
    464 
    465 void V8DOMWindowShell::setSecurityToken()
    466 {
    467     Document* document = m_frame->document();
    468     // Setup security origin and security token.
    469     if (!document) {
    470         m_context->UseDefaultSecurityToken();
    471         return;
    472     }
    473 
    474     // Ask the document's SecurityOrigin to generate a security token.
    475     // If two tokens are equal, then the SecurityOrigins canAccess each other.
    476     // If two tokens are not equal, then we have to call canAccess.
    477     // Note: we can't use the HTTPOrigin if it was set from the DOM.
    478     SecurityOrigin* origin = document->securityOrigin();
    479     String token;
    480     if (!origin->domainWasSetInDOM())
    481         token = document->securityOrigin()->toString();
    482 
    483     // An empty or "null" token means we always have to call
    484     // canAccess. The toString method on securityOrigins returns the
    485     // string "null" for empty security origins and for security
    486     // origins that should only allow access to themselves. In this
    487     // case, we use the global object as the security token to avoid
    488     // calling canAccess when a script accesses its own objects.
    489     if (token.isEmpty() || token == "null") {
    490         m_context->UseDefaultSecurityToken();
    491         return;
    492     }
    493 
    494     CString utf8Token = token.utf8();
    495     // NOTE: V8 does identity comparison in fast path, must use a symbol
    496     // as the security token.
    497     m_context->SetSecurityToken(v8::String::NewSymbol(utf8Token.data(), utf8Token.length()));
    498 }
    499 
    500 void V8DOMWindowShell::updateDocument()
    501 {
    502     if (!m_frame->document())
    503         return;
    504 
    505     if (m_global.IsEmpty())
    506         return;
    507 
    508     // There is an existing JavaScript wrapper for the global object
    509     // of this frame. JavaScript code in other frames might hold a
    510     // reference to this wrapper. We eagerly initialize the JavaScript
    511     // context for the new document to make property access on the
    512     // global object wrapper succeed.
    513     initContextIfNeeded();
    514 
    515     // Bail out if context initialization failed.
    516     if (m_context.IsEmpty())
    517         return;
    518 
    519     // We have a new document and we need to update the cache.
    520     updateDocumentWrapperCache();
    521 
    522     updateSecurityOrigin();
    523 }
    524 
    525 void V8DOMWindowShell::updateSecurityOrigin()
    526 {
    527     v8::HandleScope scope;
    528     setSecurityToken();
    529 }
    530 
    531 v8::Handle<v8::Value> V8DOMWindowShell::getHiddenObjectPrototype(v8::Handle<v8::Context> context)
    532 {
    533     return context->Global()->GetHiddenValue(V8HiddenPropertyName::objectPrototype());
    534 }
    535 
    536 void V8DOMWindowShell::installHiddenObjectPrototype(v8::Handle<v8::Context> context)
    537 {
    538     v8::Handle<v8::String> objectString = v8::String::New("Object");
    539     v8::Handle<v8::String> prototypeString = v8::String::New("prototype");
    540     v8::Handle<v8::String> hiddenObjectPrototypeString = V8HiddenPropertyName::objectPrototype();
    541     // Bail out if allocation failed.
    542     if (objectString.IsEmpty() || prototypeString.IsEmpty() || hiddenObjectPrototypeString.IsEmpty())
    543         return;
    544 
    545     v8::Handle<v8::Object> object = v8::Handle<v8::Object>::Cast(context->Global()->Get(objectString));
    546     v8::Handle<v8::Value> objectPrototype = object->Get(prototypeString);
    547 
    548     context->Global()->SetHiddenValue(hiddenObjectPrototypeString, objectPrototype);
    549 }
    550 
    551 v8::Local<v8::Object> V8DOMWindowShell::createWrapperFromCacheSlowCase(V8ClassIndex::V8WrapperType type)
    552 {
    553     // Not in cache.
    554     int classIndex = V8ClassIndex::ToInt(type);
    555     initContextIfNeeded();
    556     v8::Context::Scope scope(m_context);
    557     v8::Local<v8::Function> function = V8DOMWrapper::getConstructor(type, getHiddenObjectPrototype(m_context));
    558     v8::Local<v8::Object> instance = SafeAllocation::newInstance(function);
    559     if (!instance.IsEmpty()) {
    560         m_wrapperBoilerplates->Set(v8::Integer::New(classIndex), instance);
    561         return instance->Clone();
    562     }
    563     return notHandledByInterceptor();
    564 }
    565 
    566 void V8DOMWindowShell::setLocation(DOMWindow* window, const String& relativeURL)
    567 {
    568     Frame* frame = window->frame();
    569     if (!frame)
    570         return;
    571 
    572     KURL url = completeURL(relativeURL);
    573     if (url.isNull())
    574         return;
    575 
    576     if (!shouldAllowNavigation(frame))
    577         return;
    578 
    579     navigateIfAllowed(frame, url, false, false);
    580 }
    581 
    582 } // WebCore
    583