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(JSGlobalObject* globalObject)
96{
97 auto it = m_scriptStateToId.find(globalObject);
98 if (it != m_scriptStateToId.end())
99 return it->value;
100
101 int id = m_nextInjectedScriptId++;
102 m_scriptStateToId.set(globalObject, 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
146Expected<JSObject*, NakedPtr<Exception>> InjectedScriptManager::createInjectedScript(const String& source, JSGlobalObject* globalObject, int id)
147{
148 VM& vm = globalObject->vm();
149 JSLockHolder lock(vm);
150 auto scope = DECLARE_CATCH_SCOPE(vm);
151
152 SourceCode sourceCode = makeSource(source, { });
153 JSValue globalThisValue = globalObject->globalThis();
154
155 NakedPtr<Exception> evaluationException;
156 InspectorEvaluateHandler evaluateHandler = m_environment.evaluateHandler();
157 JSValue functionValue = evaluateHandler(globalObject, sourceCode, globalThisValue, evaluationException);
158 if (evaluationException)
159 return makeUnexpected(evaluationException);
160
161 CallData callData;
162 CallType callType = getCallData(vm, functionValue, callData);
163 if (callType == CallType::None)
164 return nullptr;
165
166 MarkedArgumentBuffer args;
167 args.append(m_injectedScriptHost->wrapper(globalObject));
168 args.append(globalThisValue);
169 args.append(jsNumber(id));
170 ASSERT(!args.hasOverflowed());
171
172 JSValue result = JSC::call(globalObject, functionValue, callType, callData, globalThisValue, args);
173 scope.clearException();
174 return result.getObject();
175}
176
177InjectedScript InjectedScriptManager::injectedScriptFor(JSGlobalObject* globalObject)
178{
179 auto it = m_scriptStateToId.find(globalObject);
180 if (it != m_scriptStateToId.end()) {
181 auto it1 = m_idToInjectedScript.find(it->value);
182 if (it1 != m_idToInjectedScript.end())
183 return it1->value;
184 }
185
186 if (!m_environment.canAccessInspectedScriptState(globalObject))
187 return InjectedScript();
188
189 int id = injectedScriptIdFor(globalObject);
190 auto createResult = createInjectedScript(injectedScriptSource(), globalObject, id);
191 if (!createResult) {
192 auto& error = createResult.error();
193 ASSERT(error);
194
195 unsigned line = 0;
196 unsigned column = 0;
197 auto& stack = error->stack();
198 if (stack.size() > 0)
199 stack[0].computeLineAndColumn(line, column);
200 WTFLogAlways("Error when creating injected script: %s (%d:%d)\n", error->value().toWTFString(globalObject).utf8().data(), line, column);
201 WTFLogAlways("%s\n", injectedScriptSource().utf8().data());
202 RELEASE_ASSERT_NOT_REACHED();
203 }
204 if (!createResult.value()) {
205 WTFLogAlways("Missing injected script object");
206 WTFLogAlways("%s\n", injectedScriptSource().utf8().data());
207 RELEASE_ASSERT_NOT_REACHED();
208 }
209
210 InjectedScript result({ globalObject, createResult.value() }, &m_environment);
211 m_idToInjectedScript.set(id, result);
212 didCreateInjectedScript(result);
213 return result;
214}
215
216void InjectedScriptManager::didCreateInjectedScript(const InjectedScript&)
217{
218 // Intentionally empty. This allows for subclasses to inject additional scripts.
219}
220
221} // namespace Inspector
222
223