Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / devtools / front_end / sdk / 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.TargetAwareObject}
34  * @param {!WebInspector.Target} target
35  */
36 WebInspector.RuntimeModel = function(target)
37 {
38     WebInspector.TargetAwareObject.call(this, target);
39
40     this._debuggerModel = target.debuggerModel;
41     this._agent = target.runtimeAgent();
42     this.target().registerRuntimeDispatcher(new WebInspector.RuntimeDispatcher(this));
43     this._agent.enable();
44     /**
45      * @type {!Object.<number, !WebInspector.ExecutionContext>}
46      */
47     this._executionContextById = {};
48 }
49
50 WebInspector.RuntimeModel.Events = {
51     ExecutionContextCreated: "ExecutionContextCreated",
52     ExecutionContextDestroyed: "ExecutionContextDestroyed",
53 }
54
55 WebInspector.RuntimeModel.prototype = {
56
57     /**
58      * @return {!Array.<!WebInspector.ExecutionContext>}
59      */
60     executionContexts: function()
61     {
62         return Object.values(this._executionContextById);
63     },
64
65     /**
66      * @param {!RuntimeAgent.ExecutionContextDescription} context
67      */
68     _executionContextCreated: function(context)
69     {
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);
73     },
74
75     /**
76      * @param {number} executionContextId
77      */
78     _executionContextDestroyed: function(executionContextId)
79     {
80         var executionContext = this._executionContextById[executionContextId];
81         delete this._executionContextById[executionContextId];
82         this.dispatchEventToListeners(WebInspector.RuntimeModel.Events.ExecutionContextDestroyed, executionContext);
83     },
84
85     _executionContextsCleared: function()
86     {
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]);
91     },
92
93     /**
94      * @param {!RuntimeAgent.RemoteObject} payload
95      * @return {!WebInspector.RemoteObject}
96      */
97     createRemoteObject: function(payload)
98     {
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);
101     },
102
103     /**
104      * @param {!RuntimeAgent.RemoteObject} payload
105      * @param {!WebInspector.ScopeRef} scopeRef
106      * @return {!WebInspector.RemoteObject}
107      */
108     createScopeRemoteObject: function(payload, scopeRef)
109     {
110         return new WebInspector.ScopeRemoteObject(this.target(), payload.objectId, scopeRef, payload.type, payload.subtype, payload.value, payload.description, payload.preview);
111     },
112
113     /**
114      * @param {number|string|boolean} value
115      * @return {!WebInspector.RemoteObject}
116      */
117     createRemoteObjectFromPrimitiveValue: function(value)
118     {
119         return new WebInspector.RemoteObjectImpl(this.target(), undefined, typeof value, undefined, value);
120     },
121
122     /**
123      * @param {string} name
124      * @param {number|string|boolean} value
125      * @return {!WebInspector.RemoteObjectProperty}
126      */
127     createRemotePropertyFromPrimitiveValue: function(name, value)
128     {
129         return new WebInspector.RemoteObjectProperty(name, this.createRemoteObjectFromPrimitiveValue(value));
130     },
131
132     __proto__: WebInspector.TargetAwareObject.prototype
133 }
134
135 /**
136  * @constructor
137  * @implements {RuntimeAgent.Dispatcher}
138  * @param {!WebInspector.RuntimeModel} runtimeModel
139  */
140 WebInspector.RuntimeDispatcher = function(runtimeModel)
141 {
142     this._runtimeModel = runtimeModel;
143 }
144
145 WebInspector.RuntimeDispatcher.prototype = {
146     executionContextCreated: function(context)
147     {
148         this._runtimeModel._executionContextCreated(context);
149     },
150
151     executionContextDestroyed: function(executionContextId)
152     {
153         this._runtimeModel._executionContextDestroyed(executionContextId);
154     },
155
156     executionContextsCleared: function()
157     {
158         this._runtimeModel._executionContextsCleared();
159     }
160
161 }
162
163 /**
164  * @constructor
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
171  */
172 WebInspector.ExecutionContext = function(target, id, name, isPageContext, frameId)
173 {
174     WebInspector.TargetAware.call(this, target);
175     this.id = id;
176     this.name = (isPageContext && !name) ? "<page context>" : name;
177     this.isMainWorldContext = isPageContext;
178     this._debuggerModel = target.debuggerModel;
179     this.frameId = frameId;
180 }
181
182 /**
183  * @param {!WebInspector.ExecutionContext} a
184  * @param {!WebInspector.ExecutionContext} b
185  * @return {number}
186  */
187 WebInspector.ExecutionContext.comparator = function(a, b)
188 {
189     // Main world context should always go first.
190     if (a.isMainWorldContext)
191         return -1;
192     if (b.isMainWorldContext)
193         return +1;
194     return a.name.localeCompare(b.name);
195 }
196
197 WebInspector.ExecutionContext.prototype = {
198
199     /**
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
207      */
208     evaluate: function(expression, objectGroup, includeCommandLineAPI, doNotPauseOnExceptionsAndMuteConsole, returnByValue, generatePreview, callback)
209     {
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);
213             return;
214         }
215
216         if (!expression) {
217             // There is no expression, so the completion should happen against global properties.
218             expression = "this";
219         }
220
221         /**
222          * @this {WebInspector.ExecutionContext}
223          * @param {?Protocol.Error} error
224          * @param {!RuntimeAgent.RemoteObject} result
225          * @param {boolean=} wasThrown
226          */
227         function evalCallback(error, result, wasThrown)
228         {
229             if (error) {
230                 callback(null, false);
231                 return;
232             }
233
234             if (returnByValue)
235                 callback(null, !!wasThrown, wasThrown ? null : result);
236             else
237                 callback(this.target().runtimeModel.createRemoteObject(result), !!wasThrown);
238         }
239         this.target().runtimeAgent().evaluate(expression, objectGroup, includeCommandLineAPI, doNotPauseOnExceptionsAndMuteConsole, this.id, returnByValue, generatePreview, evalCallback.bind(this));
240     },
241
242     /**
243      * @param {string} expressionString
244      * @param {string} prefix
245      * @param {boolean} force
246      * @param {function(!Array.<string>, number=)} completionsReadyCallback
247      */
248     completionsForExpression: function(expressionString, prefix, force, completionsReadyCallback)
249     {
250         var lastIndex = expressionString.length - 1;
251
252         var dotNotation = (expressionString[lastIndex] === ".");
253         var bracketNotation = (expressionString[lastIndex] === "[");
254
255         if (dotNotation || bracketNotation)
256             expressionString = expressionString.substr(0, lastIndex);
257
258         if (expressionString && parseInt(expressionString, 10) == expressionString) {
259             // User is entering float value, do not suggest anything.
260             completionsReadyCallback([]);
261             return;
262         }
263
264         if (!prefix && !expressionString && !force) {
265             completionsReadyCallback([]);
266             return;
267         }
268
269         if (!expressionString && this._debuggerModel.selectedCallFrame())
270             this._debuggerModel.getSelectedCallFrameVariables(receivedPropertyNames.bind(this));
271         else
272             this.evaluate(expressionString, "completion", true, true, false, false, evaluated.bind(this));
273
274         /**
275          * @this {WebInspector.ExecutionContext}
276          */
277         function evaluated(result, wasThrown)
278         {
279             if (!result || wasThrown) {
280                 completionsReadyCallback([]);
281                 return;
282             }
283
284             /**
285              * @param {string} primitiveType
286              * @suppressReceiverCheck
287              * @this {WebInspector.ExecutionContext}
288              */
289             function getCompletions(primitiveType)
290             {
291                 var object;
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);
298                 else
299                     object = this;
300
301                 var resultSet = {};
302                 for (var o = object; o; o = o.__proto__) {
303                     try {
304                         var names = Object.getOwnPropertyNames(o);
305                         for (var i = 0; i < names.length; ++i)
306                             resultSet[names[i]] = true;
307                     } catch (e) {
308                     }
309                 }
310                 return resultSet;
311             }
312
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));
317         }
318
319         /**
320          * @param {?WebInspector.RemoteObject} notRelevant
321          * @param {boolean} wasThrown
322          * @param {?RuntimeAgent.RemoteObject=} result
323          * @this {WebInspector.ExecutionContext}
324          */
325         function receivedPropertyNamesFromEval(notRelevant, wasThrown, result)
326         {
327             if (result && !wasThrown)
328                 receivedPropertyNames.call(this, result.value);
329             else
330                 completionsReadyCallback([]);
331         }
332
333         /**
334          * @this {WebInspector.ExecutionContext}
335          */
336         function receivedPropertyNames(propertyNames)
337         {
338             this.target().runtimeAgent().releaseObjectGroup("completion");
339             if (!propertyNames) {
340                 completionsReadyCallback([]);
341                 return;
342             }
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;
349             }
350             this._reportCompletions(completionsReadyCallback, dotNotation, bracketNotation, expressionString, prefix, Object.keys(propertyNames));
351         }
352     },
353
354     /**
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
361      */
362     _reportCompletions: function(completionsReadyCallback, dotNotation, bracketNotation, expressionString, prefix, properties) {
363         if (bracketNotation) {
364             if (prefix.length && prefix[0] === "'")
365                 var quoteUsed = "'";
366             else
367                 var quoteUsed = "\"";
368         }
369
370         var results = [];
371
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);
376         }
377
378         properties.sort();
379
380         for (var i = 0; i < properties.length; ++i) {
381             var property = properties[i];
382
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))
385                 continue;
386
387             if (bracketNotation) {
388                 if (!/^[0-9]+$/.test(property))
389                     property = quoteUsed + property.escapeCharacters(quoteUsed + "\\") + quoteUsed;
390                 property += "]";
391             }
392
393             if (property.length < prefix.length)
394                 continue;
395             if (prefix.length && !property.startsWith(prefix))
396                 continue;
397
398             results.push(property);
399         }
400         completionsReadyCallback(results);
401     },
402
403     __proto__: WebInspector.TargetAware.prototype
404 }
405
406 /**
407  * @type {!WebInspector.RuntimeModel}
408  */
409 WebInspector.runtimeModel;