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