Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / bindings / core / v8 / PrivateScriptRunner.cpp
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 "config.h"
6 #include "bindings/core/v8/PrivateScriptRunner.h"
7
8 #include "bindings/core/v8/DOMWrapperWorld.h"
9 #include "bindings/core/v8/ExceptionState.h"
10 #include "bindings/core/v8/V8Binding.h"
11 #include "bindings/core/v8/V8PerContextData.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/ExceptionCode.h"
18
19 namespace blink {
20
21 static void dumpV8Message(v8::Handle<v8::Message> message)
22 {
23     if (message.IsEmpty())
24         return;
25
26     // FIXME: GetScriptOrigin() and GetLineNumber() return empty handles
27     // when they are called at the first time if V8 has a pending exception.
28     // So we need to call twice to get a correct ScriptOrigin and line number.
29     // This is a bug of V8.
30     message->GetScriptOrigin();
31     message->GetLineNumber();
32
33     v8::Handle<v8::Value> resourceName = message->GetScriptOrigin().ResourceName();
34     String fileName = "Unknown JavaScript file";
35     if (!resourceName.IsEmpty() && resourceName->IsString())
36         fileName = toCoreString(v8::Handle<v8::String>::Cast(resourceName));
37     int lineNumber = message->GetLineNumber();
38     v8::Handle<v8::String> errorMessage = message->Get();
39     fprintf(stderr, "%s (line %d): %s\n", fileName.utf8().data(), lineNumber, toCoreString(errorMessage).utf8().data());
40 }
41
42 static void dumpJSError(String exceptionName, v8::Handle<v8::Message> message)
43 {
44     // FIXME: Set a ScriptOrigin of the private script and print a more informative message.
45 #ifndef NDEBUG
46     fprintf(stderr, "Private script throws an exception: %s\n", exceptionName.utf8().data());
47     dumpV8Message(message);
48 #endif
49 }
50
51 static v8::Handle<v8::Value> compileAndRunPrivateScript(v8::Isolate* isolate, String scriptClassName, const unsigned char* source, size_t size)
52 {
53     v8::TryCatch block;
54     String sourceString(reinterpret_cast<const char*>(source), size);
55     String fileName = scriptClassName + ".js";
56     v8::Handle<v8::Script> script = V8ScriptRunner::compileScript(v8String(isolate, sourceString), fileName, TextPosition::minimumPosition(), 0, isolate, NotSharableCrossOrigin, V8CacheOptionsOff);
57     if (block.HasCaught()) {
58         fprintf(stderr, "Private script error: Compile failed. (Class name = %s)\n", scriptClassName.utf8().data());
59         dumpV8Message(block.Message());
60         RELEASE_ASSERT_NOT_REACHED();
61     }
62
63     v8::Handle<v8::Value> result = V8ScriptRunner::runCompiledInternalScript(script, isolate);
64     if (block.HasCaught()) {
65         fprintf(stderr, "Private script error: installClass() failed. (Class name = %s)\n", scriptClassName.utf8().data());
66         dumpV8Message(block.Message());
67         RELEASE_ASSERT_NOT_REACHED();
68     }
69     return result;
70 }
71
72 // FIXME: If we have X.js, XPartial-1.js and XPartial-2.js, currently all of the JS files
73 // are compiled when any of the JS files is requested. Ideally we should avoid compiling
74 // unrelated JS files. For example, if a method in XPartial-1.js is requested, we just
75 // need to compile X.js and XPartial-1.js, and don't need to compile XPartial-2.js.
76 static void installPrivateScript(v8::Isolate* isolate, String className)
77 {
78     int compiledScriptCount = 0;
79     // |kPrivateScriptSourcesForTesting| is defined in V8PrivateScriptSources.h, which is auto-generated
80     // by make_private_script_source.py.
81 #ifndef NDEBUG
82     for (size_t index = 0; index < WTF_ARRAY_LENGTH(kPrivateScriptSourcesForTesting); index++) {
83         if (className == kPrivateScriptSourcesForTesting[index].className) {
84             compileAndRunPrivateScript(isolate, kPrivateScriptSourcesForTesting[index].scriptClassName, kPrivateScriptSourcesForTesting[index].source, kPrivateScriptSourcesForTesting[index].size);
85             compiledScriptCount++;
86         }
87     }
88 #endif
89
90     // |kPrivateScriptSources| is defined in V8PrivateScriptSources.h, which is auto-generated
91     // by make_private_script_source.py.
92     for (size_t index = 0; index < WTF_ARRAY_LENGTH(kPrivateScriptSources); index++) {
93         if (className == kPrivateScriptSources[index].className) {
94             compileAndRunPrivateScript(isolate, kPrivateScriptSources[index].scriptClassName, kPrivateScriptSources[index].source, kPrivateScriptSources[index].size);
95             compiledScriptCount++;
96         }
97     }
98
99     if (!compiledScriptCount) {
100         fprintf(stderr, "Private script error: Target source code was not found. (Class name = %s)\n", className.utf8().data());
101         RELEASE_ASSERT_NOT_REACHED();
102     }
103 }
104
105 static v8::Handle<v8::Value> installPrivateScriptRunner(v8::Isolate* isolate)
106 {
107     const String className = "PrivateScriptRunner";
108     size_t index;
109     // |kPrivateScriptSources| is defined in V8PrivateScriptSources.h, which is auto-generated
110     // by make_private_script_source.py.
111     for (index = 0; index < WTF_ARRAY_LENGTH(kPrivateScriptSources); index++) {
112         if (className == kPrivateScriptSources[index].className)
113             break;
114     }
115     if (index == WTF_ARRAY_LENGTH(kPrivateScriptSources)) {
116         fprintf(stderr, "Private script error: Target source code was not found. (Class name = %s)\n", className.utf8().data());
117         RELEASE_ASSERT_NOT_REACHED();
118     }
119     return compileAndRunPrivateScript(isolate, className, kPrivateScriptSources[index].source, kPrivateScriptSources[index].size);
120 }
121
122 static v8::Handle<v8::Object> classObjectOfPrivateScript(ScriptState* scriptState, String className)
123 {
124     ASSERT(scriptState->perContextData());
125     ASSERT(scriptState->executionContext());
126     v8::Isolate* isolate = scriptState->isolate();
127     v8::Handle<v8::Value> compiledClass = scriptState->perContextData()->compiledPrivateScript(className);
128     if (compiledClass.IsEmpty()) {
129         v8::Handle<v8::Value> installedClasses = scriptState->perContextData()->compiledPrivateScript("PrivateScriptRunner");
130         if (installedClasses.IsEmpty()) {
131             installedClasses = installPrivateScriptRunner(isolate);
132             scriptState->perContextData()->setCompiledPrivateScript("PrivateScriptRunner", installedClasses);
133         }
134         RELEASE_ASSERT(!installedClasses.IsEmpty());
135         RELEASE_ASSERT(installedClasses->IsObject());
136
137         installPrivateScript(isolate, className);
138         compiledClass = v8::Handle<v8::Object>::Cast(installedClasses)->Get(v8String(isolate, className));
139         RELEASE_ASSERT(!compiledClass.IsEmpty());
140         RELEASE_ASSERT(compiledClass->IsObject());
141         scriptState->perContextData()->setCompiledPrivateScript(className, compiledClass);
142     }
143     return v8::Handle<v8::Object>::Cast(compiledClass);
144 }
145
146 static void initializeHolderIfNeeded(ScriptState* scriptState, v8::Handle<v8::Object> classObject, v8::Handle<v8::Value> holder)
147 {
148     RELEASE_ASSERT(!holder.IsEmpty());
149     RELEASE_ASSERT(holder->IsObject());
150     v8::Handle<v8::Object> holderObject = v8::Handle<v8::Object>::Cast(holder);
151     v8::Isolate* isolate = scriptState->isolate();
152     v8::Handle<v8::Value> isInitialized = V8HiddenValue::getHiddenValue(isolate, holderObject, V8HiddenValue::privateScriptObjectIsInitialized(isolate));
153     if (isInitialized.IsEmpty()) {
154         v8::TryCatch block;
155         v8::Handle<v8::Value> initializeFunction = classObject->Get(v8String(isolate, "initialize"));
156         if (!initializeFunction.IsEmpty() && initializeFunction->IsFunction()) {
157             v8::TryCatch block;
158             V8ScriptRunner::callFunction(v8::Handle<v8::Function>::Cast(initializeFunction), scriptState->executionContext(), holder, 0, 0, isolate);
159             if (block.HasCaught()) {
160                 fprintf(stderr, "Private script error: Object constructor threw an exception.\n");
161                 dumpV8Message(block.Message());
162                 RELEASE_ASSERT_NOT_REACHED();
163             }
164         }
165
166         // Inject the prototype object of the private script into the prototype chain of the holder object.
167         // This is necessary to let the holder object use properties defined on the prototype object
168         // of the private script. (e.g., if the prototype object has |foo|, the holder object should be able
169         // to use it with |this.foo|.)
170         if (classObject->GetPrototype() != holderObject->GetPrototype())
171             classObject->SetPrototype(holderObject->GetPrototype());
172         holderObject->SetPrototype(classObject);
173
174         isInitialized = v8Boolean(true, isolate);
175         V8HiddenValue::setHiddenValue(isolate, holderObject, V8HiddenValue::privateScriptObjectIsInitialized(isolate), isInitialized);
176     }
177 }
178
179 v8::Handle<v8::Value> PrivateScriptRunner::installClassIfNeeded(LocalFrame* frame, String className)
180 {
181     if (!frame)
182         return v8::Handle<v8::Value>();
183     v8::HandleScope handleScope(toIsolate(frame));
184     v8::Handle<v8::Context> context = toV8Context(frame, DOMWrapperWorld::privateScriptIsolatedWorld());
185     if (context.IsEmpty())
186         return v8::Handle<v8::Value>();
187     ScriptState* scriptState = ScriptState::from(context);
188     if (!scriptState->executionContext())
189         return v8::Handle<v8::Value>();
190
191     ScriptState::Scope scope(scriptState);
192     return classObjectOfPrivateScript(scriptState, className);
193 }
194
195 v8::Handle<v8::Value> PrivateScriptRunner::runDOMAttributeGetter(ScriptState* scriptState, String className, String attributeName, v8::Handle<v8::Value> holder)
196 {
197     v8::Handle<v8::Object> classObject = classObjectOfPrivateScript(scriptState, className);
198     v8::Handle<v8::Value> descriptor = classObject->GetOwnPropertyDescriptor(v8String(scriptState->isolate(), attributeName));
199     if (descriptor.IsEmpty() || !descriptor->IsObject()) {
200         fprintf(stderr, "Private script error: Target DOM attribute getter was not found. (Class name = %s, Attribute name = %s)\n", className.utf8().data(), attributeName.utf8().data());
201         RELEASE_ASSERT_NOT_REACHED();
202     }
203     v8::Handle<v8::Value> getter = v8::Handle<v8::Object>::Cast(descriptor)->Get(v8String(scriptState->isolate(), "get"));
204     if (getter.IsEmpty() || !getter->IsFunction()) {
205         fprintf(stderr, "Private script error: Target DOM attribute getter was not found. (Class name = %s, Attribute name = %s)\n", className.utf8().data(), attributeName.utf8().data());
206         RELEASE_ASSERT_NOT_REACHED();
207     }
208     initializeHolderIfNeeded(scriptState, classObject, holder);
209     return V8ScriptRunner::callFunction(v8::Handle<v8::Function>::Cast(getter), scriptState->executionContext(), holder, 0, 0, scriptState->isolate());
210 }
211
212 void PrivateScriptRunner::runDOMAttributeSetter(ScriptState* scriptState, String className, String attributeName, v8::Handle<v8::Value> holder, v8::Handle<v8::Value> v8Value)
213 {
214     v8::Handle<v8::Object> classObject = classObjectOfPrivateScript(scriptState, className);
215     v8::Handle<v8::Value> descriptor = classObject->GetOwnPropertyDescriptor(v8String(scriptState->isolate(), attributeName));
216     if (descriptor.IsEmpty() || !descriptor->IsObject()) {
217         fprintf(stderr, "Private script error: Target DOM attribute setter was not found. (Class name = %s, Attribute name = %s)\n", className.utf8().data(), attributeName.utf8().data());
218         RELEASE_ASSERT_NOT_REACHED();
219     }
220     v8::Handle<v8::Value> setter = v8::Handle<v8::Object>::Cast(descriptor)->Get(v8String(scriptState->isolate(), "set"));
221     if (setter.IsEmpty() || !setter->IsFunction()) {
222         fprintf(stderr, "Private script error: Target DOM attribute setter was not found. (Class name = %s, Attribute name = %s)\n", className.utf8().data(), attributeName.utf8().data());
223         RELEASE_ASSERT_NOT_REACHED();
224     }
225     initializeHolderIfNeeded(scriptState, classObject, holder);
226     v8::Handle<v8::Value> argv[] = { v8Value };
227     V8ScriptRunner::callFunction(v8::Handle<v8::Function>::Cast(setter), scriptState->executionContext(), holder, WTF_ARRAY_LENGTH(argv), argv, scriptState->isolate());
228 }
229
230 v8::Handle<v8::Value> PrivateScriptRunner::runDOMMethod(ScriptState* scriptState, String className, String methodName, v8::Handle<v8::Value> holder, int argc, v8::Handle<v8::Value> argv[])
231 {
232     v8::Handle<v8::Object> classObject = classObjectOfPrivateScript(scriptState, className);
233     v8::Handle<v8::Value> method = classObject->Get(v8String(scriptState->isolate(), methodName));
234     if (method.IsEmpty() || !method->IsFunction()) {
235         fprintf(stderr, "Private script error: Target DOM method was not found. (Class name = %s, Method name = %s)\n", className.utf8().data(), methodName.utf8().data());
236         RELEASE_ASSERT_NOT_REACHED();
237     }
238     initializeHolderIfNeeded(scriptState, classObject, holder);
239     return V8ScriptRunner::callFunction(v8::Handle<v8::Function>::Cast(method), scriptState->executionContext(), holder, argc, argv, scriptState->isolate());
240 }
241
242 bool PrivateScriptRunner::rethrowExceptionInPrivateScript(v8::Isolate* isolate, ExceptionState& exceptionState, v8::TryCatch& block)
243 {
244     v8::Handle<v8::Value> exception = block.Exception();
245     if (exception.IsEmpty() || !exception->IsObject())
246         return false;
247
248     v8::Handle<v8::Object> exceptionObject = v8::Handle<v8::Object>::Cast(exception);
249     v8::Handle<v8::Value> name = exceptionObject->Get(v8String(isolate, "name"));
250     if (name.IsEmpty() || !name->IsString())
251         return false;
252
253     v8::Handle<v8::Message> tryCatchMessage = block.Message();
254     v8::Handle<v8::Value> message = exceptionObject->Get(v8String(isolate, "message"));
255     String messageString;
256     if (!message.IsEmpty() && message->IsString())
257         messageString = toCoreString(v8::Handle<v8::String>::Cast(message));
258
259     String exceptionName = toCoreString(v8::Handle<v8::String>::Cast(name));
260     if (exceptionName == "DOMExceptionInPrivateScript") {
261         v8::Handle<v8::Value> code = exceptionObject->Get(v8String(isolate, "code"));
262         RELEASE_ASSERT(!code.IsEmpty() && code->IsInt32());
263         exceptionState.throwDOMException(toInt32(code), messageString);
264         exceptionState.throwIfNeeded();
265         return true;
266     }
267     if (exceptionName == "Error") {
268         exceptionState.throwDOMException(V8GeneralError, messageString);
269         exceptionState.throwIfNeeded();
270         dumpJSError(exceptionName, tryCatchMessage);
271         return true;
272     }
273     if (exceptionName == "TypeError") {
274         exceptionState.throwDOMException(V8TypeError, messageString);
275         exceptionState.throwIfNeeded();
276         dumpJSError(exceptionName, tryCatchMessage);
277         return true;
278     }
279     if (exceptionName == "RangeError") {
280         exceptionState.throwDOMException(V8RangeError, messageString);
281         exceptionState.throwIfNeeded();
282         dumpJSError(exceptionName, tryCatchMessage);
283         return true;
284     }
285     if (exceptionName == "SyntaxError") {
286         exceptionState.throwDOMException(V8SyntaxError, messageString);
287         exceptionState.throwIfNeeded();
288         dumpJSError(exceptionName, tryCatchMessage);
289         return true;
290     }
291     if (exceptionName == "ReferenceError") {
292         exceptionState.throwDOMException(V8ReferenceError, messageString);
293         exceptionState.throwIfNeeded();
294         dumpJSError(exceptionName, tryCatchMessage);
295         return true;
296     }
297     return false;
298 }
299
300 } // namespace blink