1 // Copyright 2014 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/core/frame/pausable_script_executor.h"
6
7 #include <memory>
8 #include <utility>
9
10 #include "third_party/blink/public/platform/task_type.h"
11 #include "third_party/blink/public/platform/web_vector.h"
12 #include "third_party/blink/public/web/web_script_execution_callback.h"
13 #include "third_party/blink/renderer/bindings/core/v8/sanitize_script_errors.h"
14 #include "third_party/blink/renderer/bindings/core/v8/script_controller.h"
15 #include "third_party/blink/renderer/bindings/core/v8/script_source_code.h"
16 #include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.h"
17 #include "third_party/blink/renderer/bindings/core/v8/window_proxy.h"
18 #include "third_party/blink/renderer/core/dom/document.h"
19 #include "third_party/blink/renderer/core/frame/local_frame.h"
20 #include "third_party/blink/renderer/platform/bindings/trace_wrapper_v8_reference.h"
21 #include "third_party/blink/renderer/platform/heap/heap.h"
22 #include "third_party/blink/renderer/platform/heap/heap_allocator.h"
23 #include "third_party/blink/renderer/platform/wtf/functional.h"
24 #include "third_party/blink/renderer/platform/wtf/vector.h"
25
26 namespace blink {
27
28 namespace {
29
30 class WebScriptExecutor : public PausableScriptExecutor::Executor {
31 public:
32 WebScriptExecutor(const HeapVector<ScriptSourceCode>& sources,
33 int32_t world_id,
34 bool user_gesture);
35
36 Vector<v8::Local<v8::Value>> Execute(LocalFrame*) override;
37
Trace(Visitor * visitor)38 void Trace(Visitor* visitor) override {
39 visitor->Trace(sources_);
40 PausableScriptExecutor::Executor::Trace(visitor);
41 }
42
43 private:
44 HeapVector<ScriptSourceCode> sources_;
45 int32_t world_id_;
46 bool user_gesture_;
47 };
48
WebScriptExecutor(const HeapVector<ScriptSourceCode> & sources,int32_t world_id,bool user_gesture)49 WebScriptExecutor::WebScriptExecutor(
50 const HeapVector<ScriptSourceCode>& sources,
51 int32_t world_id,
52 bool user_gesture)
53 : sources_(sources), world_id_(world_id), user_gesture_(user_gesture) {}
54
Execute(LocalFrame * frame)55 Vector<v8::Local<v8::Value>> WebScriptExecutor::Execute(LocalFrame* frame) {
56 if (user_gesture_)
57 LocalFrame::NotifyUserActivation(frame);
58
59 Vector<v8::Local<v8::Value>> results;
60 for (const auto& source : sources_) {
61 // Note: An error event in an isolated world will never be dispatched to
62 // a foreign world.
63 v8::Local<v8::Value> script_value =
64 world_id_
65 ? frame->GetScriptController().ExecuteScriptInIsolatedWorld(
66 world_id_, source, KURL(),
67 SanitizeScriptErrors::kDoNotSanitize)
68 : frame->GetScriptController()
69 .ExecuteScriptInMainWorldAndReturnValue(
70 source, KURL(), SanitizeScriptErrors::kDoNotSanitize);
71 results.push_back(script_value);
72 }
73
74 return results;
75 }
76
77 class V8FunctionExecutor : public PausableScriptExecutor::Executor {
78 public:
79 V8FunctionExecutor(v8::Isolate*,
80 v8::Local<v8::Function>,
81 v8::Local<v8::Value> receiver,
82 int argc,
83 v8::Local<v8::Value> argv[]);
84
85 Vector<v8::Local<v8::Value>> Execute(LocalFrame*) override;
86
87 void Trace(Visitor*) override;
88
89 private:
90 TraceWrapperV8Reference<v8::Function> function_;
91 TraceWrapperV8Reference<v8::Value> receiver_;
92 HeapVector<TraceWrapperV8Reference<v8::Value>> args_;
93 };
94
V8FunctionExecutor(v8::Isolate * isolate,v8::Local<v8::Function> function,v8::Local<v8::Value> receiver,int argc,v8::Local<v8::Value> argv[])95 V8FunctionExecutor::V8FunctionExecutor(v8::Isolate* isolate,
96 v8::Local<v8::Function> function,
97 v8::Local<v8::Value> receiver,
98 int argc,
99 v8::Local<v8::Value> argv[])
100 : function_(isolate, function), receiver_(isolate, receiver) {
101 args_.ReserveCapacity(SafeCast<wtf_size_t>(argc));
102 for (int i = 0; i < argc; ++i)
103 args_.push_back(TraceWrapperV8Reference<v8::Value>(isolate, argv[i]));
104 }
105
Execute(LocalFrame * frame)106 Vector<v8::Local<v8::Value>> V8FunctionExecutor::Execute(LocalFrame* frame) {
107 v8::Isolate* isolate = v8::Isolate::GetCurrent();
108 Vector<v8::Local<v8::Value>> results;
109 v8::Local<v8::Value> single_result;
110
111 Vector<v8::Local<v8::Value>> args;
112 args.ReserveCapacity(args_.size());
113 for (wtf_size_t i = 0; i < args_.size(); ++i)
114 args.push_back(args_[i].NewLocal(isolate));
115
116 {
117 if (V8ScriptRunner::CallFunction(function_.NewLocal(isolate),
118 frame->GetDocument()->ToExecutionContext(),
119 receiver_.NewLocal(isolate), args.size(),
120 args.data(), ToIsolate(frame))
121 .ToLocal(&single_result))
122 results.push_back(single_result);
123 }
124 return results;
125 }
126
Trace(Visitor * visitor)127 void V8FunctionExecutor::Trace(Visitor* visitor) {
128 visitor->Trace(function_);
129 visitor->Trace(receiver_);
130 visitor->Trace(args_);
131 PausableScriptExecutor::Executor::Trace(visitor);
132 }
133
134 } // namespace
135
CreateAndRun(LocalFrame * frame,v8::Isolate * isolate,v8::Local<v8::Context> context,v8::Local<v8::Function> function,v8::Local<v8::Value> receiver,int argc,v8::Local<v8::Value> argv[],WebScriptExecutionCallback * callback)136 void PausableScriptExecutor::CreateAndRun(
137 LocalFrame* frame,
138 v8::Isolate* isolate,
139 v8::Local<v8::Context> context,
140 v8::Local<v8::Function> function,
141 v8::Local<v8::Value> receiver,
142 int argc,
143 v8::Local<v8::Value> argv[],
144 WebScriptExecutionCallback* callback) {
145 ScriptState* script_state = ScriptState::From(context);
146 if (!script_state->ContextIsValid()) {
147 if (callback)
148 callback->Completed(Vector<v8::Local<v8::Value>>());
149 return;
150 }
151 PausableScriptExecutor* executor =
152 MakeGarbageCollected<PausableScriptExecutor>(
153 frame, script_state, callback,
154 MakeGarbageCollected<V8FunctionExecutor>(isolate, function, receiver,
155 argc, argv));
156 executor->Run();
157 }
158
ContextDestroyed()159 void PausableScriptExecutor::ContextDestroyed() {
160 if (callback_) {
161 // Though the context is (about to be) destroyed, the callback is invoked
162 // with a vector of v8::Local<>s, which implies that creating v8::Locals
163 // is permitted. Ensure a valid scope is present for the callback.
164 // See https://crbug.com/840719.
165 ScriptState::Scope script_scope(script_state_);
166 callback_->Completed(Vector<v8::Local<v8::Value>>());
167 }
168 Dispose();
169 }
170
PausableScriptExecutor(LocalFrame * frame,scoped_refptr<DOMWrapperWorld> world,const HeapVector<ScriptSourceCode> & sources,bool user_gesture,WebScriptExecutionCallback * callback)171 PausableScriptExecutor::PausableScriptExecutor(
172 LocalFrame* frame,
173 scoped_refptr<DOMWrapperWorld> world,
174 const HeapVector<ScriptSourceCode>& sources,
175 bool user_gesture,
176 WebScriptExecutionCallback* callback)
177 : PausableScriptExecutor(
178 frame,
179 ToScriptState(frame, *world),
180 callback,
181 MakeGarbageCollected<WebScriptExecutor>(sources,
182 world->GetWorldId(),
183 user_gesture)) {}
184
PausableScriptExecutor(LocalFrame * frame,ScriptState * script_state,WebScriptExecutionCallback * callback,Executor * executor)185 PausableScriptExecutor::PausableScriptExecutor(
186 LocalFrame* frame,
187 ScriptState* script_state,
188 WebScriptExecutionCallback* callback,
189 Executor* executor)
190 : ExecutionContextLifecycleObserver(frame->GetDocument()),
191 script_state_(script_state),
192 callback_(callback),
193 blocking_option_(kNonBlocking),
194 executor_(executor) {
195 CHECK(script_state_);
196 CHECK(script_state_->ContextIsValid());
197 }
198
199 PausableScriptExecutor::~PausableScriptExecutor() = default;
200
Run()201 void PausableScriptExecutor::Run() {
202 ExecutionContext* context = GetExecutionContext();
203 DCHECK(context);
204 if (!context->IsContextPaused()) {
205 ExecuteAndDestroySelf();
206 return;
207 }
208 task_handle_ = PostCancellableTask(
209 *context->GetTaskRunner(TaskType::kJavascriptTimer), FROM_HERE,
210 WTF::Bind(&PausableScriptExecutor::ExecuteAndDestroySelf,
211 WrapPersistent(this)));
212 }
213
RunAsync(BlockingOption blocking)214 void PausableScriptExecutor::RunAsync(BlockingOption blocking) {
215 ExecutionContext* context = GetExecutionContext();
216 DCHECK(context);
217 blocking_option_ = blocking;
218 if (blocking_option_ == kOnloadBlocking)
219 Document::From(GetExecutionContext())->IncrementLoadEventDelayCount();
220
221 task_handle_ = PostCancellableTask(
222 *context->GetTaskRunner(TaskType::kJavascriptTimer), FROM_HERE,
223 WTF::Bind(&PausableScriptExecutor::ExecuteAndDestroySelf,
224 WrapPersistent(this)));
225 }
226
ExecuteAndDestroySelf()227 void PausableScriptExecutor::ExecuteAndDestroySelf() {
228 CHECK(script_state_->ContextIsValid());
229
230 if (callback_)
231 callback_->WillExecute();
232
233 ScriptState::Scope script_scope(script_state_);
234 Vector<v8::Local<v8::Value>> results =
235 executor_->Execute(Document::From(GetExecutionContext())->GetFrame());
236
237 // The script may have removed the frame, in which case contextDestroyed()
238 // will have handled the disposal/callback.
239 if (!script_state_->ContextIsValid())
240 return;
241
242 if (blocking_option_ == kOnloadBlocking)
243 Document::From(GetExecutionContext())->DecrementLoadEventDelayCount();
244
245 if (callback_)
246 callback_->Completed(results);
247
248 Dispose();
249 }
250
Dispose()251 void PausableScriptExecutor::Dispose() {
252 // Remove object as a ExecutionContextLifecycleObserver.
253 // TODO(keishi): Remove IsIteratingOverObservers() check when
254 // HeapObserverList() supports removal while iterating.
255 if (!GetExecutionContext()
256 ->ContextLifecycleObserverList()
257 .IsIteratingOverObservers()) {
258 SetExecutionContext(nullptr);
259 }
260 task_handle_.Cancel();
261 }
262
Trace(Visitor * visitor)263 void PausableScriptExecutor::Trace(Visitor* visitor) {
264 visitor->Trace(script_state_);
265 visitor->Trace(executor_);
266 ExecutionContextLifecycleObserver::Trace(visitor);
267 }
268
269 } // namespace blink
270