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
33#if ENABLE(INSPECTOR)
34
35#include "PageRuntimeAgent.h"
36
37#include "Document.h"
38#include "InjectedScript.h"
39#include "InjectedScriptManager.h"
40#include "InspectorPageAgent.h"
41#include "InspectorState.h"
42#include "InstrumentingAgents.h"
43#include "Page.h"
44#include "PageConsole.h"
45#include "ScriptController.h"
46#include "SecurityOrigin.h"
47
48using WebCore::TypeBuilder::Runtime::ExecutionContextDescription;
49
50namespace WebCore {
51
52namespace PageRuntimeAgentState {
53static const char runtimeEnabled[] = "runtimeEnabled";
54};
55
56PageRuntimeAgent::PageRuntimeAgent(InstrumentingAgents* instrumentingAgents, InspectorCompositeState* state, InjectedScriptManager* injectedScriptManager, Page* page, InspectorPageAgent* pageAgent)
57    : InspectorRuntimeAgent(instrumentingAgents, state, injectedScriptManager)
58    , m_inspectedPage(page)
59    , m_pageAgent(pageAgent)
60    , m_frontend(0)
61    , m_mainWorldContextCreated(false)
62{
63    m_instrumentingAgents->setPageRuntimeAgent(this);
64}
65
66PageRuntimeAgent::~PageRuntimeAgent()
67{
68    m_instrumentingAgents->setPageRuntimeAgent(0);
69}
70
71void PageRuntimeAgent::setFrontend(InspectorFrontend* frontend)
72{
73    m_frontend = frontend->runtime();
74}
75
76void PageRuntimeAgent::clearFrontend()
77{
78    m_frontend = 0;
79    String errorString;
80    disable(&errorString);
81}
82
83void PageRuntimeAgent::restore()
84{
85    if (m_state->getBoolean(PageRuntimeAgentState::runtimeEnabled)) {
86        String error;
87        enable(&error);
88    }
89}
90
91void PageRuntimeAgent::enable(ErrorString* errorString)
92{
93    if (m_enabled)
94        return;
95
96    InspectorRuntimeAgent::enable(errorString);
97    m_state->setBoolean(PageRuntimeAgentState::runtimeEnabled, true);
98    // Only report existing contexts if the page did commit load, otherwise we may
99    // unintentionally initialize contexts in the frames which may trigger some listeners
100    // that are expected to be triggered only after the load is committed, see http://crbug.com/131623
101    if (m_mainWorldContextCreated)
102        reportExecutionContextCreation();
103}
104
105void PageRuntimeAgent::disable(ErrorString* errorString)
106{
107    if (!m_enabled)
108        return;
109
110    InspectorRuntimeAgent::disable(errorString);
111    m_state->setBoolean(PageRuntimeAgentState::runtimeEnabled, false);
112}
113
114void PageRuntimeAgent::didCreateMainWorldContext(Frame* frame)
115{
116    m_mainWorldContextCreated = true;
117
118    if (!m_enabled)
119        return;
120    ASSERT(m_frontend);
121    String frameId = m_pageAgent->frameId(frame);
122    ScriptState* scriptState = mainWorldScriptState(frame);
123    notifyContextCreated(frameId, scriptState, 0, true);
124}
125
126void PageRuntimeAgent::didCreateIsolatedContext(Frame* frame, ScriptState* scriptState, SecurityOrigin* origin)
127{
128    if (!m_enabled)
129        return;
130    ASSERT(m_frontend);
131    String frameId = m_pageAgent->frameId(frame);
132    notifyContextCreated(frameId, scriptState, origin, false);
133}
134
135InjectedScript PageRuntimeAgent::injectedScriptForEval(ErrorString* errorString, const int* executionContextId)
136{
137    if (!executionContextId) {
138        ScriptState* scriptState = mainWorldScriptState(m_inspectedPage->mainFrame());
139        InjectedScript result = injectedScriptManager()->injectedScriptFor(scriptState);
140        if (result.hasNoValue())
141            *errorString = "Internal error: main world execution context not found.";
142        return result;
143    }
144    InjectedScript injectedScript = injectedScriptManager()->injectedScriptForId(*executionContextId);
145    if (injectedScript.hasNoValue())
146        *errorString = "Execution context with given id not found.";
147    return injectedScript;
148}
149
150void PageRuntimeAgent::muteConsole()
151{
152    PageConsole::mute();
153}
154
155void PageRuntimeAgent::unmuteConsole()
156{
157    PageConsole::unmute();
158}
159
160void PageRuntimeAgent::reportExecutionContextCreation()
161{
162    Vector<std::pair<ScriptState*, SecurityOrigin*> > isolatedContexts;
163    for (Frame* frame = m_inspectedPage->mainFrame(); frame; frame = frame->tree()->traverseNext()) {
164        if (!frame->script()->canExecuteScripts(NotAboutToExecuteScript))
165            continue;
166        String frameId = m_pageAgent->frameId(frame);
167
168        ScriptState* scriptState = mainWorldScriptState(frame);
169        notifyContextCreated(frameId, scriptState, 0, true);
170        frame->script()->collectIsolatedContexts(isolatedContexts);
171        if (isolatedContexts.isEmpty())
172            continue;
173        for (size_t i = 0; i< isolatedContexts.size(); i++)
174            notifyContextCreated(frameId, isolatedContexts[i].first, isolatedContexts[i].second, false);
175        isolatedContexts.clear();
176    }
177}
178
179void PageRuntimeAgent::notifyContextCreated(const String& frameId, ScriptState* scriptState, SecurityOrigin* securityOrigin, bool isPageContext)
180{
181    ASSERT(securityOrigin || isPageContext);
182    int executionContextId = injectedScriptManager()->injectedScriptIdFor(scriptState);
183    String name = securityOrigin ? securityOrigin->toRawString() : "";
184    m_frontend->executionContextCreated(ExecutionContextDescription::create()
185        .setId(executionContextId)
186        .setIsPageContext(isPageContext)
187        .setName(name)
188        .setFrameId(frameId)
189        .release());
190}
191
192} // namespace WebCore
193
194#endif // ENABLE(INSPECTOR)
195