2 * Copyright (C) 2012 Google Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
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
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.
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.
33 * @extends {WebInspector.TargetAwareObject}
34 * @param {!WebInspector.Target} target
36 WebInspector.RuntimeModel = function(target)
38 WebInspector.TargetAwareObject.call(this, target);
40 this._debuggerModel = target.debuggerModel;
41 this._agent = target.runtimeAgent();
42 this.target().registerRuntimeDispatcher(new WebInspector.RuntimeDispatcher(this));
45 * @type {!Object.<number, !WebInspector.ExecutionContext>}
47 this._executionContextById = {};
50 WebInspector.RuntimeModel.Events = {
51 ExecutionContextCreated: "ExecutionContextCreated",
52 ExecutionContextDestroyed: "ExecutionContextDestroyed",
55 WebInspector.RuntimeModel.prototype = {
58 * @return {!Array.<!WebInspector.ExecutionContext>}
60 executionContexts: function()
62 return Object.values(this._executionContextById);
66 * @param {!RuntimeAgent.ExecutionContextDescription} context
68 _executionContextCreated: function(context)
70 var executionContext = new WebInspector.ExecutionContext(this.target(), context.id, context.name, context.isPageContext, context.frameId);
71 this._executionContextById[executionContext.id] = executionContext;
72 this.dispatchEventToListeners(WebInspector.RuntimeModel.Events.ExecutionContextCreated, executionContext);
76 * @param {number} executionContextId
78 _executionContextDestroyed: function(executionContextId)
80 var executionContext = this._executionContextById[executionContextId];
81 delete this._executionContextById[executionContextId];
82 this.dispatchEventToListeners(WebInspector.RuntimeModel.Events.ExecutionContextDestroyed, executionContext);
85 _executionContextsCleared: function()
87 var contexts = this.executionContexts();
88 this._executionContextById = {};
89 for (var i = 0; i < contexts.length; ++i)
90 this.dispatchEventToListeners(WebInspector.RuntimeModel.Events.ExecutionContextDestroyed, contexts[i]);
94 * @param {!RuntimeAgent.RemoteObject} payload
95 * @return {!WebInspector.RemoteObject}
97 createRemoteObject: function(payload)
99 console.assert(typeof payload === "object", "Remote object payload should only be an object");
100 return new WebInspector.RemoteObjectImpl(this.target(), payload.objectId, payload.type, payload.subtype, payload.value, payload.description, payload.preview);
104 * @param {!RuntimeAgent.RemoteObject} payload
105 * @param {!WebInspector.ScopeRef} scopeRef
106 * @return {!WebInspector.RemoteObject}
108 createScopeRemoteObject: function(payload, scopeRef)
110 return new WebInspector.ScopeRemoteObject(this.target(), payload.objectId, scopeRef, payload.type, payload.subtype, payload.value, payload.description, payload.preview);
114 * @param {number|string|boolean} value
115 * @return {!WebInspector.RemoteObject}
117 createRemoteObjectFromPrimitiveValue: function(value)
119 return new WebInspector.RemoteObjectImpl(this.target(), undefined, typeof value, undefined, value);
123 * @param {string} name
124 * @param {number|string|boolean} value
125 * @return {!WebInspector.RemoteObjectProperty}
127 createRemotePropertyFromPrimitiveValue: function(name, value)
129 return new WebInspector.RemoteObjectProperty(name, this.createRemoteObjectFromPrimitiveValue(value));
132 __proto__: WebInspector.TargetAwareObject.prototype
137 * @implements {RuntimeAgent.Dispatcher}
138 * @param {!WebInspector.RuntimeModel} runtimeModel
140 WebInspector.RuntimeDispatcher = function(runtimeModel)
142 this._runtimeModel = runtimeModel;
145 WebInspector.RuntimeDispatcher.prototype = {
146 executionContextCreated: function(context)
148 this._runtimeModel._executionContextCreated(context);
151 executionContextDestroyed: function(executionContextId)
153 this._runtimeModel._executionContextDestroyed(executionContextId);
156 executionContextsCleared: function()
158 this._runtimeModel._executionContextsCleared();
165 * @extends {WebInspector.TargetAware}
166 * @param {!WebInspector.Target} target
167 * @param {number|undefined} id
168 * @param {string} name
169 * @param {boolean} isPageContext
170 * @param {string=} frameId
172 WebInspector.ExecutionContext = function(target, id, name, isPageContext, frameId)
174 WebInspector.TargetAware.call(this, target);
176 this.name = (isPageContext && !name) ? "<page context>" : name;
177 this.isMainWorldContext = isPageContext;
178 this._debuggerModel = target.debuggerModel;
179 this.frameId = frameId;
183 * @param {!WebInspector.ExecutionContext} a
184 * @param {!WebInspector.ExecutionContext} b
187 WebInspector.ExecutionContext.comparator = function(a, b)
189 // Main world context should always go first.
190 if (a.isMainWorldContext)
192 if (b.isMainWorldContext)
194 return a.name.localeCompare(b.name);
197 WebInspector.ExecutionContext.prototype = {
200 * @param {string} expression
201 * @param {string} objectGroup
202 * @param {boolean} includeCommandLineAPI
203 * @param {boolean} doNotPauseOnExceptionsAndMuteConsole
204 * @param {boolean} returnByValue
205 * @param {boolean} generatePreview
206 * @param {function(?WebInspector.RemoteObject, boolean, ?RuntimeAgent.RemoteObject=)} callback
208 evaluate: function(expression, objectGroup, includeCommandLineAPI, doNotPauseOnExceptionsAndMuteConsole, returnByValue, generatePreview, callback)
210 //FIXME: It will be moved to separate ExecutionContext
211 if (this._debuggerModel.selectedCallFrame()) {
212 this._debuggerModel.evaluateOnSelectedCallFrame(expression, objectGroup, includeCommandLineAPI, doNotPauseOnExceptionsAndMuteConsole, returnByValue, generatePreview, callback);
217 // There is no expression, so the completion should happen against global properties.
222 * @this {WebInspector.ExecutionContext}
223 * @param {?Protocol.Error} error
224 * @param {!RuntimeAgent.RemoteObject} result
225 * @param {boolean=} wasThrown
227 function evalCallback(error, result, wasThrown)
230 callback(null, false);
235 callback(null, !!wasThrown, wasThrown ? null : result);
237 callback(this.target().runtimeModel.createRemoteObject(result), !!wasThrown);
239 this.target().runtimeAgent().evaluate(expression, objectGroup, includeCommandLineAPI, doNotPauseOnExceptionsAndMuteConsole, this.id, returnByValue, generatePreview, evalCallback.bind(this));
243 * @param {string} expressionString
244 * @param {string} prefix
245 * @param {boolean} force
246 * @param {function(!Array.<string>, number=)} completionsReadyCallback
248 completionsForExpression: function(expressionString, prefix, force, completionsReadyCallback)
250 var lastIndex = expressionString.length - 1;
252 var dotNotation = (expressionString[lastIndex] === ".");
253 var bracketNotation = (expressionString[lastIndex] === "[");
255 if (dotNotation || bracketNotation)
256 expressionString = expressionString.substr(0, lastIndex);
258 if (expressionString && parseInt(expressionString, 10) == expressionString) {
259 // User is entering float value, do not suggest anything.
260 completionsReadyCallback([]);
264 if (!prefix && !expressionString && !force) {
265 completionsReadyCallback([]);
269 if (!expressionString && this._debuggerModel.selectedCallFrame())
270 this._debuggerModel.getSelectedCallFrameVariables(receivedPropertyNames.bind(this));
272 this.evaluate(expressionString, "completion", true, true, false, false, evaluated.bind(this));
275 * @this {WebInspector.ExecutionContext}
277 function evaluated(result, wasThrown)
279 if (!result || wasThrown) {
280 completionsReadyCallback([]);
285 * @param {string} primitiveType
286 * @suppressReceiverCheck
287 * @this {WebInspector.ExecutionContext}
289 function getCompletions(primitiveType)
292 if (primitiveType === "string")
293 object = new String("");
294 else if (primitiveType === "number")
295 object = new Number(0);
296 else if (primitiveType === "boolean")
297 object = new Boolean(false);
302 for (var o = object; o; o = o.__proto__) {
304 var names = Object.getOwnPropertyNames(o);
305 for (var i = 0; i < names.length; ++i)
306 resultSet[names[i]] = true;
313 if (result.type === "object" || result.type === "function")
314 result.callFunctionJSON(getCompletions, undefined, receivedPropertyNames.bind(this));
315 else if (result.type === "string" || result.type === "number" || result.type === "boolean")
316 this.evaluate("(" + getCompletions + ")(\"" + result.type + "\")", "completion", false, true, true, false, receivedPropertyNamesFromEval.bind(this));
320 * @param {?WebInspector.RemoteObject} notRelevant
321 * @param {boolean} wasThrown
322 * @param {?RuntimeAgent.RemoteObject=} result
323 * @this {WebInspector.ExecutionContext}
325 function receivedPropertyNamesFromEval(notRelevant, wasThrown, result)
327 if (result && !wasThrown)
328 receivedPropertyNames.call(this, result.value);
330 completionsReadyCallback([]);
334 * @this {WebInspector.ExecutionContext}
336 function receivedPropertyNames(propertyNames)
338 this.target().runtimeAgent().releaseObjectGroup("completion");
339 if (!propertyNames) {
340 completionsReadyCallback([]);
343 var includeCommandLineAPI = (!dotNotation && !bracketNotation);
344 if (includeCommandLineAPI) {
345 const commandLineAPI = ["dir", "dirxml", "keys", "values", "profile", "profileEnd", "monitorEvents", "unmonitorEvents", "inspect", "copy", "clear",
346 "getEventListeners", "debug", "undebug", "monitor", "unmonitor", "table", "$", "$$", "$x"];
347 for (var i = 0; i < commandLineAPI.length; ++i)
348 propertyNames[commandLineAPI[i]] = true;
350 this._reportCompletions(completionsReadyCallback, dotNotation, bracketNotation, expressionString, prefix, Object.keys(propertyNames));
355 * @param {function(!Array.<string>, number=)} completionsReadyCallback
356 * @param {boolean} dotNotation
357 * @param {boolean} bracketNotation
358 * @param {string} expressionString
359 * @param {string} prefix
360 * @param {!Array.<string>} properties
362 _reportCompletions: function(completionsReadyCallback, dotNotation, bracketNotation, expressionString, prefix, properties) {
363 if (bracketNotation) {
364 if (prefix.length && prefix[0] === "'")
367 var quoteUsed = "\"";
372 if (!expressionString) {
373 const keywords = ["break", "case", "catch", "continue", "default", "delete", "do", "else", "finally", "for", "function", "if", "in",
374 "instanceof", "new", "return", "switch", "this", "throw", "try", "typeof", "var", "void", "while", "with"];
375 properties = properties.concat(keywords);
380 for (var i = 0; i < properties.length; ++i) {
381 var property = properties[i];
383 // Assume that all non-ASCII characters are letters and thus can be used as part of identifier.
384 if (dotNotation && !/^[a-zA-Z_$\u008F-\uFFFF][a-zA-Z0-9_$\u008F-\uFFFF]*$/.test(property))
387 if (bracketNotation) {
388 if (!/^[0-9]+$/.test(property))
389 property = quoteUsed + property.escapeCharacters(quoteUsed + "\\") + quoteUsed;
393 if (property.length < prefix.length)
395 if (prefix.length && !property.startsWith(prefix))
398 results.push(property);
400 completionsReadyCallback(results);
403 __proto__: WebInspector.TargetAware.prototype
407 * @type {!WebInspector.RuntimeModel}
409 WebInspector.runtimeModel;