2 * Copyright (C) 2008, 2009 Google Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
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
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.
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.
34 #include "ChromiumBridge.h"
35 #include "CSSMutableStyleDeclaration.h"
36 #include "DateExtension.h"
37 #include "DOMObjectsInclude.h"
38 #include "DocumentLoader.h"
39 #include "FrameLoaderClient.h"
40 #include "InspectorTimelineAgent.h"
42 #include "PageGroup.h"
43 #include "ScriptController.h"
44 #include "StorageNamespace.h"
45 #include "V8Binding.h"
46 #include "V8Collection.h"
47 #include "V8ConsoleMessage.h"
48 #include "V8CustomBinding.h"
50 #include "V8DOMWindow.h"
51 #include "V8HiddenPropertyName.h"
53 #include "V8IsolatedWorld.h"
54 #include "WorkerContextExecutionProxy.h"
60 #include <wtf/Assertions.h>
61 #include <wtf/OwnArrayPtr.h>
62 #include <wtf/StdLibExtras.h>
63 #include <wtf/UnusedParam.h>
67 v8::Persistent<v8::Context> V8Proxy::m_utilityContext;
69 // Static list of registered extensions
70 V8Extensions V8Proxy::m_extensions;
72 const char* V8Proxy::kContextDebugDataType = "type";
73 const char* V8Proxy::kContextDebugDataValue = "value";
75 void batchConfigureAttributes(v8::Handle<v8::ObjectTemplate> instance,
76 v8::Handle<v8::ObjectTemplate> proto,
77 const BatchedAttribute* attributes,
78 size_t attributeCount)
80 for (size_t i = 0; i < attributeCount; ++i)
81 configureAttribute(instance, proto, attributes[i]);
84 void batchConfigureCallbacks(v8::Handle<v8::ObjectTemplate> proto,
85 v8::Handle<v8::Signature> signature,
86 v8::PropertyAttribute attributes,
87 const BatchedCallback* callbacks,
90 for (size_t i = 0; i < callbackCount; ++i) {
91 proto->Set(v8::String::New(callbacks[i].name),
92 v8::FunctionTemplate::New(callbacks[i].callback,
93 v8::Handle<v8::Value>(),
99 void batchConfigureConstants(v8::Handle<v8::FunctionTemplate> functionDescriptor,
100 v8::Handle<v8::ObjectTemplate> proto,
101 const BatchedConstant* constants,
102 size_t constantCount)
104 for (size_t i = 0; i < constantCount; ++i) {
105 const BatchedConstant* constant = &constants[i];
106 functionDescriptor->Set(v8::String::New(constant->name), v8::Integer::New(constant->value), v8::ReadOnly);
107 proto->Set(v8::String::New(constant->name), v8::Integer::New(constant->value), v8::ReadOnly);
111 typedef HashMap<Node*, v8::Object*> DOMNodeMap;
112 typedef HashMap<void*, v8::Object*> DOMObjectMap;
115 // Map of SVG objects with contexts to their contexts
116 static HashMap<void*, SVGElement*>& svgObjectToContextMap()
118 typedef HashMap<void*, SVGElement*> SvgObjectToContextMap;
119 DEFINE_STATIC_LOCAL(SvgObjectToContextMap, staticSvgObjectToContextMap, ());
120 return staticSvgObjectToContextMap;
123 void V8Proxy::setSVGContext(void* object, SVGElement* context)
128 SVGElement* oldContext = svgObjectToContextMap().get(object);
130 if (oldContext == context)
139 svgObjectToContextMap().set(object, context);
142 SVGElement* V8Proxy::svgContext(void* object)
144 return svgObjectToContextMap().get(object);
149 typedef HashMap<int, v8::FunctionTemplate*> FunctionTemplateMap;
151 bool AllowAllocation::m_current = false;
153 void logInfo(Frame* frame, const String& message, const String& url)
155 Page* page = frame->page();
158 V8ConsoleMessage consoleMessage(message, url, 0);
159 consoleMessage.dispatchNow(page);
162 enum DelayReporting {
167 static void reportUnsafeAccessTo(Frame* target, DelayReporting delay)
170 Document* targetDocument = target->document();
174 Frame* source = V8Proxy::retrieveFrameForEnteredContext();
175 if (!source || !source->document())
176 return; // Ignore error if the source document is gone.
178 Document* sourceDocument = source->document();
180 // FIXME: This error message should contain more specifics of why the same
181 // origin check has failed.
182 String str = String::format("Unsafe JavaScript attempt to access frame "
183 "with URL %s from frame with URL %s. "
184 "Domains, protocols and ports must match.\n",
185 targetDocument->url().string().utf8().data(),
186 sourceDocument->url().string().utf8().data());
188 // Build a console message with fake source ID and line number.
189 const String kSourceID = "";
190 const int kLineNumber = 1;
191 V8ConsoleMessage message(str, kSourceID, kLineNumber);
193 if (delay == ReportNow) {
194 // NOTE: Safari prints the message in the target page, but it seems like
195 // it should be in the source page. Even for delayed messages, we put it in
196 // the source page; see V8ConsoleMessage::processDelayed().
197 message.dispatchNow(source->page());
199 ASSERT(delay == ReportLater);
200 // We cannot safely report the message eagerly, because this may cause
201 // allocations and GCs internally in V8 and we cannot handle that at this
202 // point. Therefore we delay the reporting.
203 message.dispatchLater();
207 static void reportUnsafeJavaScriptAccess(v8::Local<v8::Object> host, v8::AccessType type, v8::Local<v8::Value> data)
209 Frame* target = V8Custom::GetTargetFrame(host, data);
211 reportUnsafeAccessTo(target, ReportLater);
214 static void handleFatalErrorInV8()
216 // FIXME: We temporarily deal with V8 internal error situations
217 // such as out-of-memory by crashing the renderer.
221 static void reportFatalErrorInV8(const char* location, const char* message)
223 // V8 is shutdown, we cannot use V8 api.
224 // The only thing we can do is to disable JavaScript.
225 // FIXME: clean up V8Proxy and disable JavaScript.
226 printf("V8 error: %s (%s)\n", message, location);
227 handleFatalErrorInV8();
230 V8Proxy::V8Proxy(Frame* frame)
232 , m_inlineCode(false)
233 , m_timerCallback(false)
244 void V8Proxy::destroyGlobal()
246 if (!m_global.IsEmpty()) {
248 V8GCController::unregisterGlobalHandle(this, m_global);
255 v8::Handle<v8::Script> V8Proxy::compileScript(v8::Handle<v8::String> code, const String& fileName, int baseLine)
257 const uint16_t* fileNameString = fromWebCoreString(fileName);
258 v8::Handle<v8::String> name = v8::String::New(fileNameString, fileName.length());
259 v8::Handle<v8::Integer> line = v8::Integer::New(baseLine);
260 v8::ScriptOrigin origin(name, line);
261 v8::Handle<v8::Script> script = v8::Script::Compile(code, &origin);
265 bool V8Proxy::handleOutOfMemory()
267 v8::Local<v8::Context> context = v8::Context::GetCurrent();
269 if (!context->HasOutOfMemoryException())
272 // Warning, error, disable JS for this frame?
273 Frame* frame = V8Proxy::retrieveFrame(context);
275 V8Proxy* proxy = V8Proxy::retrieve(frame);
277 // Clean m_context, and event handlers.
278 proxy->clearForClose();
280 proxy->destroyGlobal();
283 ChromiumBridge::notifyJSOutOfMemory(frame);
286 Settings* settings = frame->settings();
288 settings->setJavaScriptEnabled(false);
293 void V8Proxy::evaluateInIsolatedWorld(int worldID, const Vector<ScriptSourceCode>& sources, int extensionGroup)
295 initContextIfNeeded();
297 v8::HandleScope handleScope;
298 V8IsolatedWorld* world = 0;
301 IsolatedWorldMap::iterator iter = m_isolatedWorlds.find(worldID);
302 if (iter != m_isolatedWorlds.end()) {
303 world = iter->second;
305 world = new V8IsolatedWorld(this, extensionGroup);
306 if (world->context().IsEmpty()) {
311 m_isolatedWorlds.set(worldID, world);
313 // Setup context id for JS debugger.
314 if (!setInjectedScriptContextDebugId(world->context())) {
315 m_isolatedWorlds.take(worldID);
321 world = new V8IsolatedWorld(this, extensionGroup);
322 if (world->context().IsEmpty()) {
328 v8::Local<v8::Context> context = v8::Local<v8::Context>::New(world->context());
329 v8::Context::Scope context_scope(context);
330 for (size_t i = 0; i < sources.size(); ++i)
331 evaluate(sources[i], 0);
337 void V8Proxy::evaluateInNewContext(const Vector<ScriptSourceCode>& sources, int extensionGroup)
339 initContextIfNeeded();
341 v8::HandleScope handleScope;
343 // Set up the DOM window as the prototype of the new global object.
344 v8::Handle<v8::Context> windowContext = m_context;
345 v8::Handle<v8::Object> windowGlobal = windowContext->Global();
346 v8::Handle<v8::Object> windowWrapper = V8DOMWrapper::lookupDOMWrapper(V8ClassIndex::DOMWINDOW, windowGlobal);
348 ASSERT(V8DOMWrapper::convertDOMWrapperToNative<DOMWindow>(windowWrapper) == m_frame->domWindow());
350 v8::Persistent<v8::Context> context = createNewContext(v8::Handle<v8::Object>(), extensionGroup);
351 if (context.IsEmpty())
354 v8::Context::Scope contextScope(context);
356 // Setup context id for JS debugger.
357 if (!setInjectedScriptContextDebugId(context)) {
362 v8::Handle<v8::Object> global = context->Global();
364 v8::Handle<v8::String> implicitProtoString = v8::String::New("__proto__");
365 global->Set(implicitProtoString, windowWrapper);
367 // Give the code running in the new context a way to get access to the
369 global->Set(v8::String::New("contentWindow"), windowGlobal);
371 m_frame->loader()->client()->didCreateIsolatedScriptContext();
373 // Run code in the new context.
374 for (size_t i = 0; i < sources.size(); ++i)
375 evaluate(sources[i], 0);
377 // Using the default security token means that the canAccess is always
378 // called, which is slow.
379 // FIXME: Use tokens where possible. This will mean keeping track of all
380 // created contexts so that they can all be updated when the document domain
382 context->UseDefaultSecurityToken();
386 bool V8Proxy::setInjectedScriptContextDebugId(v8::Handle<v8::Context> targetContext)
388 // Setup context id for JS debugger.
389 v8::Context::Scope contextScope(targetContext);
390 v8::Handle<v8::Object> contextData = v8::Object::New();
391 if (contextData.IsEmpty())
394 if (m_context.IsEmpty())
396 v8::Handle<v8::Value> windowContextData = m_context->GetData();
397 if (windowContextData->IsObject()) {
398 v8::Handle<v8::String> propertyName = v8::String::New(kContextDebugDataValue);
399 if (propertyName.IsEmpty())
401 contextData->Set(propertyName, v8::Object::Cast(*windowContextData)->Get(propertyName));
403 v8::Handle<v8::String> propertyName = v8::String::New(kContextDebugDataType);
404 if (propertyName.IsEmpty())
406 contextData->Set(propertyName, v8::String::New("injected"));
407 targetContext->SetData(contextData);
411 v8::Local<v8::Value> V8Proxy::evaluate(const ScriptSourceCode& source, Node* node)
413 ASSERT(v8::Context::InContext());
415 V8GCController::checkMemoryUsage();
417 #if ENABLE(INSPECTOR)
418 if (InspectorTimelineAgent* timelineAgent = m_frame->page() ? m_frame->page()->inspectorTimelineAgent() : 0)
419 timelineAgent->willEvaluateScript(source.url().isNull() ? String() : source.url().string(), source.startLine());
422 v8::Local<v8::Value> result;
424 // Isolate exceptions that occur when compiling and executing
425 // the code. These exceptions should not interfere with
426 // javascript code we might evaluate from C++ when returning
428 v8::TryCatch tryCatch;
429 tryCatch.SetVerbose(true);
431 // Compile the script.
432 v8::Local<v8::String> code = v8ExternalString(source.source());
433 ChromiumBridge::traceEventBegin("v8.compile", node, "");
435 // NOTE: For compatibility with WebCore, ScriptSourceCode's line starts at
436 // 1, whereas v8 starts at 0.
437 v8::Handle<v8::Script> script = compileScript(code, source.url(), source.startLine() - 1);
438 ChromiumBridge::traceEventEnd("v8.compile", node, "");
440 ChromiumBridge::traceEventBegin("v8.run", node, "");
441 // Set inlineCode to true for <a href="javascript:doSomething()">
442 // and false for <script>doSomething</script>. We make a rough guess at
443 // this based on whether the script source has a URL.
444 result = runScript(script, source.url().string().isNull());
446 ChromiumBridge::traceEventEnd("v8.run", node, "");
448 #if ENABLE(INSPECTOR)
449 if (InspectorTimelineAgent* timelineAgent = m_frame->page() ? m_frame->page()->inspectorTimelineAgent() : 0)
450 timelineAgent->didEvaluateScript();
456 v8::Local<v8::Value> V8Proxy::runScript(v8::Handle<v8::Script> script, bool isInlineCode)
458 if (script.IsEmpty())
459 return notHandledByInterceptor();
461 V8GCController::checkMemoryUsage();
462 // Compute the source string and prevent against infinite recursion.
463 if (m_recursion >= kMaxRecursionDepth) {
464 v8::Local<v8::String> code = v8ExternalString("throw RangeError('Recursion too deep')");
465 // FIXME: Ideally, we should be able to re-use the origin of the
466 // script passed to us as the argument instead of using an empty string
468 script = compileScript(code, "", 0);
471 if (handleOutOfMemory())
472 ASSERT(script.IsEmpty());
474 if (script.IsEmpty())
475 return notHandledByInterceptor();
477 // Save the previous value of the inlineCode flag and update the flag for
478 // the duration of the script invocation.
479 bool previousInlineCode = inlineCode();
480 setInlineCode(isInlineCode);
482 // Run the script and keep track of the current recursion depth.
483 v8::Local<v8::Value> result;
485 V8ConsoleMessage::Scope scope;
487 // See comment in V8Proxy::callFunction.
488 m_frame->keepAlive();
491 result = script->Run();
495 // Release the storage mutex if applicable.
496 releaseStorageMutex();
498 if (handleOutOfMemory())
499 ASSERT(result.IsEmpty());
501 // Handle V8 internal error situation (Out-of-memory).
502 if (result.IsEmpty())
503 return notHandledByInterceptor();
505 // Restore inlineCode flag.
506 setInlineCode(previousInlineCode);
508 if (v8::V8::IsDead())
509 handleFatalErrorInV8();
514 v8::Local<v8::Value> V8Proxy::callFunction(v8::Handle<v8::Function> function, v8::Handle<v8::Object> receiver, int argc, v8::Handle<v8::Value> args[])
516 V8GCController::checkMemoryUsage();
517 v8::Local<v8::Value> result;
519 V8ConsoleMessage::Scope scope;
521 if (m_recursion >= kMaxRecursionDepth) {
522 v8::Local<v8::String> code = v8::String::New("throw new RangeError('Maximum call stack size exceeded.')");
525 v8::Local<v8::Script> script = v8::Script::Compile(code);
526 if (script.IsEmpty())
532 // Evaluating the JavaScript could cause the frame to be deallocated,
533 // so we start the keep alive timer here.
534 // Frame::keepAlive method adds the ref count of the frame and sets a
535 // timer to decrease the ref count. It assumes that the current JavaScript
536 // execution finishs before firing the timer.
537 m_frame->keepAlive();
540 result = function->Call(receiver, argc, args);
544 // Release the storage mutex if applicable.
545 releaseStorageMutex();
547 if (v8::V8::IsDead())
548 handleFatalErrorInV8();
553 v8::Local<v8::Value> V8Proxy::newInstance(v8::Handle<v8::Function> constructor, int argc, v8::Handle<v8::Value> args[])
555 // No artificial limitations on the depth of recursion, see comment in
556 // V8Proxy::callFunction.
557 v8::Local<v8::Value> result;
559 V8ConsoleMessage::Scope scope;
561 // See comment in V8Proxy::callFunction.
562 m_frame->keepAlive();
564 result = constructor->NewInstance(argc, args);
567 if (v8::V8::IsDead())
568 handleFatalErrorInV8();
573 v8::Local<v8::Object> V8Proxy::createWrapperFromCacheSlowCase(V8ClassIndex::V8WrapperType type)
576 int classIndex = V8ClassIndex::ToInt(type);
577 initContextIfNeeded();
578 v8::Context::Scope scope(m_context);
579 v8::Local<v8::Function> function = V8DOMWrapper::getConstructor(type, getHiddenObjectPrototype(m_context));
580 v8::Local<v8::Object> instance = SafeAllocation::newInstance(function);
581 if (!instance.IsEmpty()) {
582 m_wrapperBoilerplates->Set(v8::Integer::New(classIndex), instance);
583 return instance->Clone();
585 return notHandledByInterceptor();
588 bool V8Proxy::isContextInitialized()
590 // m_context, m_global, and m_wrapperBoilerplates should
591 // all be non-empty if if m_context is non-empty.
592 ASSERT(m_context.IsEmpty() || !m_global.IsEmpty());
593 ASSERT(m_context.IsEmpty() || !m_wrapperBoilerplates.IsEmpty());
594 return !m_context.IsEmpty();
597 DOMWindow* V8Proxy::retrieveWindow(v8::Handle<v8::Context> context)
599 v8::Handle<v8::Object> global = context->Global();
600 ASSERT(!global.IsEmpty());
601 global = V8DOMWrapper::lookupDOMWrapper(V8ClassIndex::DOMWINDOW, global);
602 ASSERT(!global.IsEmpty());
603 return V8DOMWrapper::convertToNativeObject<DOMWindow>(V8ClassIndex::DOMWINDOW, global);
606 Frame* V8Proxy::retrieveFrame(v8::Handle<v8::Context> context)
608 DOMWindow* window = retrieveWindow(context);
609 Frame* frame = window->frame();
610 if (frame && frame->domWindow() == window)
612 // We return 0 here because |context| is detached from the Frame. If we
613 // did return |frame| we could get in trouble because the frame could be
614 // navigated to another security origin.
618 Frame* V8Proxy::retrieveFrameForEnteredContext()
620 v8::Handle<v8::Context> context = v8::Context::GetEntered();
621 if (context.IsEmpty())
623 return retrieveFrame(context);
626 Frame* V8Proxy::retrieveFrameForCurrentContext()
628 v8::Handle<v8::Context> context = v8::Context::GetCurrent();
629 if (context.IsEmpty())
631 return retrieveFrame(context);
634 Frame* V8Proxy::retrieveFrameForCallingContext()
636 v8::Handle<v8::Context> context = v8::Context::GetCalling();
637 if (context.IsEmpty())
639 return retrieveFrame(context);
642 V8Proxy* V8Proxy::retrieve()
644 DOMWindow* window = retrieveWindow(currentContext());
646 return retrieve(window->frame());
649 V8Proxy* V8Proxy::retrieve(Frame* frame)
653 return frame->script()->isEnabled() ? frame->script()->proxy() : 0;
656 V8Proxy* V8Proxy::retrieve(ScriptExecutionContext* context)
658 if (!context->isDocument())
660 return retrieve(static_cast<Document*>(context)->frame());
663 void V8Proxy::disconnectFrame()
667 void V8Proxy::updateDocumentWrapper(v8::Handle<v8::Value> wrapper)
669 clearDocumentWrapper();
671 ASSERT(m_document.IsEmpty());
672 m_document = v8::Persistent<v8::Value>::New(wrapper);
674 V8GCController::registerGlobalHandle(PROXY, this, m_document);
678 void V8Proxy::clearDocumentWrapper()
680 if (!m_document.IsEmpty()) {
682 V8GCController::unregisterGlobalHandle(this, m_document);
684 m_document.Dispose();
689 void V8Proxy::updateDocumentWrapperCache()
691 v8::HandleScope handleScope;
692 v8::Context::Scope contextScope(m_context);
694 // If the document has no frame, NodeToV8Object might get the
695 // document wrapper for a document that is about to be deleted.
696 // If the ForceSet below causes a garbage collection, the document
697 // might get deleted and the global handle for the document
698 // wrapper cleared. Using the cleared global handle will lead to
699 // crashes. In this case we clear the cache and let the DOMWindow
700 // accessor handle access to the document.
701 if (!m_frame->document()->frame()) {
702 clearDocumentWrapperCache();
706 v8::Handle<v8::Value> documentWrapper = V8DOMWrapper::convertNodeToV8Object(m_frame->document());
708 // If instantiation of the document wrapper fails, clear the cache
709 // and let the DOMWindow accessor handle access to the document.
710 if (documentWrapper.IsEmpty()) {
711 clearDocumentWrapperCache();
714 m_context->Global()->ForceSet(v8::String::New("document"), documentWrapper, static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontDelete));
717 void V8Proxy::clearDocumentWrapperCache()
719 ASSERT(!m_context.IsEmpty());
720 m_context->Global()->ForceDelete(v8::String::New("document"));
723 void V8Proxy::disposeContextHandles()
725 if (!m_context.IsEmpty()) {
726 m_frame->loader()->client()->didDestroyScriptContextForFrame();
731 if (!m_wrapperBoilerplates.IsEmpty()) {
733 V8GCController::unregisterGlobalHandle(this, m_wrapperBoilerplates);
735 m_wrapperBoilerplates.Dispose();
736 m_wrapperBoilerplates.Clear();
740 void V8Proxy::releaseStorageMutex()
742 // If we've just left a top level script context and local storage has been
743 // instantiated, we must ensure that any storage locks have been freed.
744 // Per http://dev.w3.org/html5/spec/Overview.html#storage-mutex
745 if (m_recursion != 0)
747 Page* page = m_frame->page();
750 if (page->group().hasLocalStorage())
751 page->group().localStorage()->unlock();
754 void V8Proxy::resetIsolatedWorlds()
756 for (IsolatedWorldMap::iterator iter = m_isolatedWorlds.begin();
757 iter != m_isolatedWorlds.end(); ++iter) {
758 iter->second->destroy();
760 m_isolatedWorlds.clear();
763 void V8Proxy::clearForClose()
765 resetIsolatedWorlds();
767 if (!m_context.IsEmpty()) {
768 v8::HandleScope handleScope;
770 clearDocumentWrapper();
771 disposeContextHandles();
775 void V8Proxy::clearForNavigation()
777 resetIsolatedWorlds();
779 if (!m_context.IsEmpty()) {
780 v8::HandleScope handle;
781 clearDocumentWrapper();
783 v8::Context::Scope contextScope(m_context);
785 // Clear the document wrapper cache before turning on access checks on
786 // the old DOMWindow wrapper. This way, access to the document wrapper
787 // will be protected by the security checks on the DOMWindow wrapper.
788 clearDocumentWrapperCache();
790 // Turn on access check on the old DOMWindow wrapper.
791 v8::Handle<v8::Object> wrapper = V8DOMWrapper::lookupDOMWrapper(V8ClassIndex::DOMWINDOW, m_global);
792 ASSERT(!wrapper.IsEmpty());
793 wrapper->TurnOnAccessCheck();
795 // Separate the context from its global object.
796 m_context->DetachGlobal();
798 disposeContextHandles();
802 void V8Proxy::setSecurityToken()
804 Document* document = m_frame->document();
805 // Setup security origin and security token.
807 m_context->UseDefaultSecurityToken();
811 // Ask the document's SecurityOrigin to generate a security token.
812 // If two tokens are equal, then the SecurityOrigins canAccess each other.
813 // If two tokens are not equal, then we have to call canAccess.
814 // Note: we can't use the HTTPOrigin if it was set from the DOM.
815 SecurityOrigin* origin = document->securityOrigin();
817 if (!origin->domainWasSetInDOM())
818 token = document->securityOrigin()->toString();
820 // An empty or "null" token means we always have to call
821 // canAccess. The toString method on securityOrigins returns the
822 // string "null" for empty security origins and for security
823 // origins that should only allow access to themselves. In this
824 // case, we use the global object as the security token to avoid
825 // calling canAccess when a script accesses its own objects.
826 if (token.isEmpty() || token == "null") {
827 m_context->UseDefaultSecurityToken();
831 CString utf8Token = token.utf8();
832 // NOTE: V8 does identity comparison in fast path, must use a symbol
833 // as the security token.
834 m_context->SetSecurityToken(v8::String::NewSymbol(utf8Token.data(), utf8Token.length()));
837 void V8Proxy::updateDocument()
839 if (!m_frame->document())
842 if (m_global.IsEmpty())
845 // There is an existing JavaScript wrapper for the global object
846 // of this frame. JavaScript code in other frames might hold a
847 // reference to this wrapper. We eagerly initialize the JavaScript
848 // context for the new document to make property access on the
849 // global object wrapper succeed.
850 initContextIfNeeded();
852 // Bail out if context initialization failed.
853 if (m_context.IsEmpty())
856 // We have a new document and we need to update the cache.
857 updateDocumentWrapperCache();
859 updateSecurityOrigin();
862 void V8Proxy::updateSecurityOrigin()
864 v8::HandleScope scope;
868 // Same origin policy implementation:
870 // Same origin policy prevents JS code from domain A access JS & DOM objects
871 // in a different domain B. There are exceptions and several objects are
872 // accessible by cross-domain code. For example, the window.frames object is
873 // accessible by code from a different domain, but window.document is not.
875 // The binding code sets security check callbacks on a function template,
876 // and accessing instances of the template calls the callback function.
877 // The callback function checks same origin policy.
879 // Callback functions are expensive. V8 uses a security token string to do
880 // fast access checks for the common case where source and target are in the
881 // same domain. A security token is a string object that represents
882 // the protocol/url/port of a domain.
884 // There are special cases where a security token matching is not enough.
885 // For example, JavaScript can set its domain to a super domain by calling
886 // document.setDomain(...). In these cases, the binding code can reset
887 // a context's security token to its global object so that the fast access
888 // check will always fail.
890 // Check if the current execution context can access a target frame.
891 // First it checks same domain policy using the lexical context
893 // This is equivalent to KJS::Window::allowsAccessFrom(ExecState*, String&).
894 bool V8Proxy::canAccessPrivate(DOMWindow* targetWindow)
896 ASSERT(targetWindow);
900 DOMWindow* originWindow = retrieveWindow(currentContext());
901 if (originWindow == targetWindow)
907 const SecurityOrigin* activeSecurityOrigin = originWindow->securityOrigin();
908 const SecurityOrigin* targetSecurityOrigin = targetWindow->securityOrigin();
910 // We have seen crashes were the security origin of the target has not been
911 // initialized. Defend against that.
912 if (!targetSecurityOrigin)
915 if (activeSecurityOrigin->canAccess(targetSecurityOrigin))
918 // Allow access to a "about:blank" page if the dynamic context is a
919 // detached context of the same frame as the blank page.
920 if (targetSecurityOrigin->isEmpty() && originWindow->frame() == targetWindow->frame())
926 bool V8Proxy::canAccessFrame(Frame* target, bool reportError)
928 // The subject is detached from a frame, deny accesses.
932 if (!canAccessPrivate(target->domWindow())) {
934 reportUnsafeAccessTo(target, ReportNow);
940 bool V8Proxy::checkNodeSecurity(Node* node)
945 Frame* target = node->document()->frame();
950 return canAccessFrame(target, true);
953 v8::Persistent<v8::Context> V8Proxy::createNewContext(v8::Handle<v8::Object> global, int extensionGroup)
955 v8::Persistent<v8::Context> result;
957 // The activeDocumentLoader pointer could be NULL during frame shutdown.
958 if (!m_frame->loader()->activeDocumentLoader())
961 // Create a new environment using an empty template for the shadow
962 // object. Reuse the global object if one has been created earlier.
963 v8::Persistent<v8::ObjectTemplate> globalTemplate = V8DOMWindow::GetShadowObjectTemplate();
964 if (globalTemplate.IsEmpty())
967 // Install a security handler with V8.
968 globalTemplate->SetAccessCheckCallbacks(V8Custom::v8DOMWindowNamedSecurityCheck, V8Custom::v8DOMWindowIndexedSecurityCheck, v8::Integer::New(V8ClassIndex::DOMWINDOW));
969 globalTemplate->SetInternalFieldCount(V8Custom::kDOMWindowInternalFieldCount);
971 // Used to avoid sleep calls in unload handlers.
972 if (!registeredExtensionWithV8(DateExtension::get()))
973 registerExtension(DateExtension::get(), String());
975 // Dynamically tell v8 about our extensions now.
976 OwnArrayPtr<const char*> extensionNames(new const char*[m_extensions.size()]);
978 for (size_t i = 0; i < m_extensions.size(); ++i) {
979 if (m_extensions[i].group && m_extensions[i].group != extensionGroup)
982 // Note: we check the loader URL here instead of the document URL
983 // because we might be currently loading an URL into a blank page.
984 // See http://code.google.com/p/chromium/issues/detail?id=10924
985 if (m_extensions[i].scheme.length() > 0 && (m_extensions[i].scheme != m_frame->loader()->activeDocumentLoader()->url().protocol() || m_extensions[i].scheme != m_frame->page()->mainFrame()->loader()->activeDocumentLoader()->url().protocol()))
988 extensionNames[index++] = m_extensions[i].extension->name();
990 v8::ExtensionConfiguration extensions(index, extensionNames.get());
991 result = v8::Context::New(&extensions, globalTemplate, global);
996 bool V8Proxy::installDOMWindow(v8::Handle<v8::Context> context, DOMWindow* window)
998 v8::Handle<v8::String> implicitProtoString = v8::String::New("__proto__");
999 if (implicitProtoString.IsEmpty())
1002 // Create a new JS window object and use it as the prototype for the shadow global object.
1003 v8::Handle<v8::Function> windowConstructor = V8DOMWrapper::getConstructor(V8ClassIndex::DOMWINDOW, getHiddenObjectPrototype(context));
1004 v8::Local<v8::Object> jsWindow = SafeAllocation::newInstance(windowConstructor);
1005 // Bail out if allocation failed.
1006 if (jsWindow.IsEmpty())
1010 V8DOMWrapper::setDOMWrapper(jsWindow, V8ClassIndex::ToInt(V8ClassIndex::DOMWINDOW), window);
1011 V8DOMWrapper::setDOMWrapper(v8::Handle<v8::Object>::Cast(jsWindow->GetPrototype()), V8ClassIndex::ToInt(V8ClassIndex::DOMWINDOW), window);
1014 V8DOMWrapper::setJSWrapperForDOMObject(window, v8::Persistent<v8::Object>::New(jsWindow));
1016 // Insert the window instance as the prototype of the shadow object.
1017 v8::Handle<v8::Object> v8Global = context->Global();
1018 V8DOMWrapper::setDOMWrapper(v8::Handle<v8::Object>::Cast(v8Global->GetPrototype()), V8ClassIndex::ToInt(V8ClassIndex::DOMWINDOW), window);
1019 v8Global->Set(implicitProtoString, jsWindow);
1023 // Create a new environment and setup the global object.
1025 // The global object corresponds to a DOMWindow instance. However, to
1026 // allow properties of the JS DOMWindow instance to be shadowed, we
1027 // use a shadow object as the global object and use the JS DOMWindow
1028 // instance as the prototype for that shadow object. The JS DOMWindow
1029 // instance is undetectable from javascript code because the __proto__
1030 // accessors skip that object.
1032 // The shadow object and the DOMWindow instance are seen as one object
1033 // from javascript. The javascript object that corresponds to a
1034 // DOMWindow instance is the shadow object. When mapping a DOMWindow
1035 // instance to a V8 object, we return the shadow object.
1037 // To implement split-window, see
1038 // 1) https://bugs.webkit.org/show_bug.cgi?id=17249
1039 // 2) https://wiki.mozilla.org/Gecko:SplitWindow
1040 // 3) https://bugzilla.mozilla.org/show_bug.cgi?id=296639
1041 // we need to split the shadow object further into two objects:
1042 // an outer window and an inner window. The inner window is the hidden
1043 // prototype of the outer window. The inner window is the default
1044 // global object of the context. A variable declared in the global
1045 // scope is a property of the inner window.
1047 // The outer window sticks to a Frame, it is exposed to JavaScript
1048 // via window.window, window.self, window.parent, etc. The outer window
1049 // has a security token which is the domain. The outer window cannot
1050 // have its own properties. window.foo = 'x' is delegated to the
1053 // When a frame navigates to a new page, the inner window is cut off
1054 // the outer window, and the outer window identify is preserved for
1055 // the frame. However, a new inner window is created for the new page.
1056 // If there are JS code holds a closure to the old inner window,
1057 // it won't be able to reach the outer window via its global object.
1058 void V8Proxy::initContextIfNeeded()
1060 // Bail out if the context has already been initialized.
1061 if (!m_context.IsEmpty())
1064 // Create a handle scope for all local handles.
1065 v8::HandleScope handleScope;
1067 // Setup the security handlers and message listener. This only has
1069 static bool isV8Initialized = false;
1070 if (!isV8Initialized) {
1071 // Tells V8 not to call the default OOM handler, binding code
1073 v8::V8::IgnoreOutOfMemoryException();
1074 v8::V8::SetFatalErrorHandler(reportFatalErrorInV8);
1076 v8::V8::SetGlobalGCPrologueCallback(&V8GCController::gcPrologue);
1077 v8::V8::SetGlobalGCEpilogueCallback(&V8GCController::gcEpilogue);
1079 v8::V8::AddMessageListener(&V8ConsoleMessage::handler);
1081 v8::V8::SetFailedAccessCheckCallbackFunction(reportUnsafeJavaScriptAccess);
1083 isV8Initialized = true;
1087 m_context = createNewContext(m_global, 0);
1088 if (m_context.IsEmpty())
1091 v8::Local<v8::Context> v8Context = v8::Local<v8::Context>::New(m_context);
1092 v8::Context::Scope contextScope(v8Context);
1094 // Store the first global object created so we can reuse it.
1095 if (m_global.IsEmpty()) {
1096 m_global = v8::Persistent<v8::Object>::New(v8Context->Global());
1097 // Bail out if allocation of the first global objects fails.
1098 if (m_global.IsEmpty()) {
1099 disposeContextHandles();
1103 V8GCController::registerGlobalHandle(PROXY, this, m_global);
1107 installHiddenObjectPrototype(v8Context);
1108 m_wrapperBoilerplates = v8::Persistent<v8::Array>::New(v8::Array::New(V8ClassIndex::WRAPPER_TYPE_COUNT));
1109 // Bail out if allocation failed.
1110 if (m_wrapperBoilerplates.IsEmpty()) {
1111 disposeContextHandles();
1115 V8GCController::registerGlobalHandle(PROXY, this, m_wrapperBoilerplates);
1118 if (!installDOMWindow(v8Context, m_frame->domWindow()))
1119 disposeContextHandles();
1125 m_frame->loader()->client()->didCreateScriptContextForFrame();
1127 // FIXME: This is wrong. We should actually do this for the proper world once
1128 // we do isolated worlds the WebCore way.
1129 m_frame->loader()->dispatchDidClearWindowObjectInWorld(0);
1132 void V8Proxy::setDOMException(int exceptionCode)
1134 if (exceptionCode <= 0)
1137 ExceptionCodeDescription description;
1138 getExceptionCodeDescription(exceptionCode, description);
1140 v8::Handle<v8::Value> exception;
1141 switch (description.type) {
1142 case DOMExceptionType:
1143 exception = V8DOMWrapper::convertToV8Object(V8ClassIndex::DOMCOREEXCEPTION, DOMCoreException::create(description));
1145 case RangeExceptionType:
1146 exception = V8DOMWrapper::convertToV8Object(V8ClassIndex::RANGEEXCEPTION, RangeException::create(description));
1148 case EventExceptionType:
1149 exception = V8DOMWrapper::convertToV8Object(V8ClassIndex::EVENTEXCEPTION, EventException::create(description));
1151 case XMLHttpRequestExceptionType:
1152 exception = V8DOMWrapper::convertToV8Object(V8ClassIndex::XMLHTTPREQUESTEXCEPTION, XMLHttpRequestException::create(description));
1155 case SVGExceptionType:
1156 exception = V8DOMWrapper::convertToV8Object(V8ClassIndex::SVGEXCEPTION, SVGException::create(description));
1160 case XPathExceptionType:
1161 exception = V8DOMWrapper::convertToV8Object(V8ClassIndex::XPATHEXCEPTION, XPathException::create(description));
1166 ASSERT(!exception.IsEmpty());
1167 v8::ThrowException(exception);
1170 v8::Handle<v8::Value> V8Proxy::throwError(ErrorType type, const char* message)
1174 return v8::ThrowException(v8::Exception::RangeError(v8String(message)));
1175 case ReferenceError:
1176 return v8::ThrowException(v8::Exception::ReferenceError(v8String(message)));
1178 return v8::ThrowException(v8::Exception::SyntaxError(v8String(message)));
1180 return v8::ThrowException(v8::Exception::TypeError(v8String(message)));
1182 return v8::ThrowException(v8::Exception::Error(v8String(message)));
1184 ASSERT_NOT_REACHED();
1185 return notHandledByInterceptor();
1189 v8::Local<v8::Context> V8Proxy::context(Frame* frame)
1191 v8::Local<v8::Context> context = V8Proxy::mainWorldContext(frame);
1192 if (context.IsEmpty())
1193 return v8::Local<v8::Context>();
1195 if (V8IsolatedWorld* world = V8IsolatedWorld::getEntered()) {
1196 context = v8::Local<v8::Context>::New(world->context());
1197 if (frame != V8Proxy::retrieveFrame(context))
1198 return v8::Local<v8::Context>();
1204 v8::Local<v8::Context> V8Proxy::context()
1206 if (V8IsolatedWorld* world = V8IsolatedWorld::getEntered()) {
1207 RefPtr<SharedPersistent<v8::Context> > context = world->sharedContext();
1208 if (m_frame != V8Proxy::retrieveFrame(context->get()))
1209 return v8::Local<v8::Context>();
1210 return v8::Local<v8::Context>::New(context->get());
1212 initContextIfNeeded();
1213 return v8::Local<v8::Context>::New(m_context);;
1216 v8::Local<v8::Context> V8Proxy::mainWorldContext(Frame* frame)
1218 V8Proxy* proxy = retrieve(frame);
1220 return v8::Local<v8::Context>();
1222 proxy->initContextIfNeeded();
1223 return v8::Local<v8::Context>::New(proxy->m_context);
1226 v8::Local<v8::Context> V8Proxy::currentContext()
1228 return v8::Context::GetCurrent();
1231 v8::Handle<v8::Value> V8Proxy::checkNewLegal(const v8::Arguments& args)
1233 if (!AllowAllocation::m_current)
1234 return throwError(TypeError, "Illegal constructor");
1239 void V8Proxy::bindJsObjectToWindow(Frame* frame, const char* name, int type, v8::Handle<v8::FunctionTemplate> descriptor, void* impl)
1242 v8::Handle<v8::Context> v8Context = V8Proxy::mainWorldContext(frame);
1243 if (v8Context.IsEmpty())
1244 return; // JS not enabled.
1246 v8::Context::Scope scope(v8Context);
1247 v8::Handle<v8::Object> instance = descriptor->GetFunction();
1248 V8DOMWrapper::setDOMWrapper(instance, type, impl);
1250 v8::Handle<v8::Object> global = v8Context->Global();
1251 global->Set(v8::String::New(name), instance);
1254 void V8Proxy::processConsoleMessages()
1256 V8ConsoleMessage::processDelayed();
1259 // Create the utility context for holding JavaScript functions used internally
1260 // which are not visible to JavaScript executing on the page.
1261 void V8Proxy::createUtilityContext()
1263 ASSERT(m_utilityContext.IsEmpty());
1265 v8::HandleScope scope;
1266 v8::Handle<v8::ObjectTemplate> globalTemplate = v8::ObjectTemplate::New();
1267 m_utilityContext = v8::Context::New(0, globalTemplate);
1268 v8::Context::Scope contextScope(m_utilityContext);
1270 // Compile JavaScript function for retrieving the source line of the top
1271 // JavaScript stack frame.
1272 DEFINE_STATIC_LOCAL(const char*, frameSourceLineSource,
1273 ("function frameSourceLine(exec_state) {"
1274 " return exec_state.frame(0).sourceLine();"
1276 v8::Script::Compile(v8::String::New(frameSourceLineSource))->Run();
1278 // Compile JavaScript function for retrieving the source name of the top
1279 // JavaScript stack frame.
1280 DEFINE_STATIC_LOCAL(const char*, frameSourceNameSource,
1281 ("function frameSourceName(exec_state) {"
1282 " var frame = exec_state.frame(0);"
1283 " if (frame.func().resolved() && "
1284 " frame.func().script() && "
1285 " frame.func().script().name()) {"
1286 " return frame.func().script().name();"
1289 v8::Script::Compile(v8::String::New(frameSourceNameSource))->Run();
1292 bool V8Proxy::sourceLineNumber(int& result)
1294 v8::HandleScope scope;
1295 v8::Handle<v8::Context> v8UtilityContext = V8Proxy::utilityContext();
1296 if (v8UtilityContext.IsEmpty())
1298 v8::Context::Scope contextScope(v8UtilityContext);
1299 v8::Handle<v8::Function> frameSourceLine;
1300 frameSourceLine = v8::Local<v8::Function>::Cast(v8UtilityContext->Global()->Get(v8::String::New("frameSourceLine")));
1301 if (frameSourceLine.IsEmpty())
1303 v8::Handle<v8::Value> value = v8::Debug::Call(frameSourceLine);
1304 if (value.IsEmpty())
1306 result = value->Int32Value();
1310 bool V8Proxy::sourceName(String& result)
1312 v8::HandleScope scope;
1313 v8::Handle<v8::Context> v8UtilityContext = utilityContext();
1314 if (v8UtilityContext.IsEmpty())
1316 v8::Context::Scope contextScope(v8UtilityContext);
1317 v8::Handle<v8::Function> frameSourceName;
1318 frameSourceName = v8::Local<v8::Function>::Cast(v8UtilityContext->Global()->Get(v8::String::New("frameSourceName")));
1319 if (frameSourceName.IsEmpty())
1321 v8::Handle<v8::Value> value = v8::Debug::Call(frameSourceName);
1322 if (value.IsEmpty())
1324 result = toWebCoreString(value);
1328 void V8Proxy::registerExtensionWithV8(v8::Extension* extension)
1330 // If the extension exists in our list, it was already registered with V8.
1331 if (!registeredExtensionWithV8(extension))
1332 v8::RegisterExtension(extension);
1335 bool V8Proxy::registeredExtensionWithV8(v8::Extension* extension)
1337 for (size_t i = 0; i < m_extensions.size(); ++i) {
1338 if (m_extensions[i].extension == extension)
1345 void V8Proxy::registerExtension(v8::Extension* extension, const String& schemeRestriction)
1347 registerExtensionWithV8(extension);
1348 V8ExtensionInfo info = {schemeRestriction, 0, extension};
1349 m_extensions.append(info);
1352 void V8Proxy::registerExtension(v8::Extension* extension, int extensionGroup)
1354 registerExtensionWithV8(extension);
1355 V8ExtensionInfo info = {String(), extensionGroup, extension};
1356 m_extensions.append(info);
1359 bool V8Proxy::setContextDebugId(int debugId)
1361 ASSERT(debugId > 0);
1362 if (m_context.IsEmpty())
1364 v8::HandleScope scope;
1365 if (!m_context->GetData()->IsUndefined())
1368 v8::Context::Scope contextScope(m_context);
1369 v8::Handle<v8::Object> contextData = v8::Object::New();
1370 contextData->Set(v8::String::New(kContextDebugDataType), v8::String::New("page"));
1371 contextData->Set(v8::String::New(kContextDebugDataValue), v8::Integer::New(debugId));
1372 m_context->SetData(contextData);
1376 int V8Proxy::contextDebugId(v8::Handle<v8::Context> context)
1378 v8::HandleScope scope;
1379 if (!context->GetData()->IsObject())
1381 v8::Handle<v8::Value> data = context->GetData()->ToObject()->Get( v8::String::New(kContextDebugDataValue));
1382 return data->IsInt32() ? data->Int32Value() : -1;
1385 v8::Handle<v8::Value> V8Proxy::getHiddenObjectPrototype(v8::Handle<v8::Context> context)
1387 return context->Global()->GetHiddenValue(V8HiddenPropertyName::objectPrototype());
1390 void V8Proxy::installHiddenObjectPrototype(v8::Handle<v8::Context> context)
1392 v8::Handle<v8::String> objectString = v8::String::New("Object");
1393 v8::Handle<v8::String> prototypeString = v8::String::New("prototype");
1394 v8::Handle<v8::String> hiddenObjectPrototypeString = V8HiddenPropertyName::objectPrototype();
1395 // Bail out if allocation failed.
1396 if (objectString.IsEmpty() || prototypeString.IsEmpty() || hiddenObjectPrototypeString.IsEmpty())
1399 v8::Handle<v8::Object> object = v8::Handle<v8::Object>::Cast(context->Global()->Get(objectString));
1400 v8::Handle<v8::Value> objectPrototype = object->Get(prototypeString);
1402 context->Global()->SetHiddenValue(hiddenObjectPrototypeString, objectPrototype);
1405 v8::Local<v8::Context> toV8Context(ScriptExecutionContext* context)
1407 if (context->isDocument()) {
1408 if (V8Proxy* proxy = V8Proxy::retrieve(context))
1409 return proxy->context();
1410 } else if (context->isWorkerContext()) {
1411 if (WorkerContextExecutionProxy* proxy = static_cast<WorkerContext*>(context)->script()->proxy())
1412 return proxy->context();
1414 return v8::Local<v8::Context>();
1417 } // namespace WebCore