• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
handleFatalErrorInV8()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 
reportFatalErrorInV8(const char * location,const char * message)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.
getTargetFrame(v8::Local<v8::Object> host,v8::Local<v8::Value> data)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 
reportUnsafeJavaScriptAccess(v8::Local<v8::Object> host,v8::AccessType type,v8::Local<v8::Value> data)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 
create(Frame * frame)132 PassRefPtr<V8DOMWindowShell> V8DOMWindowShell::create(Frame* frame)
133 {
134     return adoptRef(new V8DOMWindowShell(frame));
135 }
136 
V8DOMWindowShell(Frame * frame)137 V8DOMWindowShell::V8DOMWindowShell(Frame* frame)
138     : m_frame(frame)
139 {
140 }
141 
isContextInitialized()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 
disposeContextHandles()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 
destroyGlobal()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 
clearForClose()179 void V8DOMWindowShell::clearForClose()
180 {
181     if (!m_context.IsEmpty()) {
182         v8::HandleScope handleScope;
183 
184         clearDocumentWrapper();
185         disposeContextHandles();
186     }
187 }
188 
clearForNavigation()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.
initContextIfNeeded()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 
createNewContext(v8::Handle<v8::Object> global,int extensionGroup)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 
setContext(v8::Handle<v8::Context> context)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 
installDOMWindow(v8::Handle<v8::Context> context,DOMWindow * window)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 
updateDocumentWrapper(v8::Handle<v8::Object> wrapper)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 
clearDocumentWrapper()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 
updateDocumentWrapperCache()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 
clearDocumentWrapperCache()459 void V8DOMWindowShell::clearDocumentWrapperCache()
460 {
461     ASSERT(!m_context.IsEmpty());
462     m_context->Global()->ForceDelete(v8::String::New("document"));
463 }
464 
setSecurityToken()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 
updateDocument()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 
updateSecurityOrigin()525 void V8DOMWindowShell::updateSecurityOrigin()
526 {
527     v8::HandleScope scope;
528     setSecurityToken();
529 }
530 
getHiddenObjectPrototype(v8::Handle<v8::Context> context)531 v8::Handle<v8::Value> V8DOMWindowShell::getHiddenObjectPrototype(v8::Handle<v8::Context> context)
532 {
533     return context->Global()->GetHiddenValue(V8HiddenPropertyName::objectPrototype());
534 }
535 
installHiddenObjectPrototype(v8::Handle<v8::Context> context)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 
createWrapperFromCacheSlowCase(V8ClassIndex::V8WrapperType type)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 
setLocation(DOMWindow * window,const String & relativeURL)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