Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / devtools / front_end / bindings / CSSWorkspaceBinding.js
1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 /**
6  * @constructor
7  * @implements {WebInspector.TargetManager.Observer}
8  */
9 WebInspector.CSSWorkspaceBinding = function()
10 {
11     /** @type {!Map.<!WebInspector.Target, !WebInspector.CSSWorkspaceBinding.TargetInfo>} */
12     this._targetToTargetInfo = new Map();
13     WebInspector.targetManager.observeTargets(this);
14
15     WebInspector.targetManager.addModelListener(WebInspector.ResourceTreeModel, WebInspector.ResourceTreeModel.EventTypes.MainFrameNavigated, this._mainFrameCreatedOrNavigated, this);
16 }
17
18 WebInspector.CSSWorkspaceBinding.prototype = {
19     /**
20      * @param {!WebInspector.Target} target
21      */
22     targetAdded: function(target)
23     {
24         this._targetToTargetInfo.put(target, new WebInspector.CSSWorkspaceBinding.TargetInfo(target, WebInspector.workspace, WebInspector.networkWorkspaceBinding));
25     },
26
27     /**
28      * @param {!WebInspector.Target} target
29      */
30     targetRemoved: function(target)
31     {
32         this._targetToTargetInfo.remove(target)._dispose();
33     },
34
35     /**
36      * @param {!WebInspector.CSSStyleSheetHeader} header
37      * @param {!WebInspector.CSSSourceMapping} mapping
38      */
39     pushSourceMapping: function(header, mapping)
40     {
41         this._ensureInfoForHeader(header)._pushSourceMapping(mapping);
42     },
43
44     /**
45      * @param {!WebInspector.CSSStyleSheetHeader} header
46      * @return {?WebInspector.CSSWorkspaceBinding.HeaderInfo}
47      */
48     _headerInfo: function(header)
49     {
50         var map = this._targetToTargetInfo.get(header.target());
51         return map._headerInfo(header.id) || null;
52     },
53
54     /**
55      * @param {!WebInspector.CSSStyleSheetHeader} header
56      * @return {!WebInspector.CSSWorkspaceBinding.HeaderInfo}
57      */
58     _ensureInfoForHeader: function(header)
59     {
60         var targetInfo = this._targetToTargetInfo.get(header.target());
61         if (!targetInfo) {
62             targetInfo = new WebInspector.CSSWorkspaceBinding.TargetInfo(header.target(), WebInspector.workspace, WebInspector.networkWorkspaceBinding);
63             this._targetToTargetInfo.put(header.target(), targetInfo);
64         }
65         return targetInfo._ensureInfoForHeader(header);
66     },
67
68     /**
69      * @param {!WebInspector.Event} event
70      */
71     _mainFrameCreatedOrNavigated: function(event)
72     {
73         var target = /** @type {!WebInspector.ResourceTreeModel} */ (event.target).target();
74         this._targetToTargetInfo.get(target)._reset();
75     },
76
77     /**
78      * @param {!WebInspector.CSSStyleSheetHeader} header
79      */
80     updateLocations: function(header)
81     {
82         var info = this._headerInfo(header);
83         if (info)
84             info._updateLocations();
85     },
86
87     /**
88      * @param {!WebInspector.CSSLocation} rawLocation
89      * @param {function(!WebInspector.UILocation):(boolean|undefined)} updateDelegate
90      * @return {!WebInspector.CSSWorkspaceBinding.LiveLocation}
91      */
92     createLiveLocation: function(rawLocation, updateDelegate)
93     {
94         var header = rawLocation.styleSheetId ? rawLocation.target().cssModel.styleSheetHeaderForId(rawLocation.styleSheetId) : null;
95         return new WebInspector.CSSWorkspaceBinding.LiveLocation(rawLocation.target().cssModel, header, rawLocation, updateDelegate);
96     },
97
98     /**
99      * @param {!WebInspector.CSSWorkspaceBinding.LiveLocation} location
100      */
101     _addLiveLocation: function(location)
102     {
103         this._ensureInfoForHeader(location._header)._addLocation(location);
104     },
105
106     /**
107      * @param {!WebInspector.CSSWorkspaceBinding.LiveLocation} location
108      */
109     _removeLiveLocation: function(location)
110     {
111         var info = this._headerInfo(location._header);
112         if (info)
113             info._removeLocation(location);
114     },
115
116     /**
117      * @param {!WebInspector.CSSProperty} cssProperty
118      * @param {boolean} forName
119      * @return {?WebInspector.UILocation}
120      */
121     propertyUILocation: function(cssProperty, forName)
122     {
123         var style = cssProperty.ownerStyle;
124         if (!style || !style.parentRule || !style.styleSheetId)
125             return null;
126
127         var range = cssProperty.range;
128         if (!range)
129             return null;
130
131         var url = style.parentRule.resourceURL();
132         if (!url)
133             return null;
134
135         var line = forName ? range.startLine : range.endLine;
136         // End of range is exclusive, so subtract 1 from the end offset.
137         var column = forName ? range.startColumn : range.endColumn - (cssProperty.text && cssProperty.text.endsWith(";") ? 2 : 1);
138         var rawLocation = new WebInspector.CSSLocation(style.target(), style.styleSheetId, url, line, column);
139         return this.rawLocationToUILocation(rawLocation);
140     },
141
142     /**
143      * @param {?WebInspector.CSSLocation} rawLocation
144      * @return {?WebInspector.UILocation}
145      */
146     rawLocationToUILocation: function(rawLocation)
147     {
148         if (!rawLocation)
149             return null;
150         var cssModel = rawLocation.target().cssModel;
151         var frameIdToSheetIds = cssModel.styleSheetIdsByFrameIdForURL(rawLocation.url);
152         if (!Object.values(frameIdToSheetIds).length)
153             return null;
154         var styleSheetIds = [];
155         for (var frameId in frameIdToSheetIds)
156             styleSheetIds = styleSheetIds.concat(frameIdToSheetIds[frameId]);
157         var uiLocation;
158         for (var i = 0; !uiLocation && i < styleSheetIds.length; ++i) {
159             var header = cssModel.styleSheetHeaderForId(styleSheetIds[i]);
160             if (!header)
161                 continue;
162             var info = this._headerInfo(header);
163             if (info)
164                 uiLocation = info._rawLocationToUILocation(rawLocation.lineNumber, rawLocation.columnNumber);
165         }
166         return uiLocation || null;
167     }
168 }
169
170 /**
171  * @constructor
172  * @param {!WebInspector.Target} target
173  * @param {!WebInspector.Workspace} workspace
174  * @param {!WebInspector.NetworkWorkspaceBinding} networkWorkspaceBinding
175  */
176 WebInspector.CSSWorkspaceBinding.TargetInfo = function(target, workspace, networkWorkspaceBinding)
177 {
178     this._target = target;
179     this._workspace = workspace;
180
181     var cssModel = target.cssModel;
182     this._stylesSourceMapping = new WebInspector.StylesSourceMapping(cssModel, workspace);
183     this._sassSourceMapping = new WebInspector.SASSSourceMapping(cssModel, workspace, networkWorkspaceBinding);
184
185     /** @type {!StringMap.<!WebInspector.CSSWorkspaceBinding.HeaderInfo>} */
186     this._headerInfoById = new StringMap();
187
188     cssModel.addEventListener(WebInspector.CSSStyleModel.Events.StyleSheetAdded, this._styleSheetAdded, this);
189     cssModel.addEventListener(WebInspector.CSSStyleModel.Events.StyleSheetRemoved, this._styleSheetRemoved, this);
190 }
191
192 WebInspector.CSSWorkspaceBinding.TargetInfo.prototype = {
193     /**
194      * @param {!WebInspector.Event} event
195      */
196     _styleSheetAdded: function(event)
197     {
198         var header = /** @type {!WebInspector.CSSStyleSheetHeader} */ (event.data);
199         this._stylesSourceMapping.addHeader(header);
200         this._sassSourceMapping.addHeader(header);
201     },
202
203     /**
204      * @param {!WebInspector.Event} event
205      */
206     _styleSheetRemoved: function(event)
207     {
208         var header = /** @type {!WebInspector.CSSStyleSheetHeader} */ (event.data);
209         this._stylesSourceMapping.removeHeader(header);
210         this._sassSourceMapping.removeHeader(header);
211         this._headerInfoById.remove(header.id);
212     },
213
214     /**
215      * @param {!CSSAgent.StyleSheetId} id
216      */
217     _headerInfo: function(id)
218     {
219         return this._headerInfoById.get(id);
220     },
221
222     _ensureInfoForHeader: function(header)
223     {
224         var info = this._headerInfoById.get(header.id);
225         if (!info) {
226             info = new WebInspector.CSSWorkspaceBinding.HeaderInfo(header);
227             this._headerInfoById.put(header.id, info);
228         }
229         return info;
230     },
231
232     _dispose: function()
233     {
234         this._reset();
235         this._target.cssModel.removeEventListener(WebInspector.CSSStyleModel.Events.StyleSheetAdded, this._styleSheetAdded, this);
236         this._target.cssModel.removeEventListener(WebInspector.CSSStyleModel.Events.StyleSheetRemoved, this._styleSheetRemoved, this);
237     },
238
239     _reset: function()
240     {
241         this._headerInfoById.clear();
242     }
243 }
244
245 /**
246  * @constructor
247  * @param {!WebInspector.CSSStyleSheetHeader} header
248  */
249 WebInspector.CSSWorkspaceBinding.HeaderInfo = function(header)
250 {
251     this._header = header;
252
253     /** @type {!Array.<!WebInspector.CSSSourceMapping>} */
254     this._sourceMappings = [];
255
256     /** @type {!Set.<!WebInspector.LiveLocation>} */
257     this._locations = new Set();
258 }
259
260 WebInspector.CSSWorkspaceBinding.HeaderInfo.prototype = {
261     /**
262      * @param {!WebInspector.LiveLocation} location
263      */
264     _addLocation: function(location)
265     {
266         this._locations.add(location);
267         location.update();
268     },
269
270     /**
271      * @param {!WebInspector.LiveLocation} location
272      */
273     _removeLocation: function(location)
274     {
275         this._locations.remove(location);
276     },
277
278     _updateLocations: function()
279     {
280         var items = this._locations.values();
281         for (var i = 0; i < items.length; ++i)
282             items[i].update();
283     },
284
285     /**
286      * @param {number} lineNumber
287      * @param {number=} columnNumber
288      * @return {?WebInspector.UILocation}
289      */
290     _rawLocationToUILocation: function(lineNumber, columnNumber)
291     {
292         var uiLocation = null;
293         var rawLocation = new WebInspector.CSSLocation(this._header.target(), this._header.id, this._header.resourceURL(), lineNumber, columnNumber);
294         for (var i = this._sourceMappings.length - 1; !uiLocation && i >= 0; --i)
295             uiLocation = this._sourceMappings[i].rawLocationToUILocation(rawLocation);
296         return uiLocation;
297     },
298
299     /**
300      * @param {!WebInspector.CSSSourceMapping} sourceMapping
301      */
302     _pushSourceMapping: function(sourceMapping)
303     {
304         this._sourceMappings.push(sourceMapping);
305         this._updateLocations();
306     }
307 }
308
309 /**
310  * @constructor
311  * @extends {WebInspector.LiveLocation}
312  * @param {!WebInspector.CSSStyleModel} cssModel
313  * @param {?WebInspector.CSSStyleSheetHeader} header
314  * @param {!WebInspector.CSSLocation} rawLocation
315  * @param {function(!WebInspector.UILocation):(boolean|undefined)} updateDelegate
316  */
317 WebInspector.CSSWorkspaceBinding.LiveLocation = function(cssModel, header, rawLocation, updateDelegate)
318 {
319     WebInspector.LiveLocation.call(this, updateDelegate);
320     this._cssModel = cssModel;
321     this._rawLocation = rawLocation;
322     if (!header)
323         this._clearStyleSheet();
324     else
325         this._setStyleSheet(header);
326 }
327
328 WebInspector.CSSWorkspaceBinding.LiveLocation.prototype = {
329     /**
330      * @param {!WebInspector.Event} event
331      */
332     _styleSheetAdded: function(event)
333     {
334         console.assert(!this._header);
335         var header = /** @type {!WebInspector.CSSStyleSheetHeader} */ (event.data);
336         if (header.sourceURL && header.sourceURL === this._rawLocation.url)
337             this._setStyleSheet(header);
338     },
339
340     /**
341      * @param {!WebInspector.Event} event
342      */
343     _styleSheetRemoved: function(event)
344     {
345         console.assert(this._header);
346         var header = /** @type {!WebInspector.CSSStyleSheetHeader} */ (event.data);
347         if (this._header !== header)
348             return;
349         WebInspector.cssWorkspaceBinding._removeLiveLocation(this);
350         this._clearStyleSheet();
351     },
352
353     /**
354      * @param {!WebInspector.CSSStyleSheetHeader} header
355      */
356     _setStyleSheet: function(header)
357     {
358         this._header = header;
359         WebInspector.cssWorkspaceBinding._addLiveLocation(this);
360         this._cssModel.removeEventListener(WebInspector.CSSStyleModel.Events.StyleSheetAdded, this._styleSheetAdded, this);
361         this._cssModel.addEventListener(WebInspector.CSSStyleModel.Events.StyleSheetRemoved, this._styleSheetRemoved, this);
362     },
363
364     _clearStyleSheet: function()
365     {
366         delete this._header;
367         this._cssModel.removeEventListener(WebInspector.CSSStyleModel.Events.StyleSheetRemoved, this._styleSheetRemoved, this);
368         this._cssModel.addEventListener(WebInspector.CSSStyleModel.Events.StyleSheetAdded, this._styleSheetAdded, this);
369     },
370
371     /**
372      * @return {?WebInspector.UILocation}
373      */
374     uiLocation: function()
375     {
376         var cssLocation = this._rawLocation;
377         if (this._header) {
378             var headerInfo = WebInspector.cssWorkspaceBinding._headerInfo(this._header);
379             return headerInfo._rawLocationToUILocation(cssLocation.lineNumber, cssLocation.columnNumber);
380         }
381         var uiSourceCode = WebInspector.workspace.uiSourceCodeForURL(cssLocation.url);
382         if (!uiSourceCode)
383             return null;
384         return uiSourceCode.uiLocation(cssLocation.lineNumber, cssLocation.columnNumber);
385     },
386
387     dispose: function()
388     {
389         WebInspector.LiveLocation.prototype.dispose.call(this);
390         if (this._header)
391             WebInspector.cssWorkspaceBinding._removeLiveLocation(this);
392         this._cssModel.removeEventListener(WebInspector.CSSStyleModel.Events.StyleSheetAdded, this._styleSheetAdded, this);
393         this._cssModel.removeEventListener(WebInspector.CSSStyleModel.Events.StyleSheetRemoved, this._styleSheetRemoved, this);
394     },
395
396     __proto__: WebInspector.LiveLocation.prototype
397 }
398
399 /**
400  * @interface
401  */
402 WebInspector.CSSSourceMapping = function()
403 {
404 }
405
406 WebInspector.CSSSourceMapping.prototype = {
407     /**
408      * @param {!WebInspector.CSSLocation} rawLocation
409      * @return {?WebInspector.UILocation}
410      */
411     rawLocationToUILocation: function(rawLocation) { },
412
413     /**
414      * @param {!WebInspector.UISourceCode} uiSourceCode
415      * @param {number} lineNumber
416      * @param {number} columnNumber
417      * @return {?WebInspector.CSSLocation}
418      */
419     uiLocationToRawLocation: function(uiSourceCode, lineNumber, columnNumber) { },
420
421     /**
422      * @return {boolean}
423      */
424     isIdentity: function() { },
425
426     /**
427      * @param {!WebInspector.UISourceCode} uiSourceCode
428      * @param {number} lineNumber
429      * @return {boolean}
430      */
431     uiLineHasMapping: function(uiSourceCode, lineNumber) { }
432 }
433
434 /**
435  * @type {!WebInspector.CSSWorkspaceBinding}
436  */
437 WebInspector.cssWorkspaceBinding;