Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / devtools / front_end / ResourceScriptMapping.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  * @implements {WebInspector.ScriptSourceMapping}
34  * @param {!WebInspector.DebuggerModel} debuggerModel
35  * @param {!WebInspector.Workspace} workspace
36  */
37 WebInspector.ResourceScriptMapping = function(debuggerModel, workspace)
38 {
39     this._debuggerModel = debuggerModel;
40     this._workspace = workspace;
41     this._workspace.addEventListener(WebInspector.Workspace.Events.UISourceCodeAdded, this._uiSourceCodeAddedToWorkspace, this);
42
43     debuggerModel.addEventListener(WebInspector.DebuggerModel.Events.GlobalObjectCleared, this._debuggerReset, this);
44     this._initialize();
45 }
46
47 WebInspector.ResourceScriptMapping.prototype = {
48     /**
49      * @param {!WebInspector.RawLocation} rawLocation
50      * @return {?WebInspector.UILocation}
51      */
52     rawLocationToUILocation: function(rawLocation)
53     {
54         var debuggerModelLocation = /** @type {!WebInspector.DebuggerModel.Location} */ (rawLocation);
55         var script = this._debuggerModel.scriptForId(debuggerModelLocation.scriptId);
56         var uiSourceCode = this._workspaceUISourceCodeForScript(script);
57         if (!uiSourceCode)
58             return null;
59         var scriptFile = uiSourceCode.scriptFile();
60         if (scriptFile && ((scriptFile.hasDivergedFromVM() && !scriptFile.isMergingToVM()) || scriptFile.isDivergingFromVM()))
61             return null;
62         return new WebInspector.UILocation(uiSourceCode, debuggerModelLocation.lineNumber, debuggerModelLocation.columnNumber || 0);
63     },
64
65     /**
66      * @param {!WebInspector.UISourceCode} uiSourceCode
67      * @param {number} lineNumber
68      * @param {number} columnNumber
69      * @return {?WebInspector.DebuggerModel.Location}
70      */
71     uiLocationToRawLocation: function(uiSourceCode, lineNumber, columnNumber)
72     {
73         var scripts = this._scriptsForUISourceCode(uiSourceCode);
74         console.assert(scripts.length);
75         return this._debuggerModel.createRawLocation(scripts[0], lineNumber, columnNumber);
76     },
77
78     /**
79      * @param {!WebInspector.Script} script
80      */
81     addScript: function(script)
82     {
83         if (script.isAnonymousScript())
84             return;
85         script.pushSourceMapping(this);
86
87         var scriptsForSourceURL = script.isInlineScript() ? this._inlineScriptsForSourceURL : this._nonInlineScriptsForSourceURL;
88         scriptsForSourceURL.put(script.sourceURL, scriptsForSourceURL.get(script.sourceURL) || []);
89         scriptsForSourceURL.get(script.sourceURL).push(script);
90
91         var uiSourceCode = this._workspaceUISourceCodeForScript(script);
92         if (!uiSourceCode)
93             return;
94
95         this._bindUISourceCodeToScripts(uiSourceCode, [script]);
96     },
97
98     _uiSourceCodeAddedToWorkspace: function(event)
99     {
100         var uiSourceCode = /** @type {!WebInspector.UISourceCode} */ (event.data);
101         if (!uiSourceCode.url)
102             return;
103
104         var scripts = this._scriptsForUISourceCode(uiSourceCode);
105         if (!scripts.length)
106             return;
107
108         this._bindUISourceCodeToScripts(uiSourceCode, scripts);
109     },
110
111     /**
112      * @param {!WebInspector.UISourceCode} uiSourceCode
113      */
114     _hasMergedToVM: function(uiSourceCode)
115     {
116         var scripts = this._scriptsForUISourceCode(uiSourceCode);
117         if (!scripts.length)
118             return;
119         for (var i = 0; i < scripts.length; ++i)
120             scripts[i].updateLocations();
121     },
122
123     /**
124      * @param {!WebInspector.UISourceCode} uiSourceCode
125      */
126     _hasDivergedFromVM: function(uiSourceCode)
127     {
128         var scripts = this._scriptsForUISourceCode(uiSourceCode);
129         if (!scripts.length)
130             return;
131         for (var i = 0; i < scripts.length; ++i)
132             scripts[i].updateLocations();
133     },
134
135     /**
136      * @param {!WebInspector.Script} script
137      * @return {?WebInspector.UISourceCode}
138      */
139     _workspaceUISourceCodeForScript: function(script)
140     {
141         if (script.isAnonymousScript())
142             return null;
143         return this._workspace.uiSourceCodeForURL(script.sourceURL);
144     },
145
146     /**
147      * @param {!WebInspector.UISourceCode} uiSourceCode
148      * @return {!Array.<!WebInspector.Script>}
149      */
150     _scriptsForUISourceCode: function(uiSourceCode)
151     {
152         var isInlineScript;
153         switch (uiSourceCode.contentType()) {
154         case WebInspector.resourceTypes.Document:
155             isInlineScript = true;
156             break;
157         case WebInspector.resourceTypes.Script:
158             isInlineScript = false;
159             break;
160         default:
161             return [];
162         }
163         if (!uiSourceCode.url)
164             return [];
165         var scriptsForSourceURL = isInlineScript ? this._inlineScriptsForSourceURL : this._nonInlineScriptsForSourceURL;
166         return scriptsForSourceURL.get(uiSourceCode.url) || [];
167     },
168
169     /**
170      * @param {!WebInspector.UISourceCode} uiSourceCode
171      * @param {!Array.<!WebInspector.Script>} scripts
172      */
173     _bindUISourceCodeToScripts: function(uiSourceCode, scripts)
174     {
175         console.assert(scripts.length);
176         var scriptFile = new WebInspector.ResourceScriptFile(this, uiSourceCode, scripts);
177         uiSourceCode.setScriptFile(scriptFile);
178         for (var i = 0; i < scripts.length; ++i)
179             scripts[i].updateLocations();
180         uiSourceCode.setSourceMapping(this);
181     },
182
183     /**
184      * @param {!WebInspector.UISourceCode} uiSourceCode
185      * @param {!Array.<!WebInspector.Script>} scripts
186      */
187     _unbindUISourceCodeFromScripts: function(uiSourceCode, scripts)
188     {
189         console.assert(scripts.length);
190         var scriptFile = /** @type {!WebInspector.ResourceScriptFile} */ (uiSourceCode.scriptFile());
191         if (scriptFile) {
192             scriptFile.dispose();
193             uiSourceCode.setScriptFile(null);
194         }
195         uiSourceCode.setSourceMapping(null);
196     },
197
198     _initialize: function()
199     {
200         /** @type {!StringMap.<!Array.<!WebInspector.Script>>} */
201         this._inlineScriptsForSourceURL = new StringMap();
202         /** @type {!StringMap.<!Array.<!WebInspector.Script>>} */
203         this._nonInlineScriptsForSourceURL = new StringMap();
204     },
205
206     _debuggerReset: function()
207     {
208         /**
209          * @param {!Array.<!WebInspector.Script>} scripts
210          * @this {WebInspector.ResourceScriptMapping}
211          */
212         function unbindUISourceCodesForScripts(scripts)
213         {
214             if (!scripts.length)
215                 return;
216             var uiSourceCode = this._workspaceUISourceCodeForScript(scripts[0]);
217             if (!uiSourceCode)
218                 return;
219             this._unbindUISourceCodeFromScripts(uiSourceCode, scripts);
220         }
221
222         this._inlineScriptsForSourceURL.values().forEach(unbindUISourceCodesForScripts.bind(this));
223         this._nonInlineScriptsForSourceURL.values().forEach(unbindUISourceCodesForScripts.bind(this));
224         this._initialize();
225     },
226 }
227
228 /**
229  * @interface
230  * @extends {WebInspector.EventTarget}
231  */
232 WebInspector.ScriptFile = function()
233 {
234 }
235
236 WebInspector.ScriptFile.Events = {
237     DidMergeToVM: "DidMergeToVM",
238     DidDivergeFromVM: "DidDivergeFromVM",
239 }
240
241 WebInspector.ScriptFile.prototype = {
242     /**
243      * @return {boolean}
244      */
245     hasDivergedFromVM: function() { return false; },
246
247     /**
248      * @return {boolean}
249      */
250     isDivergingFromVM: function() { return false; },
251
252     /**
253      * @return {boolean}
254      */
255     isMergingToVM: function() { return false; },
256
257     checkMapping: function() { },
258 }
259
260 /**
261  * @constructor
262  * @implements {WebInspector.ScriptFile}
263  * @extends {WebInspector.Object}
264  * @param {!WebInspector.ResourceScriptMapping} resourceScriptMapping
265  * @param {!WebInspector.UISourceCode} uiSourceCode
266  */
267 WebInspector.ResourceScriptFile = function(resourceScriptMapping, uiSourceCode, scripts)
268 {
269     console.assert(scripts.length);
270
271     WebInspector.ScriptFile.call(this);
272     this._resourceScriptMapping = resourceScriptMapping;
273     this._uiSourceCode = uiSourceCode;
274
275     if (this._uiSourceCode.contentType() === WebInspector.resourceTypes.Script)
276         this._script = scripts[0];
277
278     this._uiSourceCode.addEventListener(WebInspector.UISourceCode.Events.WorkingCopyCommitted, this._workingCopyCommitted, this);
279     this._uiSourceCode.addEventListener(WebInspector.UISourceCode.Events.WorkingCopyChanged, this._workingCopyChanged, this);
280     this._update();
281 }
282
283 WebInspector.ResourceScriptFile.prototype = {
284     _workingCopyCommitted: function(event)
285     {
286         /**
287          * @param {?string} error
288          * @param {!DebuggerAgent.SetScriptSourceError=} errorData
289          * @this {WebInspector.ResourceScriptFile}
290          */
291         function innerCallback(error, errorData)
292         {
293             if (error) {
294                 this._update();
295                 WebInspector.LiveEditSupport.logDetailedError(error, errorData, this._script);
296                 return;
297             }
298
299             this._scriptSource = source;
300             this._update();
301             WebInspector.LiveEditSupport.logSuccess();
302         }
303         if (!this._script)
304             return;
305         var source = this._uiSourceCode.workingCopy();
306         this._resourceScriptMapping._debuggerModel.setScriptSource(this._script.scriptId, source, innerCallback.bind(this));
307     },
308
309     /**
310      * @return {boolean}
311      */
312     _isDiverged: function()
313     {
314         if (this._uiSourceCode.formatted())
315             return false;
316         if (this._uiSourceCode.isDirty())
317             return true;
318         if (!this._script)
319             return false;
320         if (typeof this._scriptSource === "undefined")
321             return false;
322         return this._uiSourceCode.workingCopy() !== this._scriptSource;
323     },
324
325     /**
326      * @param {!WebInspector.Event} event
327      */
328     _workingCopyChanged: function(event)
329     {
330         this._update();
331     },
332
333     _update: function()
334     {
335         if (this._isDiverged() && !this._hasDivergedFromVM)
336             this._divergeFromVM();
337         else if (!this._isDiverged() && this._hasDivergedFromVM)
338             this._mergeToVM();
339     },
340
341     _divergeFromVM: function()
342     {
343         this._isDivergingFromVM = true;
344         this._resourceScriptMapping._hasDivergedFromVM(this._uiSourceCode);
345         delete this._isDivergingFromVM;
346         this._hasDivergedFromVM = true;
347         this.dispatchEventToListeners(WebInspector.ScriptFile.Events.DidDivergeFromVM, this._uiSourceCode);
348     },
349
350     _mergeToVM: function()
351     {
352         delete this._hasDivergedFromVM;
353         this._isMergingToVM = true;
354         this._resourceScriptMapping._hasMergedToVM(this._uiSourceCode);
355         delete this._isMergingToVM;
356         this.dispatchEventToListeners(WebInspector.ScriptFile.Events.DidMergeToVM, this._uiSourceCode);
357     },
358
359     /**
360      * @return {boolean}
361      */
362     hasDivergedFromVM: function()
363     {
364         return this._hasDivergedFromVM;
365     },
366
367     /**
368      * @return {boolean}
369      */
370     isDivergingFromVM: function()
371     {
372         return this._isDivergingFromVM;
373     },
374
375     /**
376      * @return {boolean}
377      */
378     isMergingToVM: function()
379     {
380         return this._isMergingToVM;
381     },
382
383     checkMapping: function()
384     {
385         if (!this._script)
386             return;
387         if (typeof this._scriptSource !== "undefined")
388             return;
389         this._script.requestContent(callback.bind(this));
390
391         /**
392          * @param {?string} source
393          * @this {WebInspector.ResourceScriptFile}
394          */
395         function callback(source)
396         {
397             this._scriptSource = source;
398             this._update();
399         }
400     },
401
402     dispose: function()
403     {
404         this._uiSourceCode.removeEventListener(WebInspector.UISourceCode.Events.WorkingCopyCommitted, this._workingCopyCommitted, this);
405         this._uiSourceCode.removeEventListener(WebInspector.UISourceCode.Events.WorkingCopyChanged, this._workingCopyChanged, this);
406     },
407
408     __proto__: WebInspector.Object.prototype
409 }