Upstream version 5.34.98.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / bindings / v8 / PageScriptDebugServer.cpp
1 /*
2  * Copyright (c) 2011 Google Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are
6  * met:
7  *
8  *     * Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  *     * Redistributions in binary form must reproduce the above
11  * copyright notice, this list of conditions and the following disclaimer
12  * in the documentation and/or other materials provided with the
13  * distribution.
14  *     * Neither the name of Google Inc. nor the names of its
15  * contributors may be used to endorse or promote products derived from
16  * this software without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  */
30
31 #include "config.h"
32 #include "bindings/v8/PageScriptDebugServer.h"
33
34
35 #include "V8Window.h"
36 #include "bindings/v8/ScriptController.h"
37 #include "bindings/v8/ScriptSourceCode.h"
38 #include "bindings/v8/V8Binding.h"
39 #include "bindings/v8/V8ScriptRunner.h"
40 #include "bindings/v8/V8WindowShell.h"
41 #include "core/inspector/InspectorInstrumentation.h"
42 #include "core/inspector/ScriptDebugListener.h"
43 #include "core/frame/Frame.h"
44 #include "core/frame/FrameHost.h"
45 #include "core/page/Page.h"
46 #include "wtf/OwnPtr.h"
47 #include "wtf/PassOwnPtr.h"
48 #include "wtf/StdLibExtras.h"
49 #include "wtf/TemporaryChange.h"
50 #include "wtf/text/StringBuilder.h"
51
52 namespace WebCore {
53
54 static Frame* retrieveFrameWithGlobalObjectCheck(v8::Handle<v8::Context> context)
55 {
56     if (context.IsEmpty())
57         return 0;
58
59     // Test that context has associated global dom window object.
60     v8::Handle<v8::Object> global = context->Global();
61     if (global.IsEmpty())
62         return 0;
63
64     global = global->FindInstanceInPrototypeChain(V8Window::domTemplate(context->GetIsolate(), worldTypeInMainThread(context->GetIsolate())));
65     if (global.IsEmpty())
66         return 0;
67
68     return toFrameIfNotDetached(context);
69 }
70
71 void PageScriptDebugServer::setPreprocessorSource(const String& preprocessorSource)
72 {
73     if (preprocessorSource.isEmpty())
74         m_preprocessorSourceCode.clear();
75     else
76         m_preprocessorSourceCode = adoptPtr(new ScriptSourceCode(preprocessorSource));
77     m_scriptPreprocessor.clear();
78 }
79
80 PageScriptDebugServer& PageScriptDebugServer::shared()
81 {
82     DEFINE_STATIC_LOCAL(PageScriptDebugServer, server, ());
83     return server;
84 }
85
86 PageScriptDebugServer::PageScriptDebugServer()
87     : ScriptDebugServer(v8::Isolate::GetCurrent())
88     , m_pausedPage(0)
89 {
90 }
91
92 void PageScriptDebugServer::addListener(ScriptDebugListener* listener, Page* page)
93 {
94     ScriptController& scriptController = page->mainFrame()->script();
95     if (!scriptController.canExecuteScripts(NotAboutToExecuteScript))
96         return;
97
98     v8::HandleScope scope(m_isolate);
99     v8::Local<v8::Context> debuggerContext = v8::Debug::GetDebugContext();
100     v8::Context::Scope contextScope(debuggerContext);
101
102     v8::Local<v8::Object> debuggerScript = m_debuggerScript.newLocal(m_isolate);
103     if (!m_listenersMap.size()) {
104         ensureDebuggerScriptCompiled();
105         ASSERT(!debuggerScript->IsUndefined());
106         v8::Debug::SetDebugEventListener2(&PageScriptDebugServer::v8DebugEventCallback, v8::External::New(m_isolate, this));
107     }
108     m_listenersMap.set(page, listener);
109
110     V8WindowShell* shell = scriptController.existingWindowShell(mainThreadNormalWorld());
111     if (!shell || !shell->isContextInitialized())
112         return;
113     v8::Local<v8::Context> context = shell->context();
114     v8::Handle<v8::Function> getScriptsFunction = v8::Local<v8::Function>::Cast(debuggerScript->Get(v8AtomicString(m_isolate, "getScripts")));
115     v8::Handle<v8::Value> argv[] = { context->GetEmbedderData(0) };
116     v8::Handle<v8::Value> value = V8ScriptRunner::callInternalFunction(getScriptsFunction, debuggerScript, WTF_ARRAY_LENGTH(argv), argv, m_isolate);
117     if (value.IsEmpty())
118         return;
119     ASSERT(!value->IsUndefined() && value->IsArray());
120     v8::Handle<v8::Array> scriptsArray = v8::Handle<v8::Array>::Cast(value);
121     for (unsigned i = 0; i < scriptsArray->Length(); ++i)
122         dispatchDidParseSource(listener, v8::Handle<v8::Object>::Cast(scriptsArray->Get(v8::Integer::New(m_isolate, i))));
123 }
124
125 void PageScriptDebugServer::removeListener(ScriptDebugListener* listener, Page* page)
126 {
127     if (!m_listenersMap.contains(page))
128         return;
129
130     if (m_pausedPage == page)
131         continueProgram();
132
133     m_listenersMap.remove(page);
134
135     if (m_listenersMap.isEmpty())
136         v8::Debug::SetDebugEventListener2(0);
137     // FIXME: Remove all breakpoints set by the agent.
138 }
139
140 void PageScriptDebugServer::setClientMessageLoop(PassOwnPtr<ClientMessageLoop> clientMessageLoop)
141 {
142     m_clientMessageLoop = clientMessageLoop;
143 }
144
145 void PageScriptDebugServer::compileScript(ScriptState* state, const String& expression, const String& sourceURL, String* scriptId, String* exceptionMessage)
146 {
147     ExecutionContext* executionContext = state->executionContext();
148     RefPtr<Frame> protect = toDocument(executionContext)->frame();
149     ScriptDebugServer::compileScript(state, expression, sourceURL, scriptId, exceptionMessage);
150     if (!scriptId->isNull())
151         m_compiledScriptURLs.set(*scriptId, sourceURL);
152 }
153
154 void PageScriptDebugServer::clearCompiledScripts()
155 {
156     ScriptDebugServer::clearCompiledScripts();
157     m_compiledScriptURLs.clear();
158 }
159
160 void PageScriptDebugServer::runScript(ScriptState* state, const String& scriptId, ScriptValue* result, bool* wasThrown, String* exceptionMessage)
161 {
162     String sourceURL = m_compiledScriptURLs.take(scriptId);
163
164     ExecutionContext* executionContext = state->executionContext();
165     Frame* frame = toDocument(executionContext)->frame();
166     InspectorInstrumentationCookie cookie;
167     if (frame)
168         cookie = InspectorInstrumentation::willEvaluateScript(frame, sourceURL, TextPosition::minimumPosition().m_line.oneBasedInt());
169
170     RefPtr<Frame> protect = frame;
171     ScriptDebugServer::runScript(state, scriptId, result, wasThrown, exceptionMessage);
172
173     if (frame)
174         InspectorInstrumentation::didEvaluateScript(cookie);
175 }
176
177 ScriptDebugListener* PageScriptDebugServer::getDebugListenerForContext(v8::Handle<v8::Context> context)
178 {
179     v8::HandleScope scope(m_isolate);
180     Frame* frame = retrieveFrameWithGlobalObjectCheck(context);
181     if (!frame)
182         return 0;
183     return m_listenersMap.get(frame->page());
184 }
185
186 void PageScriptDebugServer::runMessageLoopOnPause(v8::Handle<v8::Context> context)
187 {
188     v8::HandleScope scope(m_isolate);
189     Frame* frame = retrieveFrameWithGlobalObjectCheck(context);
190     m_pausedPage = frame->page();
191
192     // Wait for continue or step command.
193     m_clientMessageLoop->run(m_pausedPage);
194
195     // The listener may have been removed in the nested loop.
196     if (ScriptDebugListener* listener = m_listenersMap.get(m_pausedPage))
197         listener->didContinue();
198
199     m_pausedPage = 0;
200 }
201
202 void PageScriptDebugServer::quitMessageLoopOnPause()
203 {
204     m_clientMessageLoop->quitNow();
205 }
206
207 void PageScriptDebugServer::preprocessBeforeCompile(const v8::Debug::EventDetails& eventDetails)
208 {
209     v8::Handle<v8::Context> eventContext = eventDetails.GetEventContext();
210     Frame* frame = retrieveFrameWithGlobalObjectCheck(eventContext);
211     if (!frame)
212         return;
213
214     if (!canPreprocess(frame))
215         return;
216
217     v8::Handle<v8::Object> eventData = eventDetails.GetEventData();
218     v8::Local<v8::Context> debugContext = v8::Debug::GetDebugContext();
219     v8::Context::Scope contextScope(debugContext);
220     v8::TryCatch tryCatch;
221     // <script> tag source and attribute value source are preprocessed before we enter V8.
222     // Avoid preprocessing any internal scripts by processing only eval source in this V8 event handler.
223     v8::Handle<v8::Value> argvEventData[] = { eventData };
224     v8::Handle<v8::Value> v8Value = callDebuggerMethod("isEvalCompilation", WTF_ARRAY_LENGTH(argvEventData), argvEventData);
225     if (v8Value.IsEmpty() || !v8Value->ToBoolean()->Value())
226         return;
227
228     // The name and source are in the JS event data.
229     String scriptName = toCoreStringWithUndefinedOrNullCheck(callDebuggerMethod("getScriptName", WTF_ARRAY_LENGTH(argvEventData), argvEventData));
230     String script = toCoreStringWithUndefinedOrNullCheck(callDebuggerMethod("getScriptSource", WTF_ARRAY_LENGTH(argvEventData), argvEventData));
231
232     String preprocessedSource  = m_scriptPreprocessor->preprocessSourceCode(script, scriptName);
233
234     v8::Handle<v8::Value> argvPreprocessedScript[] = { eventData, v8String(debugContext->GetIsolate(), preprocessedSource) };
235     callDebuggerMethod("setScriptSource", WTF_ARRAY_LENGTH(argvPreprocessedScript), argvPreprocessedScript);
236 }
237
238 static bool isCreatingPreprocessor = false;
239
240 bool PageScriptDebugServer::canPreprocess(Frame* frame)
241 {
242     ASSERT(frame);
243
244     if (!m_preprocessorSourceCode || !frame->page() || isCreatingPreprocessor)
245         return false;
246
247     // We delay the creation of the preprocessor until just before the first JS from the
248     // Web page to ensure that the debugger's console initialization code has completed.
249     if (!m_scriptPreprocessor) {
250         TemporaryChange<bool> isPreprocessing(isCreatingPreprocessor, true);
251         m_scriptPreprocessor = adoptPtr(new ScriptPreprocessor(*m_preprocessorSourceCode.get(), frame->script(), frame->host()->console()));
252     }
253
254     if (m_scriptPreprocessor->isValid())
255         return true;
256
257     m_scriptPreprocessor.clear();
258     // Don't retry the compile if we fail one time.
259     m_preprocessorSourceCode.clear();
260     return false;
261 }
262
263 // Source to Source processing iff debugger enabled and it has loaded a preprocessor.
264 PassOwnPtr<ScriptSourceCode> PageScriptDebugServer::preprocess(Frame* frame, const ScriptSourceCode& sourceCode)
265 {
266     if (!canPreprocess(frame))
267         return PassOwnPtr<ScriptSourceCode>();
268
269     String preprocessedSource = m_scriptPreprocessor->preprocessSourceCode(sourceCode.source(), sourceCode.url());
270     return adoptPtr(new ScriptSourceCode(preprocessedSource, sourceCode.url()));
271 }
272
273 String PageScriptDebugServer::preprocessEventListener(Frame* frame, const String& source, const String& url, const String& functionName)
274 {
275     if (!canPreprocess(frame))
276         return source;
277
278     return m_scriptPreprocessor->preprocessSourceCode(source, url, functionName);
279 }
280
281 } // namespace WebCore