blob: 97c22f5981191615ac5d2f4b4fbbdf1c02b3e7fb [file] [log] [blame]
/*
* Copyright (C) 2008, 2009 Google Inc. All rights reserved.
* Copyright (C) 2009 Apple Inc. All rights reserved.
* Copyright (C) 2014 Opera Software ASA. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "third_party/blink/renderer/bindings/core/v8/script_controller.h"
#include <memory>
#include <utility>
#include "base/callback_helpers.h"
#include "third_party/blink/renderer/bindings/core/v8/script_source_code.h"
#include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.h"
#include "third_party/blink/renderer/bindings/core/v8/v8_gc_controller.h"
#include "third_party/blink/renderer/bindings/core/v8/v8_script_runner.h"
#include "third_party/blink/renderer/bindings/core/v8/window_proxy.h"
#include "third_party/blink/renderer/core/dom/document.h"
#include "third_party/blink/renderer/core/dom/scriptable_document_parser.h"
#include "third_party/blink/renderer/core/execution_context/execution_context.h"
#include "third_party/blink/renderer/core/frame/csp/content_security_policy.h"
#include "third_party/blink/renderer/core/frame/local_dom_window.h"
#include "third_party/blink/renderer/core/frame/local_frame.h"
#include "third_party/blink/renderer/core/frame/local_frame_client.h"
#include "third_party/blink/renderer/core/html/html_plugin_element.h"
#include "third_party/blink/renderer/core/inspector/console_message.h"
#include "third_party/blink/renderer/core/inspector/inspector_trace_events.h"
#include "third_party/blink/renderer/core/inspector/main_thread_debugger.h"
#include "third_party/blink/renderer/core/loader/document_loader.h"
#include "third_party/blink/renderer/core/loader/frame_loader.h"
#include "third_party/blink/renderer/core/loader/progress_tracker.h"
#include "third_party/blink/renderer/core/probe/core_probes.h"
#include "third_party/blink/renderer/core/script/classic_script.h"
#include "third_party/blink/renderer/platform/bindings/dom_wrapper_world.h"
#include "third_party/blink/renderer/platform/instrumentation/histogram.h"
#include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h"
#include "third_party/blink/renderer/platform/instrumentation/use_counter.h"
#include "third_party/blink/renderer/platform/weborigin/security_origin.h"
#include "third_party/blink/renderer/platform/wtf/assertions.h"
#include "third_party/blink/renderer/platform/wtf/std_lib_extras.h"
#include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
#include "third_party/blink/renderer/platform/wtf/text/string_utf8_adaptor.h"
namespace blink {
void ScriptController::Trace(Visitor* visitor) const {
visitor->Trace(window_);
visitor->Trace(window_proxy_manager_);
}
void ScriptController::UpdateSecurityOrigin(
const SecurityOrigin* security_origin) {
window_proxy_manager_->UpdateSecurityOrigin(security_origin);
}
TextPosition ScriptController::EventHandlerPosition() const {
ScriptableDocumentParser* parser =
window_->document()->GetScriptableDocumentParser();
if (parser)
return parser->GetTextPosition();
return TextPosition::MinimumPosition();
}
void ScriptController::EnableEval() {
SetEvalForWorld(DOMWrapperWorld::MainWorld(), true /* allow_eval */,
g_empty_string /* error_message */);
}
void ScriptController::DisableEval(const String& error_message) {
SetEvalForWorld(DOMWrapperWorld::MainWorld(), false /* allow_eval */,
error_message);
}
void ScriptController::DisableEvalForIsolatedWorld(
int32_t world_id,
const String& error_message) {
DCHECK(DOMWrapperWorld::IsIsolatedWorldId(world_id));
scoped_refptr<DOMWrapperWorld> world =
DOMWrapperWorld::EnsureIsolatedWorld(GetIsolate(), world_id);
SetEvalForWorld(*world, false /* allow_eval */, error_message);
}
void ScriptController::SetEvalForWorld(DOMWrapperWorld& world,
bool allow_eval,
const String& error_message) {
v8::HandleScope handle_scope(GetIsolate());
LocalWindowProxy* proxy =
world.IsMainWorld()
? window_proxy_manager_->MainWorldProxyMaybeUninitialized()
: WindowProxy(world);
v8::Local<v8::Context> v8_context = proxy->ContextIfInitialized();
if (v8_context.IsEmpty())
return;
v8_context->AllowCodeGenerationFromStrings(allow_eval);
if (allow_eval)
return;
v8_context->SetErrorMessageForCodeGenerationFromStrings(
V8String(GetIsolate(), error_message));
}
namespace {
Vector<const char*>& RegisteredExtensionNames() {
DEFINE_STATIC_LOCAL(Vector<const char*>, extension_names, ());
return extension_names;
}
} // namespace
void ScriptController::RegisterExtensionIfNeeded(
std::unique_ptr<v8::Extension> extension) {
for (const auto* extension_name : RegisteredExtensionNames()) {
if (!strcmp(extension_name, extension->name()))
return;
}
RegisteredExtensionNames().push_back(extension->name());
v8::RegisterExtension(std::move(extension));
}
v8::ExtensionConfiguration ScriptController::ExtensionsFor(
const ExecutionContext* context) {
if (context->ShouldInstallV8Extensions()) {
return v8::ExtensionConfiguration(RegisteredExtensionNames().size(),
RegisteredExtensionNames().data());
}
return v8::ExtensionConfiguration();
}
void ScriptController::UpdateDocument() {
window_proxy_manager_->UpdateDocument();
}
void ScriptController::ExecuteJavaScriptURL(
const KURL& url,
network::mojom::CSPDisposition csp_disposition,
const DOMWrapperWorld* world_for_csp) {
DCHECK(url.ProtocolIsJavaScript());
const int kJavascriptSchemeLength = sizeof("javascript:") - 1;
String script_source = DecodeURLEscapeSequences(
url.GetString(), DecodeURLMode::kUTF8OrIsomorphic);
if (!window_->GetFrame())
return;
auto* policy = window_->GetContentSecurityPolicyForWorld(world_for_csp);
if (csp_disposition == network::mojom::CSPDisposition::CHECK &&
!policy->AllowInline(ContentSecurityPolicy::InlineType::kNavigation,
nullptr, script_source, String() /* nonce */,
window_->Url(), EventHandlerPosition().line_)) {
return;
}
// TODO(crbug.com/896041): Investigate how trusted type checks can be
// implemented for isolated worlds.
const bool should_bypass_trusted_type_check =
csp_disposition == network::mojom::CSPDisposition::DO_NOT_CHECK ||
ContentSecurityPolicy::ShouldBypassMainWorld(world_for_csp);
script_source = script_source.Substring(kJavascriptSchemeLength);
if (!should_bypass_trusted_type_check) {
script_source = TrustedTypesCheckForJavascriptURLinNavigation(
script_source, window_.Get());
if (script_source.IsEmpty())
return;
}
bool had_navigation_before =
window_->GetFrame()->Loader().HasProvisionalNavigation();
// https://html.spec.whatwg.org/multipage/browsing-the-web.html#javascript-protocol
// Step 6. "Let baseURL be settings's API base URL." [spec text]
const KURL base_url = window_->BaseURL();
// Step 7. "Let script be the result of creating a classic script given
// scriptSource, settings, baseURL, and the default classic script fetch
// options." [spec text]
//
// We pass |SanitizeScriptErrors::kDoNotSanitize| because |muted errors| is
// false by default.
ClassicScript* script = MakeGarbageCollected<ClassicScript>(
ScriptSourceCode(script_source, ScriptSourceLocationType::kJavascriptUrl),
base_url, ScriptFetchOptions(), SanitizeScriptErrors::kDoNotSanitize);
DCHECK_EQ(&window_->GetScriptController(), this);
v8::HandleScope handle_scope(GetIsolate());
v8::Local<v8::Value> v8_result = script->RunScriptAndReturnValue(window_);
UseCounter::Count(window_.Get(), WebFeature::kExecutedJavaScriptURL);
// If executing script caused this frame to be removed from the page, we
// don't want to try to replace its document!
if (!window_->GetFrame())
return;
// If a navigation begins during the javascript: url's execution, ignore
// the return value of the script. Otherwise, replacing the document with a
// string result would cancel the navigation.
// TODO(crbug.com/1085514): Consider making HasProvisionalNavigation return
// true when a form submission is pending instead of having a separate check
// for form submissions here.
if (!had_navigation_before &&
(window_->GetFrame()->Loader().HasProvisionalNavigation() ||
window_->GetFrame()->IsFormSubmissionPending())) {
return;
}
if (v8_result.IsEmpty() || !v8_result->IsString())
return;
UseCounter::Count(window_.Get(),
WebFeature::kReplaceDocumentViaJavaScriptURL);
auto* previous_document_loader =
window_->GetFrame()->Loader().GetDocumentLoader();
DCHECK(previous_document_loader);
auto params =
previous_document_loader->CreateWebNavigationParamsToCloneDocument();
String result = ToCoreString(v8::Local<v8::String>::Cast(v8_result));
WebNavigationParams::FillStaticResponse(params.get(), "text/html", "UTF-8",
StringUTF8Adaptor(result));
window_->GetFrame()->Loader().CommitNavigation(std::move(params), nullptr,
CommitReason::kJavascriptUrl);
}
v8::Local<v8::Value> ScriptController::EvaluateMethodInMainWorld(
v8::Local<v8::Function> function,
v8::Local<v8::Value> receiver,
int argc,
v8::Local<v8::Value> argv[]) {
if (!CanExecuteScript(
ExecuteScriptPolicy::kDoNotExecuteScriptWhenScriptsDisabled)) {
return v8::Local<v8::Value>();
}
// |script_state->GetContext()| should be initialized already due to the
// WindowProxy() call inside ToScriptStateForMainWorld().
ScriptState* script_state = ToScriptStateForMainWorld(window_->GetFrame());
if (!script_state) {
return v8::Local<v8::Value>();
}
DCHECK_EQ(script_state->GetIsolate(), GetIsolate());
v8::Context::Scope scope(script_state->GetContext());
v8::EscapableHandleScope handle_scope(GetIsolate());
v8::TryCatch try_catch(GetIsolate());
try_catch.SetVerbose(true);
ExecutionContext* executionContext = ExecutionContext::From(script_state);
v8::MaybeLocal<v8::Value> resultObj = V8ScriptRunner::CallFunction(
function, executionContext, receiver, argc,
static_cast<v8::Local<v8::Value>*>(argv), ToIsolate(window_->GetFrame()));
if (resultObj.IsEmpty())
return v8::Local<v8::Value>();
return handle_scope.Escape(resultObj.ToLocalChecked());
}
bool ScriptController::CanExecuteScript(ExecuteScriptPolicy policy) {
if (policy == ExecuteScriptPolicy::kDoNotExecuteScriptWhenScriptsDisabled &&
!window_->CanExecuteScripts(kAboutToExecuteScript))
return false;
if (window_->document()->IsInitialEmptyDocument())
window_->GetFrame()->Loader().DidAccessInitialDocument();
return true;
}
scoped_refptr<DOMWrapperWorld>
ScriptController::CreateNewInspectorIsolatedWorld(const String& world_name) {
scoped_refptr<DOMWrapperWorld> world = DOMWrapperWorld::Create(
GetIsolate(), DOMWrapperWorld::WorldType::kInspectorIsolated);
// Bail out if we could not create an isolated world.
if (!world)
return nullptr;
if (!world_name.IsEmpty()) {
DOMWrapperWorld::SetNonMainWorldHumanReadableName(world->GetWorldId(),
world_name);
}
// Make sure the execution context exists.
WindowProxy(*world);
return world;
}
} // namespace blink