blob: de79b9b0ba9a6b8a91fa5f682e3af798de08be2f [file] [log] [blame]
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "third_party/blink/renderer/bindings/core/v8/js_event_handler_for_content_attribute.h"
#include "third_party/blink/renderer/bindings/core/v8/source_location.h"
#include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.h"
#include "third_party/blink/renderer/core/dom/events/event_target.h"
#include "third_party/blink/renderer/core/events/error_event.h"
#include "third_party/blink/renderer/core/frame/local_dom_window.h"
#include "third_party/blink/renderer/core/html/forms/html_form_element.h"
namespace blink {
v8::Local<v8::Value> JSEventHandlerForContentAttribute::GetListenerObject(
EventTarget& event_target) {
// Step 3. of get the current value of the event handler should be executed
// only if EventHandler's value is an internal raw uncompiled handler and it
// has never tried to get compiled.
if (HasCompiledHandler())
return JSEventHandler::GetListenerObject(event_target);
if (did_compile_)
return v8::Null(GetIsolate());
return GetCompiledHandler(event_target);
}
// Implements Step 3. of "get the current value of the event handler"
// https://html.spec.whatwg.org/multipage/webappapis.html#getting-the-current-value-of-the-event-handler
v8::Local<v8::Value> JSEventHandlerForContentAttribute::GetCompiledHandler(
EventTarget& event_target) {
// Do not compile the same code twice.
DCHECK(!did_compile_);
did_compile_ = true;
ExecutionContext* execution_context_of_event_target =
event_target.GetExecutionContext();
if (!execution_context_of_event_target)
return v8::Null(GetIsolate());
v8::Local<v8::Context> v8_context_of_event_target =
ToV8Context(execution_context_of_event_target, GetWorld());
if (v8_context_of_event_target.IsEmpty())
return v8::Null(GetIsolate());
ScriptState* script_state_of_event_target =
ScriptState::From(v8_context_of_event_target);
if (!script_state_of_event_target->ContextIsValid())
return v8::Null(GetIsolate());
// Step 1. If eventTarget is an element, then let element be eventTarget, and
// document be element's node document. Otherwise, eventTarget is a Window
// object, let element be null, and document be eventTarget's associated
// Document.
Element* element = nullptr;
Document* document = nullptr;
Node* node = event_target.ToNode();
const DOMWindow* window = event_target.ToDOMWindow();
if (node && node->IsElementNode()) {
element = ToElement(node);
document = &node->GetDocument();
} else if (node && node->IsDocumentNode()) {
// Attributes for |blink::HTMLBodyElement| is treated as ones for
// |blink::Document| unlike the definition in standards.
document = &node->GetDocument();
} else {
// TODO(crbug.com/891635): Add these checks here:
// DCHECK(window);
// DCHECK(event_target.ToLocalDOMWindow());
// DCHECK_EQ(event_target.ToLocalDOMWindow()->document(), document);
document = To<Document>(execution_context_of_event_target);
}
DCHECK(document);
// Step 6. Let settings object be the relevant settings object of document.
// Step 9. Push settings object's realm execution context onto the JavaScript
// execution context stack; it is now the running JavaScript execution
// context.
//
// |document->AllowInlineEventHandler()| checks the world of current context,
// so this scope needs to be defined before calling it.
v8::Context::Scope event_target_context_scope(v8_context_of_event_target);
// Step 2. If scripting is disabled for document, then return null.
if (!document->AllowInlineEventHandler(node, this, source_url_,
position_.line_))
return v8::Null(GetIsolate());
// Step 5. If element is not null and element has a form owner, let form owner
// be that form owner. Otherwise, let form owner be null.
HTMLFormElement* form_owner = nullptr;
if (element && element->IsHTMLElement()) {
form_owner = ToHTMLElement(element)->formOwner();
}
// Step 10. Let function be the result of calling FunctionCreate, with
// arguments:
// kind
// Normal
// ParameterList
// If eventHandler is an onerror event handler of a Window object
// Let the function have five arguments, named event, source, lineno,
// colno, and error.
// Otherwise
// Let the function have a single argument called event.
// Body
// The result of parsing body above.
// Scope
// 1. If eventHandler is an element's event handler, then let Scope be
// NewObjectEnvironment(document, the global environment). Otherwise,
// eventHandler is a Window object's event handler: let Scope be the
// global environment.
// 2. If form owner is not null, let Scope be NewObjectEnvironment(form
// owner, Scope).
// 3. If element is not null, let Scope be NewObjectEnvironment(element,
// Scope).
// Strict
// The value of strict.
//
// Note: Strict is set by V8.
v8::Isolate* isolate = script_state_of_event_target->GetIsolate();
v8::Local<v8::String> parameter_list[5];
size_t parameter_list_size = 0;
if (IsOnErrorEventHandler() && window) {
// SVG requires to introduce evt as an alias to event in event handlers.
// See ANNOTATION 3: https://www.w3.org/TR/SVG/interact.html#SVGEvents
parameter_list[parameter_list_size++] =
V8String(isolate, node && node->IsSVGElement() ? "evt" : "event");
parameter_list[parameter_list_size++] = V8String(isolate, "source");
parameter_list[parameter_list_size++] = V8String(isolate, "lineno");
parameter_list[parameter_list_size++] = V8String(isolate, "colno");
parameter_list[parameter_list_size++] = V8String(isolate, "error");
} else {
// SVG requires to introduce evt as an alias to event in event handlers.
// See ANNOTATION 3: https://www.w3.org/TR/SVG/interact.html#SVGEvents
parameter_list[parameter_list_size++] =
V8String(isolate, node && node->IsSVGElement() ? "evt" : "event");
}
DCHECK_LE(parameter_list_size, base::size(parameter_list));
v8::Local<v8::Object> scopes[3];
size_t scopes_size = 0;
if (element) {
scopes[scopes_size++] =
ToV8(document, script_state_of_event_target).As<v8::Object>();
}
if (form_owner) {
scopes[scopes_size++] =
ToV8(form_owner, script_state_of_event_target).As<v8::Object>();
}
if (element) {
scopes[scopes_size++] =
ToV8(element, script_state_of_event_target).As<v8::Object>();
}
DCHECK_LE(scopes_size, base::size(scopes));
v8::ScriptOrigin origin(
V8String(isolate, source_url_),
v8::Integer::New(isolate, position_.line_.ZeroBasedInt()),
v8::Integer::New(isolate, position_.column_.ZeroBasedInt()),
// TODO(yukiy): consider which value should be passed here.
v8::True(isolate));
v8::ScriptCompiler::Source source(V8String(isolate, code_), origin);
v8::Local<v8::Function> compiled_function;
{
v8::TryCatch block(isolate);
block.SetVerbose(true);
v8::MaybeLocal<v8::Function> maybe_result =
v8::ScriptCompiler::CompileFunctionInContext(
v8_context_of_event_target, &source, parameter_list_size,
parameter_list, scopes_size, scopes);
// Step 7. If body is not parsable as FunctionBody or if parsing detects an
// early error, then follow these substeps:
// 1. Set eventHandler's value to null.
// 2. Report the error for the appropriate script and with the appropriate
// position (line number and column number) given by location, using
// settings object's global object. If the error is still not handled
// after this, then the error may be reported to a developer console.
// 3. Return null.
if (!maybe_result.ToLocal(&compiled_function))
return v8::Null(isolate);
}
// Step 12. Set eventHandler's value to the result of creating a Web IDL
// EventHandler callback function object whose object reference is function
// and whose callback context is settings object.
compiled_function->SetName(V8String(isolate, function_name_));
SetCompiledHandler(
script_state_of_event_target, compiled_function,
V8PrivateProperty::GetCustomWrappableEventHandler(GetIsolate()));
return JSEventHandler::GetListenerObject(event_target);
}
std::unique_ptr<SourceLocation>
JSEventHandlerForContentAttribute::GetSourceLocation(EventTarget& target) {
v8::HandleScope(GetIsolate());
auto source_location = JSEventHandler::GetSourceLocation(target);
if (source_location)
return source_location;
// Fallback to uncompiled source info.
return SourceLocation::Create(source_url_, position_.line_.ZeroBasedInt(),
position_.column_.ZeroBasedInt(), nullptr);
}
} // namespace blink