1 // Copyright 2018 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "third_party/blink/renderer/bindings/core/v8/js_event_handler_for_content_attribute.h"
6
7 #include "third_party/blink/renderer/bindings/core/v8/script_controller.h"
8 #include "third_party/blink/renderer/bindings/core/v8/source_location.h"
9 #include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.h"
10 #include "third_party/blink/renderer/core/dom/events/event_target.h"
11 #include "third_party/blink/renderer/core/events/error_event.h"
12 #include "third_party/blink/renderer/core/frame/local_dom_window.h"
13 #include "third_party/blink/renderer/core/html/forms/html_form_element.h"
14
15 namespace blink {
16
Create(ExecutionContext * context,const QualifiedName & name,const AtomicString & value,HandlerType type)17 JSEventHandlerForContentAttribute* JSEventHandlerForContentAttribute::Create(
18 ExecutionContext* context,
19 const QualifiedName& name,
20 const AtomicString& value,
21 HandlerType type) {
22 if (!context || !context->CanExecuteScripts(kAboutToExecuteScript))
23 return nullptr;
24 if (value.IsNull())
25 return nullptr;
26 DCHECK(IsA<LocalDOMWindow>(context));
27 return MakeGarbageCollected<JSEventHandlerForContentAttribute>(context, name,
28 value, type);
29 }
30
JSEventHandlerForContentAttribute(ExecutionContext * context,const QualifiedName & name,const AtomicString & value,HandlerType type)31 JSEventHandlerForContentAttribute::JSEventHandlerForContentAttribute(
32 ExecutionContext* context,
33 const QualifiedName& name,
34 const AtomicString& value,
35 HandlerType type)
36 : JSEventHandler(type),
37 did_compile_(false),
38 function_name_(name.LocalName()),
39 script_body_(value),
40 source_url_(context->Url().GetString()),
41 position_(To<LocalDOMWindow>(context)
42 ->GetScriptController()
43 .EventHandlerPosition()),
44 isolate_(context->GetIsolate()) {}
45
GetListenerObject(EventTarget & event_target)46 v8::Local<v8::Value> JSEventHandlerForContentAttribute::GetListenerObject(
47 EventTarget& event_target) {
48 // Step 3. of get the current value of the event handler should be executed
49 // only if EventHandler's value is an internal raw uncompiled handler and it
50 // has never tried to get compiled.
51 if (HasCompiledHandler())
52 return JSEventHandler::GetListenerObject(event_target);
53 if (did_compile_)
54 return v8::Null(GetIsolate());
55
56 return GetCompiledHandler(event_target);
57 }
58
59 // Implements Step 3. of "get the current value of the event handler"
60 // https://html.spec.whatwg.org/C/#getting-the-current-value-of-the-event-handler
GetCompiledHandler(EventTarget & event_target)61 v8::Local<v8::Value> JSEventHandlerForContentAttribute::GetCompiledHandler(
62 EventTarget& event_target) {
63 // Do not compile the same code twice.
64 DCHECK(!did_compile_);
65 did_compile_ = true;
66
67 ExecutionContext* execution_context_of_event_target =
68 event_target.GetExecutionContext();
69 if (!execution_context_of_event_target)
70 return v8::Null(GetIsolate());
71
72 v8::Local<v8::Context> v8_context_of_event_target =
73 ToV8Context(execution_context_of_event_target, GetWorld());
74 if (v8_context_of_event_target.IsEmpty())
75 return v8::Null(GetIsolate());
76
77 ScriptState* script_state_of_event_target =
78 ScriptState::From(v8_context_of_event_target);
79 if (!script_state_of_event_target->ContextIsValid())
80 return v8::Null(GetIsolate());
81
82 // Step 1. If eventTarget is an element, then let element be eventTarget, and
83 // document be element's node document. Otherwise, eventTarget is a Window
84 // object, let element be null, and document be eventTarget's associated
85 // Document.
86 Element* element = nullptr;
87 const LocalDOMWindow* window = nullptr;
88 Document* document = nullptr;
89 if (Node* node = event_target.ToNode()) {
90 if (node->IsDocumentNode()) {
91 // Some of content attributes for |HTMLBodyElement| are treated as ones
92 // for |Document| unlike the definition in HTML standard. Those content
93 // attributes are not listed in the Window-reflecting body element event
94 // handler set.
95 // https://html.spec.whatwg.org/C/#window-reflecting-body-element-event-handler-set
96 document = &node->GetDocument();
97 } else {
98 element = To<Element>(node);
99 document = &node->GetDocument();
100 }
101 // EventTarget::GetExecutionContext() sometimes returns the document which
102 // created the EventTarget, and sometimes returns the document to which
103 // the EventTarget is currently attached. The former might be different
104 // from |document|.
105 } else {
106 window = event_target.ToLocalDOMWindow();
107 DCHECK(window);
108 DCHECK_EQ(window, execution_context_of_event_target);
109 document = window->document();
110 }
111 DCHECK(document);
112
113 // Step 6. Let settings object be the relevant settings object of document.
114 // Step 9. Push settings object's realm execution context onto the JavaScript
115 // execution context stack; it is now the running JavaScript execution
116 // context.
117 //
118 // |document->AllowInlineEventHandler()| checks the world of current context,
119 // so this scope needs to be defined before calling it.
120 v8::Context::Scope event_target_context_scope(v8_context_of_event_target);
121
122 // Step 2. If scripting is disabled for document, then return null.
123 if (!document->AllowInlineEventHandler(element, this, source_url_,
124 position_.line_))
125 return v8::Null(GetIsolate());
126
127 // Step 5. If element is not null and element has a form owner, let form owner
128 // be that form owner. Otherwise, let form owner be null.
129 HTMLFormElement* form_owner = nullptr;
130 if (auto* html_element = DynamicTo<HTMLElement>(element)) {
131 form_owner = html_element->formOwner();
132
133 // https://html.spec.whatwg.org/C/#window-reflecting-body-element-event-handler-set
134 // The Event handlers on HTMLBodyElement and HTMLFrameSetElement which are
135 // listed in the Window-reflecting body element event handler set should be
136 // treated as if they are the corresponding event handlers of the window
137 // object.
138 if (html_element->IsHTMLBodyElement() ||
139 html_element->IsHTMLFrameSetElement()) {
140 window = To<LocalDOMWindow>(execution_context_of_event_target);
141 }
142 }
143
144 // Step 10. Let function be the result of calling FunctionCreate, with
145 // arguments:
146 // kind
147 // Normal
148 // ParameterList
149 // If eventHandler is an onerror event handler of a Window object
150 // Let the function have five arguments, named event, source, lineno,
151 // colno, and error.
152 // Otherwise
153 // Let the function have a single argument called event.
154 // Body
155 // The result of parsing body above.
156 // Scope
157 // 1. If eventHandler is an element's event handler, then let Scope be
158 // NewObjectEnvironment(document, the global environment). Otherwise,
159 // eventHandler is a Window object's event handler: let Scope be the
160 // global environment.
161 // 2. If form owner is not null, let Scope be NewObjectEnvironment(form
162 // owner, Scope).
163 // 3. If element is not null, let Scope be NewObjectEnvironment(element,
164 // Scope).
165 // Strict
166 // The value of strict.
167 //
168 // Note: Strict is set by V8.
169 v8::Isolate* isolate = script_state_of_event_target->GetIsolate();
170 v8::Local<v8::String> parameter_list[5];
171 size_t parameter_list_size = 0;
172 if (IsOnErrorEventHandler() && window) {
173 // SVG requires to introduce evt as an alias to event in event handlers.
174 // See ANNOTATION 3: https://www.w3.org/TR/SVG/interact.html#SVGEvents
175 parameter_list[parameter_list_size++] =
176 V8String(isolate, element && element->IsSVGElement() ? "evt" : "event");
177 parameter_list[parameter_list_size++] = V8String(isolate, "source");
178 parameter_list[parameter_list_size++] = V8String(isolate, "lineno");
179 parameter_list[parameter_list_size++] = V8String(isolate, "colno");
180 parameter_list[parameter_list_size++] = V8String(isolate, "error");
181 } else {
182 // SVG requires to introduce evt as an alias to event in event handlers.
183 // See ANNOTATION 3: https://www.w3.org/TR/SVG/interact.html#SVGEvents
184 parameter_list[parameter_list_size++] =
185 V8String(isolate, element && element->IsSVGElement() ? "evt" : "event");
186 }
187 DCHECK_LE(parameter_list_size, base::size(parameter_list));
188
189 v8::Local<v8::Object> scopes[3];
190 size_t scopes_size = 0;
191 if (element) {
192 scopes[scopes_size++] =
193 ToV8(document, script_state_of_event_target).As<v8::Object>();
194 }
195 if (form_owner) {
196 scopes[scopes_size++] =
197 ToV8(form_owner, script_state_of_event_target).As<v8::Object>();
198 }
199 if (element) {
200 scopes[scopes_size++] =
201 ToV8(element, script_state_of_event_target).As<v8::Object>();
202 }
203 DCHECK_LE(scopes_size, base::size(scopes));
204
205 v8::ScriptOrigin origin(
206 V8String(isolate, source_url_),
207 v8::Integer::New(isolate, position_.line_.ZeroBasedInt()),
208 v8::Integer::New(isolate, position_.column_.ZeroBasedInt()),
209 v8::True(isolate)); // True as |SanitizeScriptErrors::kDoNotSanitize|
210 v8::ScriptCompiler::Source source(V8String(isolate, script_body_), origin);
211
212 v8::Local<v8::Function> compiled_function;
213 {
214 v8::TryCatch block(isolate);
215 block.SetVerbose(true);
216 v8::MaybeLocal<v8::Function> maybe_result =
217 v8::ScriptCompiler::CompileFunctionInContext(
218 v8_context_of_event_target, &source, parameter_list_size,
219 parameter_list, scopes_size, scopes);
220
221 // Step 7. If body is not parsable as FunctionBody or if parsing detects an
222 // early error, then follow these substeps:
223 // 1. Set eventHandler's value to null.
224 // 2. Report the error for the appropriate script and with the appropriate
225 // position (line number and column number) given by location, using
226 // settings object's global object. If the error is still not handled
227 // after this, then the error may be reported to a developer console.
228 // 3. Return null.
229 if (!maybe_result.ToLocal(&compiled_function))
230 return v8::Null(isolate);
231 }
232
233 // Step 12. Set eventHandler's value to the result of creating a Web IDL
234 // EventHandler callback function object whose object reference is function
235 // and whose callback context is settings object.
236 compiled_function->SetName(V8String(isolate, function_name_));
237 SetCompiledHandler(script_state_of_event_target, compiled_function);
238
239 return JSEventHandler::GetListenerObject(event_target);
240 }
241
242 std::unique_ptr<SourceLocation>
GetSourceLocation(EventTarget & target)243 JSEventHandlerForContentAttribute::GetSourceLocation(EventTarget& target) {
244 auto source_location = JSEventHandler::GetSourceLocation(target);
245 if (source_location)
246 return source_location;
247 // Fallback to uncompiled source info.
248 return std::make_unique<SourceLocation>(
249 source_url_, position_.line_.ZeroBasedInt(),
250 position_.column_.ZeroBasedInt(), nullptr);
251 }
252
253 } // namespace blink
254