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