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