1 /*
2 * Copyright (C) 2012 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 "core/inspector/InspectorCanvasAgent.h"
33
34 #include "bindings/v8/ScriptProfiler.h"
35 #include "bindings/v8/ScriptValue.h"
36 #include "core/html/HTMLCanvasElement.h"
37 #include "core/inspector/BindingVisitors.h"
38 #include "core/inspector/InjectedScript.h"
39 #include "core/inspector/InjectedScriptCanvasModule.h"
40 #include "core/inspector/InjectedScriptManager.h"
41 #include "core/inspector/InspectorPageAgent.h"
42 #include "core/inspector/InspectorState.h"
43 #include "core/inspector/InstrumentingAgents.h"
44 #include "core/loader/DocumentLoader.h"
45 #include "core/frame/LocalDOMWindow.h"
46 #include "core/frame/LocalFrame.h"
47
48 using WebCore::TypeBuilder::Array;
49 using WebCore::TypeBuilder::Canvas::ResourceId;
50 using WebCore::TypeBuilder::Canvas::ResourceState;
51 using WebCore::TypeBuilder::Canvas::TraceLog;
52 using WebCore::TypeBuilder::Canvas::TraceLogId;
53 using WebCore::TypeBuilder::Page::FrameId;
54 using WebCore::TypeBuilder::Runtime::RemoteObject;
55
56 namespace WebCore {
57
58 namespace CanvasAgentState {
59 static const char canvasAgentEnabled[] = "canvasAgentEnabled";
60 };
61
InspectorCanvasAgent(InspectorPageAgent * pageAgent,InjectedScriptManager * injectedScriptManager)62 InspectorCanvasAgent::InspectorCanvasAgent(InspectorPageAgent* pageAgent, InjectedScriptManager* injectedScriptManager)
63 : InspectorBaseAgent<InspectorCanvasAgent>("Canvas")
64 , m_pageAgent(pageAgent)
65 , m_injectedScriptManager(injectedScriptManager)
66 , m_frontend(0)
67 , m_enabled(false)
68 {
69 }
70
~InspectorCanvasAgent()71 InspectorCanvasAgent::~InspectorCanvasAgent()
72 {
73 }
74
setFrontend(InspectorFrontend * frontend)75 void InspectorCanvasAgent::setFrontend(InspectorFrontend* frontend)
76 {
77 ASSERT(frontend);
78 m_frontend = frontend->canvas();
79 }
80
clearFrontend()81 void InspectorCanvasAgent::clearFrontend()
82 {
83 m_frontend = 0;
84 disable(0);
85 }
86
restore()87 void InspectorCanvasAgent::restore()
88 {
89 if (m_state->getBoolean(CanvasAgentState::canvasAgentEnabled)) {
90 ErrorString error;
91 enable(&error);
92 }
93 }
94
enable(ErrorString *)95 void InspectorCanvasAgent::enable(ErrorString*)
96 {
97 if (m_enabled)
98 return;
99 m_enabled = true;
100 m_state->setBoolean(CanvasAgentState::canvasAgentEnabled, m_enabled);
101 m_instrumentingAgents->setInspectorCanvasAgent(this);
102 findFramesWithUninstrumentedCanvases();
103 }
104
disable(ErrorString *)105 void InspectorCanvasAgent::disable(ErrorString*)
106 {
107 m_enabled = false;
108 m_state->setBoolean(CanvasAgentState::canvasAgentEnabled, m_enabled);
109 m_instrumentingAgents->setInspectorCanvasAgent(0);
110 m_framesWithUninstrumentedCanvases.clear();
111 if (m_frontend)
112 m_frontend->traceLogsRemoved(0, 0);
113 }
114
dropTraceLog(ErrorString * errorString,const TraceLogId & traceLogId)115 void InspectorCanvasAgent::dropTraceLog(ErrorString* errorString, const TraceLogId& traceLogId)
116 {
117 InjectedScriptCanvasModule module = injectedScriptCanvasModule(errorString, traceLogId);
118 if (!module.isEmpty())
119 module.dropTraceLog(errorString, traceLogId);
120 }
121
hasUninstrumentedCanvases(ErrorString * errorString,bool * result)122 void InspectorCanvasAgent::hasUninstrumentedCanvases(ErrorString* errorString, bool* result)
123 {
124 if (!checkIsEnabled(errorString))
125 return;
126 for (FramesWithUninstrumentedCanvases::const_iterator it = m_framesWithUninstrumentedCanvases.begin(); it != m_framesWithUninstrumentedCanvases.end(); ++it) {
127 if (it->value) {
128 *result = true;
129 return;
130 }
131 }
132 *result = false;
133 }
134
captureFrame(ErrorString * errorString,const FrameId * frameId,TraceLogId * traceLogId)135 void InspectorCanvasAgent::captureFrame(ErrorString* errorString, const FrameId* frameId, TraceLogId* traceLogId)
136 {
137 LocalFrame* frame = frameId ? m_pageAgent->assertFrame(errorString, *frameId) : m_pageAgent->mainFrame();
138 if (!frame)
139 return;
140 InjectedScriptCanvasModule module = injectedScriptCanvasModule(errorString, ScriptState::forMainWorld(frame));
141 if (!module.isEmpty())
142 module.captureFrame(errorString, traceLogId);
143 }
144
startCapturing(ErrorString * errorString,const FrameId * frameId,TraceLogId * traceLogId)145 void InspectorCanvasAgent::startCapturing(ErrorString* errorString, const FrameId* frameId, TraceLogId* traceLogId)
146 {
147 LocalFrame* frame = frameId ? m_pageAgent->assertFrame(errorString, *frameId) : m_pageAgent->mainFrame();
148 if (!frame)
149 return;
150 InjectedScriptCanvasModule module = injectedScriptCanvasModule(errorString, ScriptState::forMainWorld(frame));
151 if (!module.isEmpty())
152 module.startCapturing(errorString, traceLogId);
153 }
154
stopCapturing(ErrorString * errorString,const TraceLogId & traceLogId)155 void InspectorCanvasAgent::stopCapturing(ErrorString* errorString, const TraceLogId& traceLogId)
156 {
157 InjectedScriptCanvasModule module = injectedScriptCanvasModule(errorString, traceLogId);
158 if (!module.isEmpty())
159 module.stopCapturing(errorString, traceLogId);
160 }
161
getTraceLog(ErrorString * errorString,const TraceLogId & traceLogId,const int * startOffset,const int * maxLength,RefPtr<TraceLog> & traceLog)162 void InspectorCanvasAgent::getTraceLog(ErrorString* errorString, const TraceLogId& traceLogId, const int* startOffset, const int* maxLength, RefPtr<TraceLog>& traceLog)
163 {
164 InjectedScriptCanvasModule module = injectedScriptCanvasModule(errorString, traceLogId);
165 if (!module.isEmpty())
166 module.traceLog(errorString, traceLogId, startOffset, maxLength, &traceLog);
167 }
168
replayTraceLog(ErrorString * errorString,const TraceLogId & traceLogId,int stepNo,RefPtr<ResourceState> & result,double * replayTime)169 void InspectorCanvasAgent::replayTraceLog(ErrorString* errorString, const TraceLogId& traceLogId, int stepNo, RefPtr<ResourceState>& result, double* replayTime)
170 {
171 InjectedScriptCanvasModule module = injectedScriptCanvasModule(errorString, traceLogId);
172 if (!module.isEmpty())
173 module.replayTraceLog(errorString, traceLogId, stepNo, &result, replayTime);
174 }
175
getResourceState(ErrorString * errorString,const TraceLogId & traceLogId,const ResourceId & resourceId,RefPtr<ResourceState> & result)176 void InspectorCanvasAgent::getResourceState(ErrorString* errorString, const TraceLogId& traceLogId, const ResourceId& resourceId, RefPtr<ResourceState>& result)
177 {
178 InjectedScriptCanvasModule module = injectedScriptCanvasModule(errorString, traceLogId);
179 if (!module.isEmpty())
180 module.resourceState(errorString, traceLogId, resourceId, &result);
181 }
182
evaluateTraceLogCallArgument(ErrorString * errorString,const TraceLogId & traceLogId,int callIndex,int argumentIndex,const String * objectGroup,RefPtr<RemoteObject> & result,RefPtr<ResourceState> & resourceState)183 void InspectorCanvasAgent::evaluateTraceLogCallArgument(ErrorString* errorString, const TraceLogId& traceLogId, int callIndex, int argumentIndex, const String* objectGroup, RefPtr<RemoteObject>& result, RefPtr<ResourceState>& resourceState)
184 {
185 InjectedScriptCanvasModule module = injectedScriptCanvasModule(errorString, traceLogId);
186 if (!module.isEmpty())
187 module.evaluateTraceLogCallArgument(errorString, traceLogId, callIndex, argumentIndex, objectGroup ? *objectGroup : String(), &result, &resourceState);
188 }
189
wrapCanvas2DRenderingContextForInstrumentation(const ScriptValue & context)190 ScriptValue InspectorCanvasAgent::wrapCanvas2DRenderingContextForInstrumentation(const ScriptValue& context)
191 {
192 ErrorString error;
193 InjectedScriptCanvasModule module = injectedScriptCanvasModule(&error, context);
194 if (module.isEmpty())
195 return ScriptValue();
196 return notifyRenderingContextWasWrapped(module.wrapCanvas2DContext(context));
197 }
198
wrapWebGLRenderingContextForInstrumentation(const ScriptValue & glContext)199 ScriptValue InspectorCanvasAgent::wrapWebGLRenderingContextForInstrumentation(const ScriptValue& glContext)
200 {
201 ErrorString error;
202 InjectedScriptCanvasModule module = injectedScriptCanvasModule(&error, glContext);
203 if (module.isEmpty())
204 return ScriptValue();
205 return notifyRenderingContextWasWrapped(module.wrapWebGLContext(glContext));
206 }
207
notifyRenderingContextWasWrapped(const ScriptValue & wrappedContext)208 ScriptValue InspectorCanvasAgent::notifyRenderingContextWasWrapped(const ScriptValue& wrappedContext)
209 {
210 ASSERT(m_frontend);
211 ScriptState* scriptState = wrappedContext.scriptState();
212 LocalDOMWindow* domWindow = 0;
213 if (scriptState)
214 domWindow = scriptState->domWindow();
215 LocalFrame* frame = domWindow ? domWindow->frame() : 0;
216 if (frame && !m_framesWithUninstrumentedCanvases.contains(frame))
217 m_framesWithUninstrumentedCanvases.set(frame, false);
218 String frameId = m_pageAgent->frameId(frame);
219 if (!frameId.isEmpty())
220 m_frontend->contextCreated(frameId);
221 return wrappedContext;
222 }
223
injectedScriptCanvasModule(ErrorString * errorString,ScriptState * scriptState)224 InjectedScriptCanvasModule InspectorCanvasAgent::injectedScriptCanvasModule(ErrorString* errorString, ScriptState* scriptState)
225 {
226 if (!checkIsEnabled(errorString))
227 return InjectedScriptCanvasModule();
228 InjectedScriptCanvasModule module = InjectedScriptCanvasModule::moduleForState(m_injectedScriptManager, scriptState);
229 if (module.isEmpty()) {
230 ASSERT_NOT_REACHED();
231 *errorString = "Internal error: no Canvas module";
232 }
233 return module;
234 }
235
injectedScriptCanvasModule(ErrorString * errorString,const ScriptValue & scriptValue)236 InjectedScriptCanvasModule InspectorCanvasAgent::injectedScriptCanvasModule(ErrorString* errorString, const ScriptValue& scriptValue)
237 {
238 if (!checkIsEnabled(errorString))
239 return InjectedScriptCanvasModule();
240 if (scriptValue.isEmpty()) {
241 ASSERT_NOT_REACHED();
242 *errorString = "Internal error: original ScriptValue has no value";
243 return InjectedScriptCanvasModule();
244 }
245 return injectedScriptCanvasModule(errorString, scriptValue.scriptState());
246 }
247
injectedScriptCanvasModule(ErrorString * errorString,const String & objectId)248 InjectedScriptCanvasModule InspectorCanvasAgent::injectedScriptCanvasModule(ErrorString* errorString, const String& objectId)
249 {
250 if (!checkIsEnabled(errorString))
251 return InjectedScriptCanvasModule();
252 InjectedScript injectedScript = m_injectedScriptManager->injectedScriptForObjectId(objectId);
253 if (injectedScript.isEmpty()) {
254 *errorString = "Inspected frame has gone";
255 return InjectedScriptCanvasModule();
256 }
257 return injectedScriptCanvasModule(errorString, injectedScript.scriptState());
258 }
259
findFramesWithUninstrumentedCanvases()260 void InspectorCanvasAgent::findFramesWithUninstrumentedCanvases()
261 {
262 class NodeVisitor FINAL : public WrappedNodeVisitor {
263 public:
264 NodeVisitor(Page* page, FramesWithUninstrumentedCanvases& result)
265 : m_page(page)
266 , m_framesWithUninstrumentedCanvases(result)
267 {
268 }
269
270 virtual void visitNode(Node* node) OVERRIDE
271 {
272 ASSERT(node);
273 if (!isHTMLCanvasElement(*node) || !node->document().frame())
274 return;
275
276 LocalFrame* frame = node->document().frame();
277 if (frame->page() != m_page)
278 return;
279
280 if (toHTMLCanvasElement(node)->renderingContext())
281 m_framesWithUninstrumentedCanvases.set(frame, true);
282 }
283
284 private:
285 Page* m_page;
286 FramesWithUninstrumentedCanvases& m_framesWithUninstrumentedCanvases;
287 } nodeVisitor(m_pageAgent->page(), m_framesWithUninstrumentedCanvases);
288
289 m_framesWithUninstrumentedCanvases.clear();
290 ScriptProfiler::visitNodeWrappers(&nodeVisitor);
291
292 if (m_frontend) {
293 for (FramesWithUninstrumentedCanvases::const_iterator it = m_framesWithUninstrumentedCanvases.begin(); it != m_framesWithUninstrumentedCanvases.end(); ++it) {
294 String frameId = m_pageAgent->frameId(it->key);
295 if (!frameId.isEmpty())
296 m_frontend->contextCreated(frameId);
297 }
298 }
299 }
300
checkIsEnabled(ErrorString * errorString) const301 bool InspectorCanvasAgent::checkIsEnabled(ErrorString* errorString) const
302 {
303 if (m_enabled)
304 return true;
305 *errorString = "Canvas agent is not enabled";
306 return false;
307 }
308
didCommitLoad(LocalFrame *,DocumentLoader * loader)309 void InspectorCanvasAgent::didCommitLoad(LocalFrame*, DocumentLoader* loader)
310 {
311 if (!m_enabled)
312 return;
313 Frame* frame = loader->frame();
314 if (frame == m_pageAgent->mainFrame()) {
315 for (FramesWithUninstrumentedCanvases::iterator it = m_framesWithUninstrumentedCanvases.begin(); it != m_framesWithUninstrumentedCanvases.end(); ++it)
316 it->value = false;
317 m_frontend->traceLogsRemoved(0, 0);
318 } else {
319 while (frame) {
320 if (frame->isLocalFrame()) {
321 LocalFrame* localFrame = toLocalFrame(frame);
322 if (m_framesWithUninstrumentedCanvases.contains(localFrame))
323 m_framesWithUninstrumentedCanvases.set(localFrame, false);
324 if (m_pageAgent->hasIdForFrame(localFrame)) {
325 String frameId = m_pageAgent->frameId(localFrame);
326 m_frontend->traceLogsRemoved(&frameId, 0);
327 }
328 }
329 frame = frame->tree().traverseNext();
330 }
331 }
332 }
333
frameDetachedFromParent(LocalFrame * frame)334 void InspectorCanvasAgent::frameDetachedFromParent(LocalFrame* frame)
335 {
336 if (m_enabled)
337 m_framesWithUninstrumentedCanvases.remove(frame);
338 }
339
didBeginFrame()340 void InspectorCanvasAgent::didBeginFrame()
341 {
342 if (!m_enabled)
343 return;
344 ErrorString error;
345 for (FramesWithUninstrumentedCanvases::const_iterator it = m_framesWithUninstrumentedCanvases.begin(); it != m_framesWithUninstrumentedCanvases.end(); ++it) {
346 InjectedScriptCanvasModule module = injectedScriptCanvasModule(&error, ScriptState::forMainWorld(it->key));
347 if (!module.isEmpty())
348 module.markFrameEnd();
349 }
350 }
351
352 } // namespace WebCore
353
354