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