Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / devtools / front_end / RuntimeModel.js
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 /**
32  * @constructor
33  * @extends {WebInspector.Object}
34  * @param {!WebInspector.ResourceTreeModel} resourceTreeModel
35  */
36 WebInspector.RuntimeModel = function(resourceTreeModel)
37 {
38     resourceTreeModel.addEventListener(WebInspector.ResourceTreeModel.EventTypes.FrameAdded, this._frameAdded, this);
39     resourceTreeModel.addEventListener(WebInspector.ResourceTreeModel.EventTypes.FrameNavigated, this._frameNavigated, this);
40     resourceTreeModel.addEventListener(WebInspector.ResourceTreeModel.EventTypes.FrameDetached, this._frameDetached, this);
41     resourceTreeModel.addEventListener(WebInspector.ResourceTreeModel.EventTypes.CachedResourcesLoaded, this._didLoadCachedResources, this);
42     this._frameIdToContextList = {};
43 }
44
45 WebInspector.RuntimeModel.Events = {
46     FrameExecutionContextListAdded: "FrameExecutionContextListAdded",
47     FrameExecutionContextListRemoved: "FrameExecutionContextListRemoved",
48 }
49
50 WebInspector.RuntimeModel.prototype = {
51     /**
52      * @param {?WebInspector.ExecutionContext} executionContext
53      */
54     setCurrentExecutionContext: function(executionContext)
55     {
56         this._currentExecutionContext = executionContext;
57     },
58
59     /**
60      * @return {?WebInspector.ExecutionContext}
61      */
62     currentExecutionContext: function()
63     {
64         return this._currentExecutionContext;
65     },
66
67     /**
68      * @return {!Array.<!WebInspector.FrameExecutionContextList>}
69      */
70     contextLists: function()
71     {
72         return Object.values(this._frameIdToContextList);
73     },
74
75     /**
76      * @param {!WebInspector.ResourceTreeFrame} frame
77      * @return {!WebInspector.FrameExecutionContextList}
78      */
79     contextListByFrame: function(frame)
80     {
81         return this._frameIdToContextList[frame.id];
82     },
83
84     /**
85      * @param {!WebInspector.Event} event
86      */
87     _frameAdded: function(event)
88     {
89         var frame = /** @type {!WebInspector.ResourceTreeFrame} */ (event.data);
90         var context = new WebInspector.FrameExecutionContextList(frame);
91         this._frameIdToContextList[frame.id] = context;
92         this.dispatchEventToListeners(WebInspector.RuntimeModel.Events.FrameExecutionContextListAdded, context);
93     },
94
95     /**
96      * @param {!WebInspector.Event} event
97      */
98     _frameNavigated: function(event)
99     {
100         var frame = /** @type {!WebInspector.ResourceTreeFrame} */ (event.data);
101         var context = this._frameIdToContextList[frame.id];
102         if (context)
103             context._frameNavigated(frame);
104     },
105
106     /**
107      * @param {!WebInspector.Event} event
108      */
109     _frameDetached: function(event)
110     {
111         var frame = /** @type {!WebInspector.ResourceTreeFrame} */ (event.data);
112         var context = this._frameIdToContextList[frame.id];
113         if (!context)
114             return;
115         this.dispatchEventToListeners(WebInspector.RuntimeModel.Events.FrameExecutionContextListRemoved, context);
116         delete this._frameIdToContextList[frame.id];
117     },
118
119     _didLoadCachedResources: function()
120     {
121         InspectorBackend.registerRuntimeDispatcher(new WebInspector.RuntimeDispatcher(this));
122         RuntimeAgent.enable();
123     },
124
125     _executionContextCreated: function(context)
126     {
127         var contextList = this._frameIdToContextList[context.frameId];
128         console.assert(contextList);
129         contextList._addExecutionContext(new WebInspector.ExecutionContext(context.id, context.name, context.isPageContext));
130     },
131
132     /**
133      * @param {string} expression
134      * @param {string} objectGroup
135      * @param {boolean} includeCommandLineAPI
136      * @param {boolean} doNotPauseOnExceptionsAndMuteConsole
137      * @param {boolean} returnByValue
138      * @param {boolean} generatePreview
139      * @param {function(?WebInspector.RemoteObject, boolean, ?RuntimeAgent.RemoteObject=)} callback
140      */
141     evaluate: function(expression, objectGroup, includeCommandLineAPI, doNotPauseOnExceptionsAndMuteConsole, returnByValue, generatePreview, callback)
142     {
143         if (WebInspector.debuggerModel.selectedCallFrame()) {
144             WebInspector.debuggerModel.evaluateOnSelectedCallFrame(expression, objectGroup, includeCommandLineAPI, doNotPauseOnExceptionsAndMuteConsole, returnByValue, generatePreview, callback);
145             return;
146         }
147
148         if (!expression) {
149             // There is no expression, so the completion should happen against global properties.
150             expression = "this";
151         }
152
153         /**
154          * @param {?Protocol.Error} error
155          * @param {!RuntimeAgent.RemoteObject} result
156          * @param {boolean=} wasThrown
157          */
158         function evalCallback(error, result, wasThrown)
159         {
160             if (error) {
161                 callback(null, false);
162                 return;
163             }
164
165             if (returnByValue)
166                 callback(null, !!wasThrown, wasThrown ? null : result);
167             else
168                 callback(WebInspector.RemoteObject.fromPayload(result), !!wasThrown);
169         }
170         RuntimeAgent.evaluate(expression, objectGroup, includeCommandLineAPI, doNotPauseOnExceptionsAndMuteConsole, this._currentExecutionContext ? this._currentExecutionContext.id : undefined, returnByValue, generatePreview, evalCallback);
171     },
172
173     /**
174      * @param {!Element} proxyElement
175      * @param {!Range} wordRange
176      * @param {boolean} force
177      * @param {function(!Array.<string>, number=)} completionsReadyCallback
178      */
179     completionsForTextPrompt: function(proxyElement, wordRange, force, completionsReadyCallback)
180     {
181         // Pass less stop characters to rangeOfWord so the range will be a more complete expression.
182         var expressionRange = wordRange.startContainer.rangeOfWord(wordRange.startOffset, " =:[({;,!+-*/&|^<>", proxyElement, "backward");
183         var expressionString = expressionRange.toString();
184         var prefix = wordRange.toString();
185         this._completionsForExpression(expressionString, prefix, force, completionsReadyCallback);
186     },
187
188     /**
189      * @param {string} expressionString
190      * @param {string} prefix
191      * @param {boolean} force
192      * @param {function(!Array.<string>, number=)} completionsReadyCallback
193      */
194     _completionsForExpression: function(expressionString, prefix, force, completionsReadyCallback)
195     {
196         var lastIndex = expressionString.length - 1;
197
198         var dotNotation = (expressionString[lastIndex] === ".");
199         var bracketNotation = (expressionString[lastIndex] === "[");
200
201         if (dotNotation || bracketNotation)
202             expressionString = expressionString.substr(0, lastIndex);
203
204         if (expressionString && parseInt(expressionString, 10) == expressionString) {
205             // User is entering float value, do not suggest anything.
206             completionsReadyCallback([]);
207             return;
208         }
209
210         if (!prefix && !expressionString && !force) {
211             completionsReadyCallback([]);
212             return;
213         }
214
215         if (!expressionString && WebInspector.debuggerModel.selectedCallFrame())
216             WebInspector.debuggerModel.getSelectedCallFrameVariables(receivedPropertyNames.bind(this));
217         else
218             this.evaluate(expressionString, "completion", true, true, false, false, evaluated.bind(this));
219
220         /**
221          * @this {WebInspector.RuntimeModel}
222          */
223         function evaluated(result, wasThrown)
224         {
225             if (!result || wasThrown) {
226                 completionsReadyCallback([]);
227                 return;
228             }
229
230             /**
231              * @param {string} primitiveType
232              * @this {WebInspector.RuntimeModel}
233              */
234             function getCompletions(primitiveType)
235             {
236                 var object;
237                 if (primitiveType === "string")
238                     object = new String("");
239                 else if (primitiveType === "number")
240                     object = new Number(0);
241                 else if (primitiveType === "boolean")
242                     object = new Boolean(false);
243                 else
244                     object = this;
245
246                 var resultSet = {};
247                 for (var o = object; o; o = o.__proto__) {
248                     try {
249                         var names = Object.getOwnPropertyNames(o);
250                         for (var i = 0; i < names.length; ++i)
251                             resultSet[names[i]] = true;
252                     } catch (e) {
253                     }
254                 }
255                 return resultSet;
256             }
257
258             if (result.type === "object" || result.type === "function")
259                 result.callFunctionJSON(getCompletions, undefined, receivedPropertyNames.bind(this));
260             else if (result.type === "string" || result.type === "number" || result.type === "boolean")
261                 this.evaluate("(" + getCompletions + ")(\"" + result.type + "\")", "completion", false, true, true, false, receivedPropertyNamesFromEval.bind(this));
262         }
263
264         /**
265          * @param {?WebInspector.RemoteObject} notRelevant
266          * @param {boolean} wasThrown
267          * @param {?RuntimeAgent.RemoteObject=} result
268          * @this {WebInspector.RuntimeModel}
269          */
270         function receivedPropertyNamesFromEval(notRelevant, wasThrown, result)
271         {
272             if (result && !wasThrown)
273                 receivedPropertyNames.call(this, result.value);
274             else
275                 completionsReadyCallback([]);
276         }
277
278         /**
279          * @this {WebInspector.RuntimeModel}
280          */
281         function receivedPropertyNames(propertyNames)
282         {
283             RuntimeAgent.releaseObjectGroup("completion");
284             if (!propertyNames) {
285                 completionsReadyCallback([]);
286                 return;
287             }
288             var includeCommandLineAPI = (!dotNotation && !bracketNotation);
289             if (includeCommandLineAPI) {
290                 const commandLineAPI = ["dir", "dirxml", "keys", "values", "profile", "profileEnd", "monitorEvents", "unmonitorEvents", "inspect", "copy", "clear",
291                     "getEventListeners", "debug", "undebug", "monitor", "unmonitor", "table", "$", "$$", "$x"];
292                 for (var i = 0; i < commandLineAPI.length; ++i)
293                     propertyNames[commandLineAPI[i]] = true;
294             }
295             this._reportCompletions(completionsReadyCallback, dotNotation, bracketNotation, expressionString, prefix, Object.keys(propertyNames));
296         }
297     },
298
299     /**
300      * @param {function(!Array.<string>, number=)} completionsReadyCallback
301      * @param {boolean} dotNotation
302      * @param {boolean} bracketNotation
303      * @param {string} expressionString
304      * @param {string} prefix
305      * @param {!Array.<string>} properties
306      */
307     _reportCompletions: function(completionsReadyCallback, dotNotation, bracketNotation, expressionString, prefix, properties) {
308         if (bracketNotation) {
309             if (prefix.length && prefix[0] === "'")
310                 var quoteUsed = "'";
311             else
312                 var quoteUsed = "\"";
313         }
314
315         var results = [];
316
317         if (!expressionString) {
318             const keywords = ["break", "case", "catch", "continue", "default", "delete", "do", "else", "finally", "for", "function", "if", "in",
319                               "instanceof", "new", "return", "switch", "this", "throw", "try", "typeof", "var", "void", "while", "with"];
320             properties = properties.concat(keywords);
321         }
322
323         properties.sort();
324
325         for (var i = 0; i < properties.length; ++i) {
326             var property = properties[i];
327
328             // Assume that all non-ASCII characters are letters and thus can be used as part of identifier.
329             if (dotNotation && !/^[a-zA-Z_$\u008F-\uFFFF][a-zA-Z0-9_$\u008F-\uFFFF]*$/.test(property))
330                 continue;
331
332             if (bracketNotation) {
333                 if (!/^[0-9]+$/.test(property))
334                     property = quoteUsed + property.escapeCharacters(quoteUsed + "\\") + quoteUsed;
335                 property += "]";
336             }
337
338             if (property.length < prefix.length)
339                 continue;
340             if (prefix.length && !property.startsWith(prefix))
341                 continue;
342
343             results.push(property);
344         }
345         completionsReadyCallback(results);
346     },
347
348     __proto__: WebInspector.Object.prototype
349 }
350
351 /**
352  * @type {!WebInspector.RuntimeModel}
353  */
354 WebInspector.runtimeModel;
355
356 /**
357  * @constructor
358  * @implements {RuntimeAgent.Dispatcher}
359  * @param {!WebInspector.RuntimeModel} runtimeModel
360  */
361 WebInspector.RuntimeDispatcher = function(runtimeModel)
362 {
363     this._runtimeModel = runtimeModel;
364 }
365
366 WebInspector.RuntimeDispatcher.prototype = {
367     executionContextCreated: function(context)
368     {
369         this._runtimeModel._executionContextCreated(context);
370     }
371 }
372
373 /**
374  * @constructor
375  */
376 WebInspector.ExecutionContext = function(id, name, isPageContext)
377 {
378     this.id = id;
379     this.name = (isPageContext && !name) ? "<page context>" : name;
380     this.isMainWorldContext = isPageContext;
381 }
382
383 /**
384  * @param {!WebInspector.ExecutionContext} a
385  * @param {!WebInspector.ExecutionContext} b
386  * @return {number}
387  */
388 WebInspector.ExecutionContext.comparator = function(a, b)
389 {
390     // Main world context should always go first.
391     if (a.isMainWorldContext)
392         return -1;
393     if (b.isMainWorldContext)
394         return +1;
395     return a.name.localeCompare(b.name);
396 }
397
398 /**
399  * @constructor
400  * @extends {WebInspector.Object}
401  * @param {!WebInspector.ResourceTreeFrame} frame
402  */
403 WebInspector.FrameExecutionContextList = function(frame)
404 {
405     this._frame = frame;
406     this._executionContexts = [];
407 }
408
409 WebInspector.FrameExecutionContextList.EventTypes = {
410     ContextsUpdated: "ContextsUpdated",
411     ContextAdded: "ContextAdded"
412 }
413
414 WebInspector.FrameExecutionContextList.prototype =
415 {
416     /**
417      * @param {!WebInspector.ResourceTreeFrame} frame
418      */
419     _frameNavigated: function(frame)
420     {
421         this._frame = frame;
422         this._executionContexts = [];
423         this.dispatchEventToListeners(WebInspector.FrameExecutionContextList.EventTypes.ContextsUpdated, this);
424     },
425
426     /**
427      * @param {!WebInspector.ExecutionContext} context
428      */
429     _addExecutionContext: function(context)
430     {
431         var insertAt = insertionIndexForObjectInListSortedByFunction(context, this._executionContexts, WebInspector.ExecutionContext.comparator);
432         this._executionContexts.splice(insertAt, 0, context);
433         this.dispatchEventToListeners(WebInspector.FrameExecutionContextList.EventTypes.ContextAdded, this);
434     },
435
436     /**
437      * @return {!Array.<!WebInspector.ExecutionContext>}
438      */
439     executionContexts: function()
440     {
441         return this._executionContexts;
442     },
443
444     /**
445      * @return {!WebInspector.ExecutionContext}
446      */
447     mainWorldContext: function() 
448     {
449         return this._executionContexts[0];
450     },
451
452     /**
453      * @param {string} securityOrigin
454      * @return {?WebInspector.ExecutionContext}
455      */
456     contextBySecurityOrigin: function(securityOrigin)
457     {
458         for (var i = 0; i < this._executionContexts.length; ++i) {
459             var context = this._executionContexts[i];
460             if (!context.isMainWorldContext && context.name === securityOrigin)
461                 return context; 
462         }
463         return null;
464     },
465
466     /**
467      * @return {string}
468      */
469     get frameId()
470     {
471         return this._frame.id;
472     },
473
474     /**
475      * @return {string}
476      */
477     get url()
478     {
479         return this._frame.url;
480     },
481
482     /**
483      * @return {string}
484      */
485     get displayName()
486     {
487         return this._frame.displayName();
488     },
489
490     __proto__: WebInspector.Object.prototype
491 }