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