Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(160)

Side by Side Diff: third_party/WebKit/Source/bindings/core/v8/PrivateScriptRunner.cpp

Issue 2571063002: Remove Blink-in-JS (Closed)
Patch Set: Created 4 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
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 "bindings/core/v8/PrivateScriptRunner.h"
6
7 #include "bindings/core/v8/DOMWrapperWorld.h"
8 #include "bindings/core/v8/ExceptionState.h"
9 #include "bindings/core/v8/V8Binding.h"
10 #include "bindings/core/v8/V8PerContextData.h"
11 #include "bindings/core/v8/V8PrivateProperty.h"
12 #include "bindings/core/v8/V8ScriptRunner.h"
13 #include "core/PrivateScriptSources.h"
14 #ifndef NDEBUG
15 #include "core/PrivateScriptSourcesForTesting.h"
16 #endif
17 #include "core/dom/Document.h"
18 #include "core/dom/ExceptionCode.h"
19 #include "platform/PlatformResourceLoader.h"
20
21 namespace blink {
22
23 static void dumpV8Message(v8::Local<v8::Context> context,
24 v8::Local<v8::Message> message) {
25 if (message.IsEmpty())
26 return;
27
28 // FIXME: GetScriptOrigin() and GetLineNumber() return empty handles
29 // when they are called at the first time if V8 has a pending exception.
30 // So we need to call twice to get a correct ScriptOrigin and line number.
31 // This is a bug of V8.
32 message->GetScriptOrigin();
33 v8::Maybe<int> unused = message->GetLineNumber(context);
34 ALLOW_UNUSED_LOCAL(unused);
35
36 v8::Local<v8::Value> resourceName = message->GetScriptOrigin().ResourceName();
37 String fileName = "Unknown JavaScript file";
38 if (!resourceName.IsEmpty() && resourceName->IsString())
39 fileName = toCoreString(v8::Local<v8::String>::Cast(resourceName));
40 int lineNumber = 0;
41 v8Call(message->GetLineNumber(context), lineNumber);
42 v8::Local<v8::String> errorMessage = message->Get();
43 fprintf(stderr, "%s (line %d): %s\n", fileName.utf8().data(), lineNumber,
44 toCoreString(errorMessage).utf8().data());
45 }
46
47 static void importFunction(const v8::FunctionCallbackInfo<v8::Value>& args);
48
49 static v8::Local<v8::Value> compileAndRunPrivateScript(ScriptState* scriptState,
50 String scriptClassName,
51 const char* source,
52 size_t size) {
53 v8::Isolate* isolate = scriptState->isolate();
54 v8::TryCatch block(isolate);
55 String sourceString(source, size);
56 String fileName = scriptClassName + ".js";
57
58 v8::Local<v8::Context> context = scriptState->context();
59 v8::Local<v8::Object> global = context->Global();
60 v8::Local<v8::String> key = v8String(isolate, "privateScriptController");
61
62 if (global->HasOwnProperty(context, key).ToChecked()) {
63 v8::Local<v8::Value> privateScriptController =
64 global->Get(context, key).ToLocalChecked();
65 CHECK(privateScriptController->IsObject());
66 v8::Local<v8::Object> privateScriptControllerObject =
67 privateScriptController.As<v8::Object>();
68 v8::Local<v8::Value> importFunctionValue =
69 privateScriptControllerObject->Get(context, v8String(isolate, "import"))
70 .ToLocalChecked();
71 if (importFunctionValue->IsUndefined()) {
72 v8::Local<v8::Function> function;
73 // This is a memory leak, FunctionTemplates are eternal.
74 if (!v8::FunctionTemplate::New(isolate, importFunction)
75 ->GetFunction(context)
76 .ToLocal(&function) ||
77 !v8CallBoolean(privateScriptControllerObject->Set(
78 context, v8String(isolate, "import"), function))) {
79 dumpV8Message(context, block.Message());
80 LOG(FATAL)
81 << "Private script error: Setting import function failed. (Class "
82 "name = "
83 << scriptClassName.utf8().data() << ")";
84 }
85 }
86 }
87
88 v8::Local<v8::Script> script;
89 if (!v8Call(V8ScriptRunner::compileScript(
90 v8String(isolate, sourceString), fileName, String(),
91 TextPosition::minimumPosition(), isolate, nullptr, nullptr,
92 nullptr, NotSharableCrossOrigin),
93 script, block)) {
94 dumpV8Message(context, block.Message());
95 LOG(FATAL) << "Private script error: Compile failed. (Class name = "
96 << scriptClassName.utf8().data() << ")";
97 }
98
99 v8::Local<v8::Value> result;
100 if (!v8Call(V8ScriptRunner::runCompiledInternalScript(isolate, script),
101 result, block)) {
102 dumpV8Message(context, block.Message());
103 LOG(FATAL) << "Private script error: installClass() failed. (Class name = "
104 << scriptClassName.utf8().data() << ")";
105 }
106 return result;
107 }
108
109 // Private scripts can use privateScriptController.import(bundledResource,
110 // compileAndRunScript) to import dependent resources.
111 // |bundledResource| is a string resource name.
112 // |compileAndRunScript| optional boolean representing if the javascript should
113 // be executed. Default: true.
114 void importFunction(const v8::FunctionCallbackInfo<v8::Value>& args) {
115 v8::Isolate* isolate = args.GetIsolate();
116 RELEASE_ASSERT(isolate && (args.Length() >= 1));
117 String resourceFileName = toCoreString(
118 args[0]->ToString(isolate->GetCurrentContext()).ToLocalChecked());
119 String resourceData =
120 loadResourceAsASCIIString(resourceFileName.utf8().data());
121 RELEASE_ASSERT(resourceData.length());
122 bool compileAndRunScript = true;
123 if (args.Length() == 2) {
124 RELEASE_ASSERT(args[1]->IsBoolean());
125 compileAndRunScript = args[1].As<v8::Boolean>()->Value();
126 }
127
128 if (resourceFileName.endsWith(".js") && compileAndRunScript)
129 compileAndRunPrivateScript(
130 ScriptState::current(isolate), resourceFileName.replace(".js", ""),
131 resourceData.utf8().data(), resourceData.length());
132 args.GetReturnValue().Set(v8String(isolate, resourceData));
133 }
134
135 // FIXME: If we have X.js, XPartial-1.js and XPartial-2.js, currently all of the
136 // JS files are compiled when any of the JS files is requested. Ideally we
137 // should avoid compiling unrelated JS files. For example, if a method in
138 // XPartial-1.js is requested, we just need to compile X.js and XPartial-1.js,
139 // and don't need to compile XPartial-2.js.
140 static void installPrivateScript(v8::Isolate* isolate, String className) {
141 ScriptState* scriptState = ScriptState::current(isolate);
142 int compiledScriptCount = 0;
143 // |kPrivateScriptSourcesForTesting| is defined in V8PrivateScriptSources.h,
144 // which is auto-generated by make_private_script_source.py.
145 #ifndef NDEBUG
146 for (size_t index = 0;
147 index < WTF_ARRAY_LENGTH(kPrivateScriptSourcesForTesting); index++) {
148 if (className == kPrivateScriptSourcesForTesting[index].className) {
149 compileAndRunPrivateScript(
150 scriptState, kPrivateScriptSourcesForTesting[index].scriptClassName,
151 kPrivateScriptSourcesForTesting[index].source,
152 kPrivateScriptSourcesForTesting[index].size);
153 compiledScriptCount++;
154 }
155 }
156 #endif
157
158 // |kPrivateScriptSources| is defined in V8PrivateScriptSources.h, which is
159 // auto-generated by make_private_script_source.py.
160 for (size_t index = 0; index < WTF_ARRAY_LENGTH(kPrivateScriptSources);
161 index++) {
162 if (className == kPrivateScriptSources[index].className) {
163 String resourceData =
164 loadResourceAsASCIIString(kPrivateScriptSources[index].resourceFile);
165 compileAndRunPrivateScript(
166 scriptState, kPrivateScriptSources[index].scriptClassName,
167 resourceData.utf8().data(), resourceData.length());
168 compiledScriptCount++;
169 }
170 }
171
172 if (!compiledScriptCount) {
173 LOG(FATAL)
174 << "Private script error: Target source code was not found. (Class "
175 "name = "
176 << className.utf8().data() << ")";
177 }
178 }
179
180 static v8::Local<v8::Value> installPrivateScriptRunner(v8::Isolate* isolate) {
181 const String className = "PrivateScriptRunner";
182 size_t index;
183 // |kPrivateScriptSources| is defined in V8PrivateScriptSources.h, which is
184 // auto-generated by make_private_script_source.py.
185 for (index = 0; index < WTF_ARRAY_LENGTH(kPrivateScriptSources); index++) {
186 if (className == kPrivateScriptSources[index].className)
187 break;
188 }
189 if (index == WTF_ARRAY_LENGTH(kPrivateScriptSources)) {
190 LOG(FATAL)
191 << "Private script error: Target source code was not found. (Class "
192 "name = "
193 << className.utf8().data() << ")";
194 }
195 String resourceData =
196 loadResourceAsASCIIString(kPrivateScriptSources[index].resourceFile);
197 return compileAndRunPrivateScript(ScriptState::current(isolate), className,
198 resourceData.utf8().data(),
199 resourceData.length());
200 }
201
202 static v8::Local<v8::Object> classObjectOfPrivateScript(
203 ScriptState* scriptState,
204 String className) {
205 ASSERT(scriptState->perContextData());
206 ASSERT(scriptState->getExecutionContext());
207 v8::Isolate* isolate = scriptState->isolate();
208 v8::Local<v8::Value> compiledClass =
209 scriptState->perContextData()->compiledPrivateScript(className);
210 if (compiledClass.IsEmpty()) {
211 v8::Local<v8::Value> installedClasses =
212 scriptState->perContextData()->compiledPrivateScript(
213 "PrivateScriptRunner");
214 if (installedClasses.IsEmpty()) {
215 installedClasses = installPrivateScriptRunner(isolate);
216 scriptState->perContextData()->setCompiledPrivateScript(
217 "PrivateScriptRunner", installedClasses);
218 }
219 RELEASE_ASSERT(!installedClasses.IsEmpty());
220 RELEASE_ASSERT(installedClasses->IsObject());
221
222 installPrivateScript(isolate, className);
223 compiledClass =
224 v8::Local<v8::Object>::Cast(installedClasses)
225 ->Get(scriptState->context(), v8String(isolate, className))
226 .ToLocalChecked();
227 RELEASE_ASSERT(compiledClass->IsObject());
228 scriptState->perContextData()->setCompiledPrivateScript(className,
229 compiledClass);
230 }
231 return v8::Local<v8::Object>::Cast(compiledClass);
232 }
233
234 static void initializeHolderIfNeeded(ScriptState* scriptState,
235 v8::Local<v8::Object> classObject,
236 v8::Local<v8::Value> holder) {
237 RELEASE_ASSERT(!holder.IsEmpty());
238 RELEASE_ASSERT(holder->IsObject());
239 v8::Local<v8::Object> holderObject = v8::Local<v8::Object>::Cast(holder);
240 v8::Isolate* isolate = scriptState->isolate();
241 v8::Local<v8::Context> context = scriptState->context();
242 auto privateIsInitialized =
243 V8PrivateProperty::getPrivateScriptRunnerIsInitialized(isolate);
244 if (privateIsInitialized.hasValue(context, holderObject))
245 return; // Already initialized.
246
247 v8::TryCatch block(isolate);
248 v8::Local<v8::Value> initializeFunction;
249 if (classObject->Get(scriptState->context(), v8String(isolate, "initialize"))
250 .ToLocal(&initializeFunction) &&
251 initializeFunction->IsFunction()) {
252 v8::TryCatch block(isolate);
253 v8::Local<v8::Value> result;
254 if (!V8ScriptRunner::callInternalFunction(
255 v8::Local<v8::Function>::Cast(initializeFunction), holder, 0, 0,
256 isolate)
257 .ToLocal(&result)) {
258 dumpV8Message(context, block.Message());
259 LOG(FATAL)
260 << "Private script error: Object constructor threw an exception.";
261 }
262 }
263
264 // Inject the prototype object of the private script into the prototype chain
265 // of the holder object. This is necessary to let the holder object use
266 // properties defined on the prototype object of the private script. (e.g., if
267 // the prototype object has |foo|, the holder object should be able to use it
268 // with |this.foo|.)
269 if (classObject->GetPrototype() != holderObject->GetPrototype()) {
270 if (!v8CallBoolean(
271 classObject->SetPrototype(context, holderObject->GetPrototype()))) {
272 dumpV8Message(context, block.Message());
273 LOG(FATAL) << "Private script error: SetPrototype failed.";
274 }
275 }
276 if (!v8CallBoolean(holderObject->SetPrototype(context, classObject))) {
277 dumpV8Message(context, block.Message());
278 LOG(FATAL) << "Private script error: SetPrototype failed.";
279 }
280
281 privateIsInitialized.set(context, holderObject, v8Boolean(true, isolate));
282 }
283
284 v8::Local<v8::Value> PrivateScriptRunner::installClassIfNeeded(
285 Document* document,
286 String className) {
287 if (!document->contextDocument()->frame())
288 return v8::Local<v8::Value>();
289
290 v8::HandleScope handleScope(toIsolate(document));
291 ScriptState* scriptState =
292 ScriptState::forWorld(document->contextDocument()->frame(),
293 DOMWrapperWorld::privateScriptIsolatedWorld());
294 if (!scriptState)
295 return v8::Local<v8::Value>();
296
297 ScriptState::Scope scope(scriptState);
298 return classObjectOfPrivateScript(scriptState, className);
299 }
300
301 namespace {
302
303 void rethrowExceptionInPrivateScript(v8::Isolate* isolate,
304 v8::TryCatch& block,
305 ScriptState* scriptStateInUserScript,
306 ExceptionState::ContextType errorContext,
307 const char* propertyName,
308 const char* interfaceName) {
309 v8::Local<v8::Context> context = scriptStateInUserScript->context();
310 v8::Local<v8::Value> exception = block.Exception();
311 RELEASE_ASSERT(!exception.IsEmpty() && exception->IsObject());
312
313 v8::Local<v8::Object> exceptionObject =
314 v8::Local<v8::Object>::Cast(exception);
315 v8::Local<v8::Value> name =
316 exceptionObject->Get(context, v8String(isolate, "name")).ToLocalChecked();
317 RELEASE_ASSERT(name->IsString());
318
319 v8::Local<v8::Message> tryCatchMessage = block.Message();
320 v8::Local<v8::Value> message;
321 String messageString;
322 if (exceptionObject->Get(context, v8String(isolate, "message"))
323 .ToLocal(&message) &&
324 message->IsString())
325 messageString = toCoreString(v8::Local<v8::String>::Cast(message));
326
327 String exceptionName = toCoreString(v8::Local<v8::String>::Cast(name));
328 if (exceptionName == "PrivateScriptException") {
329 v8::Local<v8::Value> code =
330 exceptionObject->Get(context, v8String(isolate, "code"))
331 .ToLocalChecked();
332 RELEASE_ASSERT(code->IsInt32());
333 int exceptionCode = code.As<v8::Int32>()->Value();
334 ScriptState::Scope scope(scriptStateInUserScript);
335 ExceptionState exceptionState(scriptStateInUserScript->isolate(),
336 errorContext, interfaceName, propertyName);
337 exceptionState.throwDOMException(exceptionCode, messageString);
338 return;
339 }
340
341 // Standard JS errors thrown by a private script are treated as real errors
342 // of the private script and crash the renderer, except for a stack overflow
343 // error. A stack overflow error can happen in a valid private script
344 // if user's script can create a recursion that involves the private script.
345 if (exceptionName == "RangeError" &&
346 messageString.contains("Maximum call stack size exceeded")) {
347 ScriptState::Scope scope(scriptStateInUserScript);
348 ExceptionState exceptionState(scriptStateInUserScript->isolate(),
349 errorContext, interfaceName, propertyName);
350 exceptionState.throwDOMException(V8RangeError, messageString);
351 return;
352 }
353
354 dumpV8Message(context, tryCatchMessage);
355 LOG(FATAL) << "Private script error: " << exceptionName.utf8().data()
356 << " was thrown.";
357 }
358
359 } // namespace
360
361 v8::Local<v8::Value> PrivateScriptRunner::runDOMAttributeGetter(
362 ScriptState* scriptState,
363 ScriptState* scriptStateInUserScript,
364 const char* className,
365 const char* attributeName,
366 v8::Local<v8::Value> holder) {
367 v8::Isolate* isolate = scriptState->isolate();
368 v8::Local<v8::Object> classObject =
369 classObjectOfPrivateScript(scriptState, className);
370 v8::Local<v8::Value> descriptor;
371 if (!classObject
372 ->GetOwnPropertyDescriptor(scriptState->context(),
373 v8String(isolate, attributeName))
374 .ToLocal(&descriptor) ||
375 !descriptor->IsObject()) {
376 LOG(FATAL)
377 << "Private script error: Target DOM attribute getter was not found. "
378 "(Class name = "
379 << className << ", Attribute name = " << attributeName << ")";
380 }
381 v8::Local<v8::Value> getter;
382 if (!v8::Local<v8::Object>::Cast(descriptor)
383 ->Get(scriptState->context(), v8String(isolate, "get"))
384 .ToLocal(&getter) ||
385 !getter->IsFunction()) {
386 LOG(FATAL)
387 << "Private script error: Target DOM attribute getter was not found. "
388 "(Class name = "
389 << className << ", Attribute name = " << attributeName << ")";
390 }
391 initializeHolderIfNeeded(scriptState, classObject, holder);
392 v8::TryCatch block(isolate);
393 v8::Local<v8::Value> result;
394 if (!V8ScriptRunner::callInternalFunction(
395 v8::Local<v8::Function>::Cast(getter), holder, 0, 0, isolate)
396 .ToLocal(&result)) {
397 rethrowExceptionInPrivateScript(isolate, block, scriptStateInUserScript,
398 ExceptionState::GetterContext,
399 attributeName, className);
400 block.ReThrow();
401 return v8::Local<v8::Value>();
402 }
403 return result;
404 }
405
406 bool PrivateScriptRunner::runDOMAttributeSetter(
407 ScriptState* scriptState,
408 ScriptState* scriptStateInUserScript,
409 const char* className,
410 const char* attributeName,
411 v8::Local<v8::Value> holder,
412 v8::Local<v8::Value> v8Value) {
413 v8::Isolate* isolate = scriptState->isolate();
414 v8::Local<v8::Object> classObject =
415 classObjectOfPrivateScript(scriptState, className);
416 v8::Local<v8::Value> descriptor;
417 if (!classObject
418 ->GetOwnPropertyDescriptor(scriptState->context(),
419 v8String(isolate, attributeName))
420 .ToLocal(&descriptor) ||
421 !descriptor->IsObject()) {
422 LOG(FATAL)
423 << "Private script error: Target DOM attribute setter was not found. "
424 "(Class name = "
425 << className << ", Attribute name = " << attributeName << ")";
426 }
427 v8::Local<v8::Value> setter;
428 if (!v8::Local<v8::Object>::Cast(descriptor)
429 ->Get(scriptState->context(), v8String(isolate, "set"))
430 .ToLocal(&setter) ||
431 !setter->IsFunction()) {
432 LOG(FATAL) << "Private script error: Target DOM attribute setter was not "
433 "found. (Class name = "
434 << className << ", Attribute name = " << attributeName << ")";
435 }
436 initializeHolderIfNeeded(scriptState, classObject, holder);
437 v8::Local<v8::Value> argv[] = {v8Value};
438 v8::TryCatch block(isolate);
439 v8::Local<v8::Value> result;
440 if (!V8ScriptRunner::callInternalFunction(
441 v8::Local<v8::Function>::Cast(setter), holder,
442 WTF_ARRAY_LENGTH(argv), argv, isolate)
443 .ToLocal(&result)) {
444 rethrowExceptionInPrivateScript(isolate, block, scriptStateInUserScript,
445 ExceptionState::SetterContext,
446 attributeName, className);
447 block.ReThrow();
448 return false;
449 }
450 return true;
451 }
452
453 v8::Local<v8::Value> PrivateScriptRunner::runDOMMethod(
454 ScriptState* scriptState,
455 ScriptState* scriptStateInUserScript,
456 const char* className,
457 const char* methodName,
458 v8::Local<v8::Value> holder,
459 int argc,
460 v8::Local<v8::Value> argv[]) {
461 v8::Local<v8::Object> classObject =
462 classObjectOfPrivateScript(scriptState, className);
463 v8::Local<v8::Value> method;
464 if (!classObject
465 ->Get(scriptState->context(),
466 v8String(scriptState->isolate(), methodName))
467 .ToLocal(&method) ||
468 !method->IsFunction()) {
469 LOG(FATAL)
470 << "Private script error: Target DOM method was not found. (Class "
471 "name = "
472 << className << ", Method name = " << methodName << ")";
473 }
474 initializeHolderIfNeeded(scriptState, classObject, holder);
475 v8::TryCatch block(scriptState->isolate());
476 v8::Local<v8::Value> result;
477 if (!V8ScriptRunner::callInternalFunction(
478 v8::Local<v8::Function>::Cast(method), holder, argc, argv,
479 scriptState->isolate())
480 .ToLocal(&result)) {
481 rethrowExceptionInPrivateScript(
482 scriptState->isolate(), block, scriptStateInUserScript,
483 ExceptionState::ExecutionContext, methodName, className);
484 block.ReThrow();
485 return v8::Local<v8::Value>();
486 }
487 return result;
488 }
489
490 } // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698