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