1/*
2 * Copyright (C) 2007-2017 Apple Inc. All rights reserved.
3 * Copyright (C) 2008 Matt Lilek <[email protected]>
4 * Copyright (C) 2012 Google Inc. All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. Neither the name of Apple Inc. ("Apple") nor the names of
16 * its contributors may be used to endorse or promote products derived
17 * from this software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
20 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
23 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
26 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31#include "config.h"
32#include "InjectedScriptManager.h"
33
34#include "CatchScope.h"
35#include "Completion.h"
36#include "InjectedScriptHost.h"
37#include "InjectedScriptSource.h"
38#include "JSCInlines.h"
39#include "JSInjectedScriptHost.h"
40#include "JSLock.h"
41#include "ScriptObject.h"
42#include "SourceCode.h"
43#include <wtf/JSONValues.h>
44
45using namespace JSC;
46
47namespace Inspector {
48
49InjectedScriptManager::InjectedScriptManager(InspectorEnvironment& environment, Ref<InjectedScriptHost>&& injectedScriptHost)
50 : m_environment(environment)
51 , m_injectedScriptHost(WTFMove(injectedScriptHost))
52 , m_nextInjectedScriptId(1)
53{
54}
55
56InjectedScriptManager::~InjectedScriptManager()
57{
58}
59
60void InjectedScriptManager::connect()
61{
62}
63
64void InjectedScriptManager::disconnect()
65{
66 discardInjectedScripts();
67}
68
69void InjectedScriptManager::discardInjectedScripts()
70{
71 m_injectedScriptHost->clearAllWrappers();
72 m_idToInjectedScript.clear();
73 m_scriptStateToId.clear();
74}
75
76InjectedScriptHost& InjectedScriptManager::injectedScriptHost()
77{
78 return m_injectedScriptHost.get();
79}
80
81InjectedScript InjectedScriptManager::injectedScriptForId(int id)
82{
83 auto it = m_idToInjectedScript.find(id);
84 if (it != m_idToInjectedScript.end())
85 return it->value;
86
87 for (auto it = m_scriptStateToId.begin(); it != m_scriptStateToId.end(); ++it) {
88 if (it->value == id)
89 return injectedScriptFor(it->key);
90 }
91
92 return InjectedScript();
93}
94
95int InjectedScriptManager::injectedScriptIdFor(ExecState* scriptState)
96{
97 auto it = m_scriptStateToId.find(scriptState);
98 if (it != m_scriptStateToId.end())
99 return it->value;
100
101 int id = m_nextInjectedScriptId++;
102 m_scriptStateToId.set(scriptState, id);
103 return id;
104}
105
106InjectedScript InjectedScriptManager::injectedScriptForObjectId(const String& objectId)
107{
108 RefPtr<JSON::Value> parsedObjectId;
109 if (!JSON::Value::parseJSON(objectId, parsedObjectId))
110 return InjectedScript();
111
112 RefPtr<JSON::Object> resultObject;
113 if (!parsedObjectId->asObject(resultObject))
114 return InjectedScript();
115
116 long injectedScriptId = 0;
117 if (!resultObject->getInteger("injectedScriptId"_s, injectedScriptId))
118 return InjectedScript();
119
120 return m_idToInjectedScript.get(injectedScriptId);
121}
122
123void InjectedScriptManager::releaseObjectGroup(const String& objectGroup)
124{
125 for (auto& injectedScript : m_idToInjectedScript.values())
126 injectedScript.releaseObjectGroup(objectGroup);
127}
128
129void InjectedScriptManager::clearEventValue()
130{
131 for (auto& injectedScript : m_idToInjectedScript.values())
132 injectedScript.clearEventValue();
133}
134
135void InjectedScriptManager::clearExceptionValue()
136{
137 for (auto& injectedScript : m_idToInjectedScript.values())
138 injectedScript.clearExceptionValue();
139}
140
141String InjectedScriptManager::injectedScriptSource()
142{
143 return StringImpl::createWithoutCopying(InjectedScriptSource_js, sizeof(InjectedScriptSource_js));
144}
145
146JSC::JSObject* InjectedScriptManager::createInjectedScript(const String& source, ExecState* scriptState, int id)
147{
148 VM& vm = scriptState->vm();
149 JSLockHolder lock(vm);
150 auto scope = DECLARE_CATCH_SCOPE(vm);
151
152 SourceCode sourceCode = makeSource(source, { });
153 JSGlobalObject* globalObject = scriptState->lexicalGlobalObject();
154 JSValue globalThisValue = scriptState->globalThisValue();
155
156 NakedPtr<Exception> evaluationException;
157 InspectorEvaluateHandler evaluateHandler = m_environment.evaluateHandler();
158 JSValue functionValue = evaluateHandler(scriptState, sourceCode, globalThisValue, evaluationException);
159 if (evaluationException)
160 return nullptr;
161
162 CallData callData;
163 CallType callType = getCallData(vm, functionValue, callData);
164 if (callType == CallType::None)
165 return nullptr;
166
167 MarkedArgumentBuffer args;
168 args.append(m_injectedScriptHost->wrapper(scriptState, globalObject));
169 args.append(globalThisValue);
170 args.append(jsNumber(id));
171 ASSERT(!args.hasOverflowed());
172
173 JSValue result = JSC::call(scriptState, functionValue, callType, callData, globalThisValue, args);
174 scope.clearException();
175 return result.getObject();
176}
177
178InjectedScript InjectedScriptManager::injectedScriptFor(ExecState* inspectedExecState)
179{
180 auto it = m_scriptStateToId.find(inspectedExecState);
181 if (it != m_scriptStateToId.end()) {
182 auto it1 = m_idToInjectedScript.find(it->value);
183 if (it1 != m_idToInjectedScript.end())
184 return it1->value;
185 }
186
187 if (!m_environment.canAccessInspectedScriptState(inspectedExecState))
188 return InjectedScript();
189
190 int id = injectedScriptIdFor(inspectedExecState);
191 auto injectedScriptObject = createInjectedScript(injectedScriptSource(), inspectedExecState, id);
192 if (!injectedScriptObject) {
193 WTFLogAlways("Failed to parse/execute InjectedScriptSource.js!");
194 WTFLogAlways("%s\n", injectedScriptSource().ascii().data());
195 RELEASE_ASSERT_NOT_REACHED();
196 }
197
198 InjectedScript result({ inspectedExecState, injectedScriptObject }, &m_environment);
199 m_idToInjectedScript.set(id, result);
200 didCreateInjectedScript(result);
201 return result;
202}
203
204void InjectedScriptManager::didCreateInjectedScript(const InjectedScript&)
205{
206 // Intentionally empty. This allows for subclasses to inject additional scripts.
207}
208
209} // namespace Inspector
210
211