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