Home | History | Annotate | Download | only in v8
      1 /*
      2  * Copyright (C) 2008, 2009 Google Inc. All rights reserved.
      3  * Copyright (C) 2009 Apple Inc. All rights reserved.
      4  *
      5  * Redistribution and use in source and binary forms, with or without
      6  * modification, are permitted provided that the following conditions are
      7  * met:
      8  *
      9  *     * Redistributions of source code must retain the above copyright
     10  * notice, this list of conditions and the following disclaimer.
     11  *     * Redistributions in binary form must reproduce the above
     12  * copyright notice, this list of conditions and the following disclaimer
     13  * in the documentation and/or other materials provided with the
     14  * distribution.
     15  *     * Neither the name of Google Inc. nor the names of its
     16  * contributors may be used to endorse or promote products derived from
     17  * this software without specific prior written permission.
     18  *
     19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     20  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     21  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     22  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     23  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     24  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     25  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     26  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     27  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     28  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     29  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     30  */
     31 
     32 #include "config.h"
     33 #include "bindings/v8/ScriptController.h"
     34 
     35 #include "V8Event.h"
     36 #include "V8HTMLElement.h"
     37 #include "V8Window.h"
     38 #include "bindings/v8/BindingSecurity.h"
     39 #include "bindings/v8/NPV8Object.h"
     40 #include "bindings/v8/ScriptCallStackFactory.h"
     41 #include "bindings/v8/ScriptSourceCode.h"
     42 #include "bindings/v8/ScriptValue.h"
     43 #include "bindings/v8/V8Binding.h"
     44 #include "bindings/v8/V8GCController.h"
     45 #include "bindings/v8/V8HiddenPropertyName.h"
     46 #include "bindings/v8/V8NPObject.h"
     47 #include "bindings/v8/V8PerContextData.h"
     48 #include "bindings/v8/V8ScriptRunner.h"
     49 #include "bindings/v8/V8WindowShell.h"
     50 #include "bindings/v8/npruntime_impl.h"
     51 #include "bindings/v8/npruntime_priv.h"
     52 #include "core/dom/Document.h"
     53 #include "core/dom/Event.h"
     54 #include "core/dom/EventListener.h"
     55 #include "core/dom/EventNames.h"
     56 #include "core/dom/Node.h"
     57 #include "core/dom/ScriptableDocumentParser.h"
     58 #include "core/dom/UserGestureIndicator.h"
     59 #include "core/html/HTMLPlugInElement.h"
     60 #include "core/inspector/InspectorInstrumentation.h"
     61 #include "core/inspector/ScriptCallStack.h"
     62 #include "core/loader/DocumentLoader.h"
     63 #include "core/loader/FrameLoader.h"
     64 #include "core/loader/FrameLoaderClient.h"
     65 #include "core/page/ContentSecurityPolicy.h"
     66 #include "core/page/DOMWindow.h"
     67 #include "core/page/Frame.h"
     68 #include "core/page/Page.h"
     69 #include "core/page/Settings.h"
     70 #include "core/platform/HistogramSupport.h"
     71 #include "core/platform/NotImplemented.h"
     72 #include "core/platform/Widget.h"
     73 #include "core/platform/chromium/TraceEvent.h"
     74 #include "core/plugins/PluginView.h"
     75 #include "weborigin/SecurityOrigin.h"
     76 #include "wtf/CurrentTime.h"
     77 #include "wtf/StdLibExtras.h"
     78 #include "wtf/StringExtras.h"
     79 #include "wtf/text/CString.h"
     80 #include "wtf/text/StringBuilder.h"
     81 #include "wtf/text/TextPosition.h"
     82 
     83 namespace WebCore {
     84 
     85 bool ScriptController::canAccessFromCurrentOrigin(Frame *frame)
     86 {
     87     return !v8::Context::InContext() || BindingSecurity::shouldAllowAccessToFrame(frame);
     88 }
     89 
     90 ScriptController::ScriptController(Frame* frame)
     91     : m_frame(frame)
     92     , m_sourceURL(0)
     93     , m_isolate(v8::Isolate::GetCurrent())
     94     , m_windowShell(V8WindowShell::create(frame, mainThreadNormalWorld(), m_isolate))
     95     , m_paused(false)
     96     , m_windowScriptNPObject(0)
     97 {
     98 }
     99 
    100 ScriptController::~ScriptController()
    101 {
    102     clearForClose(true);
    103 }
    104 
    105 void ScriptController::clearScriptObjects()
    106 {
    107     PluginObjectMap::iterator it = m_pluginObjects.begin();
    108     for (; it != m_pluginObjects.end(); ++it) {
    109         _NPN_UnregisterObject(it->value);
    110         _NPN_ReleaseObject(it->value);
    111     }
    112     m_pluginObjects.clear();
    113 
    114     if (m_windowScriptNPObject) {
    115         // Dispose of the underlying V8 object before releasing our reference
    116         // to it, so that if a plugin fails to release it properly we will
    117         // only leak the NPObject wrapper, not the object, its document, or
    118         // anything else they reference.
    119         disposeUnderlyingV8Object(m_windowScriptNPObject);
    120         _NPN_ReleaseObject(m_windowScriptNPObject);
    121         m_windowScriptNPObject = 0;
    122     }
    123 }
    124 
    125 void ScriptController::clearForOutOfMemory()
    126 {
    127     clearForClose(true);
    128 }
    129 
    130 void ScriptController::clearForClose(bool destroyGlobal)
    131 {
    132     m_windowShell->clearForClose(destroyGlobal);
    133     for (IsolatedWorldMap::iterator iter = m_isolatedWorlds.begin(); iter != m_isolatedWorlds.end(); ++iter)
    134         iter->value->clearForClose(destroyGlobal);
    135     V8GCController::hintForCollectGarbage();
    136 }
    137 
    138 void ScriptController::clearForClose()
    139 {
    140     double start = currentTime();
    141     clearForClose(false);
    142     HistogramSupport::histogramCustomCounts("WebCore.ScriptController.clearForClose", (currentTime() - start) * 1000, 0, 10000, 50);
    143 }
    144 
    145 void ScriptController::updateSecurityOrigin()
    146 {
    147     m_windowShell->updateSecurityOrigin();
    148 }
    149 
    150 bool ScriptController::processingUserGesture()
    151 {
    152     return UserGestureIndicator::processingUserGesture();
    153 }
    154 
    155 v8::Local<v8::Value> ScriptController::callFunction(v8::Handle<v8::Function> function, v8::Handle<v8::Object> receiver, int argc, v8::Handle<v8::Value> args[])
    156 {
    157     // Keep Frame (and therefore ScriptController) alive.
    158     RefPtr<Frame> protect(m_frame);
    159     return ScriptController::callFunctionWithInstrumentation(m_frame ? m_frame->document() : 0, function, receiver, argc, args);
    160 }
    161 
    162 ScriptValue ScriptController::callFunctionEvenIfScriptDisabled(v8::Handle<v8::Function> function, v8::Handle<v8::Object> receiver, int argc, v8::Handle<v8::Value> argv[])
    163 {
    164     // FIXME: This should probably perform the same isPaused check that happens in ScriptController::executeScript.
    165     return ScriptValue(callFunction(function, receiver, argc, argv));
    166 }
    167 
    168 static void resourceInfo(const v8::Handle<v8::Function> function, String& resourceName, int& lineNumber)
    169 {
    170     v8::ScriptOrigin origin = function->GetScriptOrigin();
    171     if (origin.ResourceName().IsEmpty()) {
    172         resourceName = "undefined";
    173         lineNumber = 1;
    174     } else {
    175         resourceName = toWebCoreString(origin.ResourceName());
    176         lineNumber = function->GetScriptLineNumber() + 1;
    177     }
    178 }
    179 
    180 static String resourceString(const v8::Handle<v8::Function> function)
    181 {
    182     String resourceName;
    183     int lineNumber;
    184     resourceInfo(function, resourceName, lineNumber);
    185 
    186     StringBuilder builder;
    187     builder.append(resourceName);
    188     builder.append(':');
    189     builder.appendNumber(lineNumber);
    190     return builder.toString();
    191 }
    192 
    193 v8::Local<v8::Value> ScriptController::callFunctionWithInstrumentation(ScriptExecutionContext* context, v8::Handle<v8::Function> function, v8::Handle<v8::Object> receiver, int argc, v8::Handle<v8::Value> args[])
    194 {
    195     InspectorInstrumentationCookie cookie;
    196     if (InspectorInstrumentation::timelineAgentEnabled(context)) {
    197         String resourceName;
    198         int lineNumber;
    199         resourceInfo(function, resourceName, lineNumber);
    200         cookie = InspectorInstrumentation::willCallFunction(context, resourceName, lineNumber);
    201     }
    202 
    203     v8::Local<v8::Value> result = V8ScriptRunner::callFunction(function, context, receiver, argc, args);
    204 
    205     InspectorInstrumentation::didCallFunction(cookie);
    206     return result;
    207 }
    208 
    209 v8::Local<v8::Value> ScriptController::compileAndRunScript(const ScriptSourceCode& source, AccessControlStatus corsStatus)
    210 {
    211     ASSERT(v8::Context::InContext());
    212 
    213     InspectorInstrumentationCookie cookie = InspectorInstrumentation::willEvaluateScript(m_frame, source.url().isNull() ? String() : source.url().string(), source.startLine());
    214 
    215     v8::Local<v8::Value> result;
    216     {
    217         // Isolate exceptions that occur when compiling and executing
    218         // the code. These exceptions should not interfere with
    219         // javascript code we might evaluate from C++ when returning
    220         // from here.
    221         v8::TryCatch tryCatch;
    222         tryCatch.SetVerbose(true);
    223 
    224         v8::Handle<v8::String> code = v8String(source.source(), m_isolate);
    225         OwnPtr<v8::ScriptData> scriptData = V8ScriptRunner::precompileScript(code, source.resource());
    226 
    227         // NOTE: For compatibility with WebCore, ScriptSourceCode's line starts at
    228         // 1, whereas v8 starts at 0.
    229         v8::Handle<v8::Script> script = V8ScriptRunner::compileScript(code, source.url(), source.startPosition(), scriptData.get(), m_isolate, corsStatus);
    230 
    231         // Keep Frame (and therefore ScriptController) alive.
    232         RefPtr<Frame> protect(m_frame);
    233         result = V8ScriptRunner::runCompiledScript(script, m_frame->document());
    234         ASSERT(!tryCatch.HasCaught() || result.IsEmpty());
    235     }
    236 
    237     InspectorInstrumentation::didEvaluateScript(cookie);
    238 
    239     return result;
    240 }
    241 
    242 bool ScriptController::initializeMainWorld()
    243 {
    244     if (m_windowShell->isContextInitialized())
    245         return false;
    246     return windowShell(mainThreadNormalWorld())->isContextInitialized();
    247 }
    248 
    249 V8WindowShell* ScriptController::existingWindowShell(DOMWrapperWorld* world)
    250 {
    251     ASSERT(world);
    252 
    253     if (world->isMainWorld())
    254         return m_windowShell->isContextInitialized() ? m_windowShell.get() : 0;
    255 
    256     // FIXME: Remove this block. See comment with existingWindowShellWorkaroundWorld().
    257     if (world == existingWindowShellWorkaroundWorld())
    258         return m_windowShell.get();
    259 
    260     IsolatedWorldMap::iterator iter = m_isolatedWorlds.find(world->worldId());
    261     if (iter == m_isolatedWorlds.end())
    262         return 0;
    263     return iter->value->isContextInitialized() ? iter->value.get() : 0;
    264 }
    265 
    266 V8WindowShell* ScriptController::windowShell(DOMWrapperWorld* world)
    267 {
    268     ASSERT(world);
    269 
    270     V8WindowShell* shell = 0;
    271     if (world->isMainWorld())
    272         shell = m_windowShell.get();
    273     else {
    274         IsolatedWorldMap::iterator iter = m_isolatedWorlds.find(world->worldId());
    275         if (iter != m_isolatedWorlds.end())
    276             shell = iter->value.get();
    277         else {
    278             OwnPtr<V8WindowShell> isolatedWorldShell = V8WindowShell::create(m_frame, world, m_isolate);
    279             shell = isolatedWorldShell.get();
    280             m_isolatedWorlds.set(world->worldId(), isolatedWorldShell.release());
    281         }
    282     }
    283     if (!shell->isContextInitialized() && shell->initializeIfNeeded()) {
    284         if (world->isMainWorld()) {
    285             // FIXME: Remove this if clause. See comment with existingWindowShellWorkaroundWorld().
    286             m_frame->loader()->dispatchDidClearWindowObjectInWorld(existingWindowShellWorkaroundWorld());
    287         } else
    288             m_frame->loader()->dispatchDidClearWindowObjectInWorld(world);
    289     }
    290     return shell;
    291 }
    292 
    293 bool ScriptController::shouldBypassMainWorldContentSecurityPolicy()
    294 {
    295     if (DOMWrapperWorld* world = isolatedWorldForEnteredContext())
    296         return world->isolatedWorldHasContentSecurityPolicy();
    297     return false;
    298 }
    299 
    300 TextPosition ScriptController::eventHandlerPosition() const
    301 {
    302     ScriptableDocumentParser* parser = m_frame->document()->scriptableDocumentParser();
    303     if (parser)
    304         return parser->textPosition();
    305     return TextPosition::minimumPosition();
    306 }
    307 
    308 static inline v8::Local<v8::Context> contextForWorld(ScriptController* scriptController, DOMWrapperWorld* world)
    309 {
    310     return scriptController->windowShell(world)->context();
    311 }
    312 
    313 v8::Local<v8::Context> ScriptController::currentWorldContext()
    314 {
    315     if (!v8::Context::InContext())
    316         return contextForWorld(this, mainThreadNormalWorld());
    317 
    318     v8::Handle<v8::Context> context = v8::Context::GetEntered();
    319     DOMWrapperWorld* isolatedWorld = DOMWrapperWorld::isolatedWorld(context);
    320     if (!isolatedWorld)
    321         return contextForWorld(this, mainThreadNormalWorld());
    322 
    323     Frame* frame = toFrameIfNotDetached(context);
    324     if (!m_frame)
    325         return v8::Local<v8::Context>();
    326 
    327     if (m_frame == frame)
    328         return v8::Local<v8::Context>::New(context);
    329 
    330     return contextForWorld(this, isolatedWorld);
    331 }
    332 
    333 v8::Local<v8::Context> ScriptController::mainWorldContext()
    334 {
    335     return contextForWorld(this, mainThreadNormalWorld());
    336 }
    337 
    338 v8::Local<v8::Context> ScriptController::mainWorldContext(Frame* frame)
    339 {
    340     if (!frame)
    341         return v8::Local<v8::Context>();
    342 
    343     return contextForWorld(frame->script(), mainThreadNormalWorld());
    344 }
    345 
    346 // Create a V8 object with an interceptor of NPObjectPropertyGetter.
    347 void ScriptController::bindToWindowObject(Frame* frame, const String& key, NPObject* object)
    348 {
    349     v8::HandleScope handleScope(m_isolate);
    350 
    351     v8::Handle<v8::Context> v8Context = ScriptController::mainWorldContext(frame);
    352     if (v8Context.IsEmpty())
    353         return;
    354 
    355     v8::Context::Scope scope(v8Context);
    356 
    357     v8::Handle<v8::Object> value = createV8ObjectForNPObject(object, 0);
    358 
    359     // Attach to the global object.
    360     v8::Handle<v8::Object> global = v8Context->Global();
    361     global->Set(v8String(key, m_isolate), value);
    362 }
    363 
    364 void ScriptController::enableEval()
    365 {
    366     if (!m_windowShell->isContextInitialized())
    367         return;
    368     v8::HandleScope handleScope(m_isolate);
    369     m_windowShell->context()->AllowCodeGenerationFromStrings(true);
    370 }
    371 
    372 void ScriptController::disableEval(const String& errorMessage)
    373 {
    374     if (!m_windowShell->isContextInitialized())
    375         return;
    376     v8::HandleScope handleScope(m_isolate);
    377     v8::Local<v8::Context> v8Context = m_windowShell->context();
    378     v8Context->AllowCodeGenerationFromStrings(false);
    379     v8Context->SetErrorMessageForCodeGenerationFromStrings(v8String(errorMessage, m_isolate));
    380 }
    381 
    382 PassScriptInstance ScriptController::createScriptInstanceForWidget(Widget* widget)
    383 {
    384     ASSERT(widget);
    385 
    386     if (!widget->isPluginView())
    387         return 0;
    388 
    389     NPObject* npObject = toPluginView(widget)->scriptableObject();
    390     if (!npObject)
    391         return 0;
    392 
    393     // Frame Memory Management for NPObjects
    394     // -------------------------------------
    395     // NPObjects are treated differently than other objects wrapped by JS.
    396     // NPObjects can be created either by the browser (e.g. the main
    397     // window object) or by the plugin (the main plugin object
    398     // for a HTMLEmbedElement). Further, unlike most DOM Objects, the frame
    399     // is especially careful to ensure NPObjects terminate at frame teardown because
    400     // if a plugin leaks a reference, it could leak its objects (or the browser's objects).
    401     //
    402     // The Frame maintains a list of plugin objects (m_pluginObjects)
    403     // which it can use to quickly find the wrapped embed object.
    404     //
    405     // Inside the NPRuntime, we've added a few methods for registering
    406     // wrapped NPObjects. The purpose of the registration is because
    407     // javascript garbage collection is non-deterministic, yet we need to
    408     // be able to tear down the plugin objects immediately. When an object
    409     // is registered, javascript can use it. When the object is destroyed,
    410     // or when the object's "owning" object is destroyed, the object will
    411     // be un-registered, and the javascript engine must not use it.
    412     //
    413     // Inside the javascript engine, the engine can keep a reference to the
    414     // NPObject as part of its wrapper. However, before accessing the object
    415     // it must consult the _NPN_Registry.
    416 
    417     v8::Local<v8::Object> wrapper = createV8ObjectForNPObject(npObject, 0);
    418 
    419     // Track the plugin object. We've been given a reference to the object.
    420     m_pluginObjects.set(widget, npObject);
    421 
    422     return V8ScriptInstance::create(wrapper);
    423 }
    424 
    425 void ScriptController::cleanupScriptObjectsForPlugin(Widget* nativeHandle)
    426 {
    427     PluginObjectMap::iterator it = m_pluginObjects.find(nativeHandle);
    428     if (it == m_pluginObjects.end())
    429         return;
    430     _NPN_UnregisterObject(it->value);
    431     _NPN_ReleaseObject(it->value);
    432     m_pluginObjects.remove(it);
    433 }
    434 
    435 V8Extensions& ScriptController::registeredExtensions()
    436 {
    437     DEFINE_STATIC_LOCAL(V8Extensions, extensions, ());
    438     return extensions;
    439 }
    440 
    441 void ScriptController::registerExtensionIfNeeded(v8::Extension* extension)
    442 {
    443     const V8Extensions& extensions = registeredExtensions();
    444     for (size_t i = 0; i < extensions.size(); ++i) {
    445         if (extensions[i] == extension)
    446             return;
    447     }
    448     v8::RegisterExtension(extension);
    449     registeredExtensions().append(extension);
    450 }
    451 
    452 static NPObject* createNoScriptObject()
    453 {
    454     notImplemented();
    455     return 0;
    456 }
    457 
    458 static NPObject* createScriptObject(Frame* frame)
    459 {
    460     v8::HandleScope handleScope;
    461     v8::Handle<v8::Context> v8Context = ScriptController::mainWorldContext(frame);
    462     if (v8Context.IsEmpty())
    463         return createNoScriptObject();
    464 
    465     v8::Context::Scope scope(v8Context);
    466     DOMWindow* window = frame->domWindow();
    467     v8::Handle<v8::Value> global = toV8(window, v8::Handle<v8::Object>(), v8Context->GetIsolate());
    468     ASSERT(global->IsObject());
    469 
    470     return npCreateV8ScriptObject(0, v8::Handle<v8::Object>::Cast(global), window);
    471 }
    472 
    473 NPObject* ScriptController::windowScriptNPObject()
    474 {
    475     if (m_windowScriptNPObject)
    476         return m_windowScriptNPObject;
    477 
    478     if (canExecuteScripts(NotAboutToExecuteScript)) {
    479         // JavaScript is enabled, so there is a JavaScript window object.
    480         // Return an NPObject bound to the window object.
    481         m_windowScriptNPObject = createScriptObject(m_frame);
    482         _NPN_RegisterObject(m_windowScriptNPObject, 0);
    483     } else {
    484         // JavaScript is not enabled, so we cannot bind the NPObject to the
    485         // JavaScript window object. Instead, we create an NPObject of a
    486         // different class, one which is not bound to a JavaScript object.
    487         m_windowScriptNPObject = createNoScriptObject();
    488     }
    489     return m_windowScriptNPObject;
    490 }
    491 
    492 NPObject* ScriptController::createScriptObjectForPluginElement(HTMLPlugInElement* plugin)
    493 {
    494     // Can't create NPObjects when JavaScript is disabled.
    495     if (!canExecuteScripts(NotAboutToExecuteScript))
    496         return createNoScriptObject();
    497 
    498     v8::HandleScope handleScope;
    499     v8::Handle<v8::Context> v8Context = ScriptController::mainWorldContext(m_frame);
    500     if (v8Context.IsEmpty())
    501         return createNoScriptObject();
    502     v8::Context::Scope scope(v8Context);
    503 
    504     DOMWindow* window = m_frame->domWindow();
    505     v8::Handle<v8::Value> v8plugin = toV8(plugin, v8::Handle<v8::Object>(), v8Context->GetIsolate());
    506     if (!v8plugin->IsObject())
    507         return createNoScriptObject();
    508 
    509     return npCreateV8ScriptObject(0, v8::Handle<v8::Object>::Cast(v8plugin), window);
    510 }
    511 
    512 void ScriptController::clearWindowShell()
    513 {
    514     double start = currentTime();
    515     // V8 binding expects ScriptController::clearWindowShell only be called
    516     // when a frame is loading a new page. This creates a new context for the new page.
    517     m_windowShell->clearForNavigation();
    518     for (IsolatedWorldMap::iterator iter = m_isolatedWorlds.begin(); iter != m_isolatedWorlds.end(); ++iter)
    519         iter->value->clearForNavigation();
    520     V8GCController::hintForCollectGarbage();
    521     HistogramSupport::histogramCustomCounts("WebCore.ScriptController.clearWindowShell", (currentTime() - start) * 1000, 0, 10000, 50);
    522 }
    523 
    524 void ScriptController::setCaptureCallStackForUncaughtExceptions(bool value)
    525 {
    526     v8::V8::SetCaptureStackTraceForUncaughtExceptions(value, ScriptCallStack::maxCallStackSizeToCapture, stackTraceOptions);
    527 }
    528 
    529 void ScriptController::collectIsolatedContexts(Vector<std::pair<ScriptState*, SecurityOrigin*> >& result)
    530 {
    531     v8::HandleScope handleScope;
    532     for (IsolatedWorldMap::iterator it = m_isolatedWorlds.begin(); it != m_isolatedWorlds.end(); ++it) {
    533         V8WindowShell* isolatedWorldShell = it->value.get();
    534         SecurityOrigin* origin = isolatedWorldShell->world()->isolatedWorldSecurityOrigin();
    535         if (!origin)
    536             continue;
    537         v8::Local<v8::Context> v8Context = isolatedWorldShell->context();
    538         if (v8Context.IsEmpty())
    539             continue;
    540         ScriptState* scriptState = ScriptState::forContext(v8Context);
    541         result.append(std::pair<ScriptState*, SecurityOrigin*>(scriptState, origin));
    542     }
    543 }
    544 
    545 bool ScriptController::setContextDebugId(int debugId)
    546 {
    547     ASSERT(debugId > 0);
    548     if (!m_windowShell->isContextInitialized())
    549         return false;
    550     v8::HandleScope scope;
    551     v8::Local<v8::Context> context = m_windowShell->context();
    552     return V8PerContextDebugData::setContextDebugData(context, "page", debugId);
    553 }
    554 
    555 int ScriptController::contextDebugId(v8::Handle<v8::Context> context)
    556 {
    557     return V8PerContextDebugData::contextDebugId(context);
    558 }
    559 
    560 void ScriptController::updateDocument()
    561 {
    562     // For an uninitialized main window shell, do not incur the cost of context initialization during FrameLoader::init().
    563     if ((!m_windowShell->isContextInitialized() || !m_windowShell->isGlobalInitialized()) && m_frame->loader()->stateMachine()->creatingInitialEmptyDocument())
    564         return;
    565 
    566     if (!initializeMainWorld())
    567         windowShell(mainThreadNormalWorld())->updateDocument();
    568 }
    569 
    570 void ScriptController::namedItemAdded(HTMLDocument* doc, const AtomicString& name)
    571 {
    572     windowShell(mainThreadNormalWorld())->namedItemAdded(doc, name);
    573 }
    574 
    575 void ScriptController::namedItemRemoved(HTMLDocument* doc, const AtomicString& name)
    576 {
    577     windowShell(mainThreadNormalWorld())->namedItemRemoved(doc, name);
    578 }
    579 
    580 bool ScriptController::canExecuteScripts(ReasonForCallingCanExecuteScripts reason)
    581 {
    582     if (m_frame->document() && m_frame->document()->isSandboxed(SandboxScripts)) {
    583         // FIXME: This message should be moved off the console once a solution to https://bugs.webkit.org/show_bug.cgi?id=103274 exists.
    584         if (reason == AboutToExecuteScript)
    585             m_frame->document()->addConsoleMessage(SecurityMessageSource, ErrorMessageLevel, "Blocked script execution in '" + m_frame->document()->url().elidedString() + "' because the document's frame is sandboxed and the 'allow-scripts' permission is not set.");
    586         return false;
    587     }
    588 
    589     if (m_frame->document() && m_frame->document()->isViewSource()) {
    590         ASSERT(m_frame->document()->securityOrigin()->isUnique());
    591         return true;
    592     }
    593 
    594     Settings* settings = m_frame->settings();
    595     const bool allowed = m_frame->loader()->client()->allowScript(settings && settings->isScriptEnabled());
    596     if (!allowed && reason == AboutToExecuteScript)
    597         m_frame->loader()->client()->didNotAllowScript();
    598     return allowed;
    599 }
    600 
    601 ScriptValue ScriptController::executeScript(const String& script, bool forceUserGesture)
    602 {
    603     UserGestureIndicator gestureIndicator(forceUserGesture ? DefinitelyProcessingNewUserGesture : PossiblyProcessingUserGesture);
    604     return executeScript(ScriptSourceCode(script, m_frame->document()->url()));
    605 }
    606 
    607 ScriptValue ScriptController::executeScript(const ScriptSourceCode& sourceCode)
    608 {
    609     if (!canExecuteScripts(AboutToExecuteScript) || isPaused())
    610         return ScriptValue();
    611 
    612     RefPtr<Frame> protect(m_frame); // Script execution can destroy the frame, and thus the ScriptController.
    613 
    614     return executeScriptInMainWorld(sourceCode);
    615 }
    616 
    617 bool ScriptController::executeScriptIfJavaScriptURL(const KURL& url)
    618 {
    619     if (!protocolIsJavaScript(url))
    620         return false;
    621 
    622     if (!m_frame->page()
    623         || !m_frame->document()->contentSecurityPolicy()->allowJavaScriptURLs(m_frame->document()->url(), eventHandlerPosition().m_line))
    624         return true;
    625 
    626     // We need to hold onto the Frame here because executing script can
    627     // destroy the frame.
    628     RefPtr<Frame> protector(m_frame);
    629     RefPtr<Document> ownerDocument(m_frame->document());
    630 
    631     const int javascriptSchemeLength = sizeof("javascript:") - 1;
    632 
    633     bool locationChangeBefore = m_frame->navigationScheduler()->locationChangePending();
    634 
    635     String decodedURL = decodeURLEscapeSequences(url.string());
    636     ScriptValue result = executeScript(decodedURL.substring(javascriptSchemeLength));
    637 
    638     // If executing script caused this frame to be removed from the page, we
    639     // don't want to try to replace its document!
    640     if (!m_frame->page())
    641         return true;
    642 
    643     String scriptResult;
    644     if (!result.getString(scriptResult))
    645         return true;
    646 
    647     // We're still in a frame, so there should be a DocumentLoader.
    648     ASSERT(m_frame->document()->loader());
    649 
    650     if (!locationChangeBefore && m_frame->navigationScheduler()->locationChangePending())
    651         return true;
    652 
    653     // DocumentWriter::replaceDocument can cause the DocumentLoader to get deref'ed and possible destroyed,
    654     // so protect it with a RefPtr.
    655     if (RefPtr<DocumentLoader> loader = m_frame->document()->loader())
    656         loader->replaceDocument(scriptResult, ownerDocument.get());
    657     return true;
    658 }
    659 
    660 ScriptValue ScriptController::executeScriptInMainWorld(const ScriptSourceCode& sourceCode, AccessControlStatus corsStatus)
    661 {
    662     String sourceURL = sourceCode.url();
    663     const String* savedSourceURL = m_sourceURL;
    664     m_sourceURL = &sourceURL;
    665 
    666     v8::HandleScope handleScope;
    667     v8::Handle<v8::Context> v8Context = ScriptController::mainWorldContext(m_frame);
    668     if (v8Context.IsEmpty())
    669         return ScriptValue();
    670 
    671     RefPtr<Frame> protect(m_frame);
    672     if (m_frame->loader()->stateMachine()->isDisplayingInitialEmptyDocument())
    673         m_frame->loader()->didAccessInitialDocument();
    674 
    675     v8::Context::Scope scope(v8Context);
    676     v8::Local<v8::Value> object = compileAndRunScript(sourceCode, corsStatus);
    677 
    678     m_sourceURL = savedSourceURL;
    679 
    680     if (object.IsEmpty())
    681         return ScriptValue();
    682 
    683     return ScriptValue(object);
    684 }
    685 
    686 void ScriptController::executeScriptInIsolatedWorld(int worldID, const Vector<ScriptSourceCode>& sources, int extensionGroup, Vector<ScriptValue>* results)
    687 {
    688     ASSERT(worldID > 0);
    689 
    690     v8::HandleScope handleScope;
    691     v8::Local<v8::Array> v8Results;
    692     {
    693         v8::HandleScope evaluateHandleScope;
    694         RefPtr<DOMWrapperWorld> world = DOMWrapperWorld::ensureIsolatedWorld(worldID, extensionGroup);
    695         V8WindowShell* isolatedWorldShell = windowShell(world.get());
    696 
    697         if (!isolatedWorldShell->isContextInitialized())
    698             return;
    699 
    700         v8::Local<v8::Context> context = isolatedWorldShell->context();
    701         v8::Context::Scope contextScope(context);
    702         v8::Local<v8::Array> resultArray = v8::Array::New(sources.size());
    703 
    704         for (size_t i = 0; i < sources.size(); ++i) {
    705             v8::Local<v8::Value> evaluationResult = compileAndRunScript(sources[i]);
    706             if (evaluationResult.IsEmpty())
    707                 evaluationResult = v8::Local<v8::Value>::New(v8::Undefined());
    708             resultArray->Set(i, evaluationResult);
    709         }
    710 
    711         v8Results = evaluateHandleScope.Close(resultArray);
    712     }
    713 
    714     if (results && !v8Results.IsEmpty()) {
    715         for (size_t i = 0; i < v8Results->Length(); ++i)
    716             results->append(ScriptValue(v8Results->Get(i)));
    717     }
    718 }
    719 
    720 } // namespace WebCore
    721