2 * Copyright (C) 2012 Google Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
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
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.
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.
33 * @implements {WebInspector.CSSSourceMapping}
34 * @param {!WebInspector.CSSStyleModel} cssModel
35 * @param {!WebInspector.Workspace} workspace
37 WebInspector.StylesSourceMapping = function(cssModel, workspace)
39 this._cssModel = cssModel;
40 this._workspace = workspace;
41 this._workspace.addEventListener(WebInspector.Workspace.Events.ProjectRemoved, this._projectRemoved, this);
42 this._workspace.addEventListener(WebInspector.Workspace.Events.UISourceCodeAdded, this._uiSourceCodeAddedToWorkspace, this);
43 this._workspace.addEventListener(WebInspector.Workspace.Events.UISourceCodeRemoved, this._uiSourceCodeRemoved, this);
45 cssModel.target().resourceTreeModel.addEventListener(WebInspector.ResourceTreeModel.EventTypes.MainFrameNavigated, this._mainFrameNavigated, this);
47 this._cssModel.addEventListener(WebInspector.CSSStyleModel.Events.StyleSheetChanged, this._styleSheetChanged, this);
51 WebInspector.StylesSourceMapping.MinorChangeUpdateTimeoutMs = 1000;
53 WebInspector.StylesSourceMapping.prototype = {
55 * @param {!WebInspector.CSSLocation} rawLocation
56 * @return {?WebInspector.UILocation}
58 rawLocationToUILocation: function(rawLocation)
60 var location = /** @type WebInspector.CSSLocation */ (rawLocation);
61 var uiSourceCode = this._workspace.uiSourceCodeForURL(location.url);
64 return uiSourceCode.uiLocation(location.lineNumber, location.columnNumber);
68 * @param {!WebInspector.UISourceCode} uiSourceCode
69 * @param {number} lineNumber
70 * @param {number} columnNumber
71 * @return {!WebInspector.CSSLocation}
73 uiLocationToRawLocation: function(uiSourceCode, lineNumber, columnNumber)
75 return new WebInspector.CSSLocation(this._cssModel.target(), null, uiSourceCode.url || "", lineNumber, columnNumber);
81 isIdentity: function()
87 * @param {!WebInspector.UISourceCode} uiSourceCode
88 * @param {number} lineNumber
91 uiLineHasMapping: function(uiSourceCode, lineNumber)
97 * @return {!WebInspector.Target}
101 return this._cssModel.target();
105 * @param {!WebInspector.CSSStyleSheetHeader} header
107 addHeader: function(header)
109 var url = header.resourceURL();
113 WebInspector.cssWorkspaceBinding.pushSourceMapping(header, this);
114 var map = this._urlToHeadersByFrameId[url];
116 map = /** @type {!Map.<string, !Map.<string, !WebInspector.CSSStyleSheetHeader>>} */ (new Map());
117 this._urlToHeadersByFrameId[url] = map;
119 var headersById = map.get(header.frameId);
121 headersById = /** @type {!Map.<string, !WebInspector.CSSStyleSheetHeader>} */ (new Map());
122 map.set(header.frameId, headersById);
124 headersById.set(header.id, header);
125 var uiSourceCode = this._workspace.uiSourceCodeForURL(url);
127 this._bindUISourceCode(uiSourceCode, header);
131 * @param {!WebInspector.CSSStyleSheetHeader} header
133 removeHeader: function(header)
135 var url = header.resourceURL();
139 var map = this._urlToHeadersByFrameId[url];
141 var headersById = map.get(header.frameId);
142 console.assert(headersById);
143 headersById.remove(header.id);
145 if (!headersById.size) {
146 map.remove(header.frameId);
148 delete this._urlToHeadersByFrameId[url];
149 var uiSourceCode = this._workspace.uiSourceCodeForURL(url);
151 this._unbindUISourceCode(uiSourceCode);
157 * @param {!WebInspector.UISourceCode} uiSourceCode
159 _unbindUISourceCode: function(uiSourceCode)
161 var styleFile = this._styleFiles.get(uiSourceCode);
165 this._styleFiles.remove(uiSourceCode);
169 * @param {!WebInspector.Event} event
171 _uiSourceCodeAddedToWorkspace: function(event)
173 var uiSourceCode = /** @type {!WebInspector.UISourceCode} */ (event.data);
174 var url = uiSourceCode.url;
175 if (!url || !this._urlToHeadersByFrameId[url])
177 this._bindUISourceCode(uiSourceCode, this._urlToHeadersByFrameId[url].valuesArray()[0].valuesArray()[0]);
181 * @param {!WebInspector.UISourceCode} uiSourceCode
182 * @param {!WebInspector.CSSStyleSheetHeader} header
184 _bindUISourceCode: function(uiSourceCode, header)
186 if (this._styleFiles.get(uiSourceCode) || header.isInline)
188 var url = uiSourceCode.url;
189 this._styleFiles.set(uiSourceCode, new WebInspector.StyleFile(uiSourceCode, this));
190 WebInspector.cssWorkspaceBinding.updateLocations(header);
194 * @param {!WebInspector.Event} event
196 _projectRemoved: function(event)
198 var project = /** @type {!WebInspector.Project} */ (event.data);
199 var uiSourceCodes = project.uiSourceCodes();
200 for (var i = 0; i < uiSourceCodes.length; ++i)
201 this._unbindUISourceCode(uiSourceCodes[i]);
205 * @param {!WebInspector.Event} event
207 _uiSourceCodeRemoved: function(event)
209 var uiSourceCode = /** @type {!WebInspector.UISourceCode} */ (event.data);
210 this._unbindUISourceCode(uiSourceCode);
213 _initialize: function()
215 /** @type {!Object.<string, !Map.<string, !Map.<string, !WebInspector.CSSStyleSheetHeader>>>} */
216 this._urlToHeadersByFrameId = {};
217 /** @type {!Map.<!WebInspector.UISourceCode, !WebInspector.StyleFile>} */
218 this._styleFiles = new Map();
222 * @param {!WebInspector.Event} event
224 _mainFrameNavigated: function(event)
226 for (var url in this._urlToHeadersByFrameId) {
227 var uiSourceCode = this._workspace.uiSourceCodeForURL(url);
230 this._unbindUISourceCode(uiSourceCode);
236 * @param {!WebInspector.UISourceCode} uiSourceCode
237 * @param {string} content
238 * @param {boolean} majorChange
239 * @param {function(?string)} userCallback
241 _setStyleContent: function(uiSourceCode, content, majorChange, userCallback)
243 var styleSheetIds = this._cssModel.styleSheetIdsForURL(uiSourceCode.url);
244 if (!styleSheetIds.length) {
245 userCallback("No stylesheet found: " + uiSourceCode.url);
249 this._isSettingContent = true;
252 * @param {?Protocol.Error} error
253 * @this {WebInspector.StylesSourceMapping}
255 function callback(error)
258 delete this._isSettingContent;
260 this._cssModel.setStyleSheetText(styleSheetIds[0], content, majorChange, callback.bind(this));
264 * @param {!WebInspector.Event} event
266 _styleSheetChanged: function(event)
268 if (this._isSettingContent)
271 if (event.data.majorChange) {
272 this._updateStyleSheetText(event.data.styleSheetId);
276 this._updateStyleSheetTextSoon(event.data.styleSheetId);
280 * @param {!CSSAgent.StyleSheetId} styleSheetId
282 _updateStyleSheetTextSoon: function(styleSheetId)
284 if (this._updateStyleSheetTextTimer)
285 clearTimeout(this._updateStyleSheetTextTimer);
287 this._updateStyleSheetTextTimer = setTimeout(this._updateStyleSheetText.bind(this, styleSheetId), WebInspector.StylesSourceMapping.MinorChangeUpdateTimeoutMs);
291 * @param {!CSSAgent.StyleSheetId} styleSheetId
293 _updateStyleSheetText: function(styleSheetId)
295 if (this._updateStyleSheetTextTimer) {
296 clearTimeout(this._updateStyleSheetTextTimer);
297 delete this._updateStyleSheetTextTimer;
300 var header = this._cssModel.styleSheetHeaderForId(styleSheetId);
303 var styleSheetURL = header.resourceURL();
306 var uiSourceCode = this._workspace.uiSourceCodeForURL(styleSheetURL)
309 header.requestContent(callback.bind(this, uiSourceCode));
312 * @param {!WebInspector.UISourceCode} uiSourceCode
313 * @param {?string} content
314 * @this {WebInspector.StylesSourceMapping}
316 function callback(uiSourceCode, content)
318 var styleFile = this._styleFiles.get(uiSourceCode);
320 styleFile.addRevision(content || "");
327 * @param {!WebInspector.UISourceCode} uiSourceCode
328 * @param {!WebInspector.StylesSourceMapping} mapping
330 WebInspector.StyleFile = function(uiSourceCode, mapping)
332 this._uiSourceCode = uiSourceCode;
333 this._mapping = mapping;
334 this._uiSourceCode.addEventListener(WebInspector.UISourceCode.Events.WorkingCopyChanged, this._workingCopyChanged, this);
335 this._uiSourceCode.addEventListener(WebInspector.UISourceCode.Events.WorkingCopyCommitted, this._workingCopyCommitted, this);
336 this._commitThrottler = new WebInspector.Throttler(WebInspector.StyleFile.updateTimeout);
339 WebInspector.StyleFile.updateTimeout = 200;
341 WebInspector.StyleFile.prototype = {
343 * @param {!WebInspector.Event} event
345 _workingCopyCommitted: function(event)
347 if (this._isAddingRevision)
350 this._isMajorChangePending = true;
351 this._commitThrottler.schedule(this._commitIncrementalEdit.bind(this), true);
355 * @param {!WebInspector.Event} event
357 _workingCopyChanged: function(event)
359 if (this._isAddingRevision)
362 this._commitThrottler.schedule(this._commitIncrementalEdit.bind(this), false);
366 * @param {!WebInspector.Throttler.FinishCallback} finishCallback
368 _commitIncrementalEdit: function(finishCallback)
370 this._mapping._setStyleContent(this._uiSourceCode, this._uiSourceCode.workingCopy(), this._isMajorChangePending, this._styleContentSet.bind(this, finishCallback));
371 this._isMajorChangePending = false;
375 * @param {!WebInspector.Throttler.FinishCallback} finishCallback
376 * @param {?string} error
378 _styleContentSet: function(finishCallback, error)
381 WebInspector.console.error(error);
386 * @param {string} content
388 addRevision: function(content)
390 this._isAddingRevision = true;
391 this._uiSourceCode.addRevision(content);
392 delete this._isAddingRevision;
397 this._uiSourceCode.removeEventListener(WebInspector.UISourceCode.Events.WorkingCopyCommitted, this._workingCopyCommitted, this);
398 this._uiSourceCode.removeEventListener(WebInspector.UISourceCode.Events.WorkingCopyChanged, this._workingCopyChanged, this);