Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(765)

Side by Side Diff: third_party/WebKit/Source/devtools/front_end/bindings/StylesSourceMapping.js

Issue 2662513003: DevTools: make StylesSourceMapping in charge of creating and removing UISourceCodes (Closed)
Patch Set: pass tests Created 3 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 /* 1 /*
2 * Copyright (C) 2012 Google Inc. All rights reserved. 2 * Copyright (C) 2012 Google Inc. All rights reserved.
3 * 3 *
4 * Redistribution and use in source and binary forms, with or without 4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are 5 * modification, are permitted provided that the following conditions are
6 * met: 6 * met:
7 * 7 *
8 * * Redistributions of source code must retain the above copyright 8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer. 9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above 10 * * Redistributions in binary form must reproduce the above
(...skipping 22 matching lines...) Expand all
33 */ 33 */
34 Bindings.StylesSourceMapping = class { 34 Bindings.StylesSourceMapping = class {
35 /** 35 /**
36 * @param {!SDK.CSSModel} cssModel 36 * @param {!SDK.CSSModel} cssModel
37 * @param {!Workspace.Workspace} workspace 37 * @param {!Workspace.Workspace} workspace
38 */ 38 */
39 constructor(cssModel, workspace) { 39 constructor(cssModel, workspace) {
40 this._cssModel = cssModel; 40 this._cssModel = cssModel;
41 this._workspace = workspace; 41 this._workspace = workspace;
42 42
43 /** @type {!Map<string, !Map<string, !Map<string, !SDK.CSSStyleSheetHeader>> >} */ 43 var target = this._cssModel.target();
44 this._urlToHeadersByFrameId = new Map(); 44 var projectId = 'styles:' + target.id();
45 this._project = new Bindings.ContentProviderBasedProject(
46 this._workspace, projectId, Workspace.projectTypes.Network, '', false /* isServiceProject */);
47 Bindings.NetworkProject.connectProjectToTarget(this._project, target);
48
45 /** @type {!Map.<!Workspace.UISourceCode, !Bindings.StyleFile>} */ 49 /** @type {!Map.<!Workspace.UISourceCode, !Bindings.StyleFile>} */
46 this._styleFiles = new Map(); 50 this._styleFiles = new Map();
47 51
48 this._eventListeners = [ 52 this._eventListeners = [
49 this._workspace.addEventListener(Workspace.Workspace.Events.ProjectRemoved , this._projectRemoved, this),
50 this._workspace.addEventListener(
51 Workspace.Workspace.Events.UISourceCodeAdded, this._uiSourceCodeAddedT oWorkspace, this),
52 this._workspace.addEventListener(Workspace.Workspace.Events.UISourceCodeRe moved, this._uiSourceCodeRemoved, this),
53 this._cssModel.addEventListener(SDK.CSSModel.Events.StyleSheetAdded, this. _styleSheetAdded, this), 53 this._cssModel.addEventListener(SDK.CSSModel.Events.StyleSheetAdded, this. _styleSheetAdded, this),
54 this._cssModel.addEventListener(SDK.CSSModel.Events.StyleSheetRemoved, thi s._styleSheetRemoved, this), 54 this._cssModel.addEventListener(SDK.CSSModel.Events.StyleSheetRemoved, thi s._styleSheetRemoved, this),
55 this._cssModel.addEventListener(SDK.CSSModel.Events.StyleSheetChanged, thi s._styleSheetChanged, this), 55 this._cssModel.addEventListener(SDK.CSSModel.Events.StyleSheetChanged, thi s._styleSheetChanged, this)
56 SDK.ResourceTreeModel.fromTarget(cssModel.target())
57 .addEventListener(SDK.ResourceTreeModel.Events.MainFrameNavigated, thi s._unbindAllUISourceCodes, this)
58 ]; 56 ];
59 } 57 }
60 58
61 /** 59 /**
62 * @param {!SDK.CSSLocation} rawLocation 60 * @param {!SDK.CSSLocation} rawLocation
63 * @return {?Workspace.UILocation} 61 * @return {?Workspace.UILocation}
64 */ 62 */
65 rawLocationToUILocation(rawLocation) { 63 rawLocationToUILocation(rawLocation) {
66 var header = rawLocation.header(); 64 var header = rawLocation.header();
67 if (!header) 65 if (!header)
68 return null; 66 return null;
69 var uiSourceCode = Bindings.NetworkProject.uiSourceCodeForStyleURL(this._wor kspace, rawLocation.url, header); 67 var uiSourceCode = this._project.uiSourceCodeForURL(header.contentURL());
70 if (!uiSourceCode) 68 if (!uiSourceCode)
71 return null; 69 return null;
72 var lineNumber = rawLocation.lineNumber; 70 var lineNumber = rawLocation.lineNumber;
73 var columnNumber = rawLocation.columnNumber; 71 var columnNumber = rawLocation.columnNumber;
74 if (header && header.isInline && header.hasSourceURL) { 72 if (header && header.isInline && header.hasSourceURL) {
75 lineNumber -= header.lineNumberInSource(0); 73 lineNumber -= header.lineNumberInSource(0);
76 columnNumber -= header.columnNumberInSource(lineNumber, 0); 74 columnNumber -= header.columnNumberInSource(lineNumber, 0);
77 } 75 }
78 return uiSourceCode.uiLocation(lineNumber, columnNumber); 76 return uiSourceCode.uiLocation(lineNumber, columnNumber);
79 } 77 }
80 78
81 /** 79 /**
82 * @param {!Common.Event} event 80 * @param {!Common.Event} event
83 */ 81 */
84 _styleSheetAdded(event) { 82 _styleSheetAdded(event) {
85 var header = /** @type {!SDK.CSSStyleSheetHeader} */ (event.data); 83 var header = /** @type {!SDK.CSSStyleSheetHeader} */ (event.data);
84 if (header.isInline && !header.hasSourceURL && header.origin !== 'inspector' )
85 return;
86
86 var url = header.resourceURL(); 87 var url = header.resourceURL();
87 if (!url) 88 if (!url)
88 return; 89 return;
89 90
90 var map = this._urlToHeadersByFrameId.get(url); 91 var frame = SDK.ResourceTreeFrame.fromStyleSheet(header);
91 if (!map) { 92 var uiSourceCode = this._project.uiSourceCodeForURL(header.contentURL());
92 map = /** @type {!Map.<string, !Map.<string, !SDK.CSSStyleSheetHeader>>} * / (new Map()); 93 if (!uiSourceCode) {
93 this._urlToHeadersByFrameId.set(url, map); 94 var originalContentProvider = header.originalContentProvider();
95 uiSourceCode = this._project.createUISourceCode(url, originalContentProvid er.contentType());
96 var metadata = frame ? Bindings.resourceMetadata(frame.resourceForURL(url) ) : null;
97 this._project.addUISourceCodeWithProvider(uiSourceCode, originalContentPro vider, metadata);
98 this._styleFiles.set(uiSourceCode, new Bindings.StyleFile(this._cssModel, uiSourceCode));
99 Bindings.NetworkProject.useExplicitMimeType(uiSourceCode);
94 } 100 }
95 var headersById = map.get(header.frameId); 101 var styleFile = this._styleFiles.get(uiSourceCode);
96 if (!headersById) { 102 styleFile.addHeader(header);
97 headersById = /** @type {!Map.<string, !SDK.CSSStyleSheetHeader>} */ (new Map()); 103 if (frame)
98 map.set(header.frameId, headersById); 104 Bindings.NetworkProject.connectFrameToUISourceCode(uiSourceCode, frame);
99 } 105 Bindings.cssWorkspaceBinding.updateLocations(header);
100 headersById.set(header.id, header);
101 var uiSourceCode = Bindings.NetworkProject.uiSourceCodeForStyleURL(this._wor kspace, url, header);
102 if (uiSourceCode)
103 this._bindUISourceCode(uiSourceCode, header);
104 } 106 }
105 107
106 /** 108 /**
107 * @param {!Common.Event} event 109 * @param {!Common.Event} event
108 */ 110 */
109 _styleSheetRemoved(event) { 111 _styleSheetRemoved(event) {
110 var header = /** @type {!SDK.CSSStyleSheetHeader} */ (event.data); 112 var header = /** @type {!SDK.CSSStyleSheetHeader} */ (event.data);
111 var url = header.resourceURL(); 113 var uiSourceCode = this._project.uiSourceCodeForURL(header.contentURL());
112 if (!url) 114 if (!uiSourceCode)
113 return; 115 return;
114
115 var map = this._urlToHeadersByFrameId.get(url);
116 console.assert(map);
117 var headersById = map.get(header.frameId);
118 console.assert(headersById);
119 headersById.delete(header.id);
120
121 if (!headersById.size) {
122 map.delete(header.frameId);
123 if (!map.size) {
124 this._urlToHeadersByFrameId.delete(url);
125 var uiSourceCode = Bindings.NetworkProject.uiSourceCodeForStyleURL(this. _workspace, url, header);
126 if (uiSourceCode)
127 this._unbindUISourceCode(uiSourceCode);
128 }
129 }
130 }
131
132 /**
133 * @param {!Workspace.UISourceCode} uiSourceCode
134 */
135 _unbindUISourceCode(uiSourceCode) {
136 var styleFile = this._styleFiles.get(uiSourceCode); 116 var styleFile = this._styleFiles.get(uiSourceCode);
137 if (!styleFile) 117 styleFile.removeHeader(header);
118 if (styleFile.hasHeaders())
138 return; 119 return;
139 styleFile.dispose(); 120 styleFile.dispose();
140 this._styleFiles.delete(uiSourceCode); 121 this._styleFiles.delete(uiSourceCode);
141 } 122 this._project.removeFile(uiSourceCode.url());
142
143 /**
144 * @param {!Common.Event} event
145 */
146 _unbindAllUISourceCodes(event) {
147 if (event.data.target() !== this._cssModel.target())
148 return;
149 for (var styleFile of this._styleFiles.values())
150 styleFile.dispose();
151 this._styleFiles.clear();
152 this._urlToHeadersByFrameId = new Map();
153 }
154
155 /**
156 * @param {!Common.Event} event
157 */
158 _uiSourceCodeAddedToWorkspace(event) {
159 var uiSourceCode = /** @type {!Workspace.UISourceCode} */ (event.data);
160 if (!this._urlToHeadersByFrameId.has(uiSourceCode.url()))
161 return;
162 this._bindUISourceCode(
163 uiSourceCode, this._urlToHeadersByFrameId.get(uiSourceCode.url()).values Array()[0].valuesArray()[0]);
164 }
165
166 /**
167 * @param {!Workspace.UISourceCode} uiSourceCode
168 * @param {!SDK.CSSStyleSheetHeader} header
169 */
170 _bindUISourceCode(uiSourceCode, header) {
171 if (this._styleFiles.get(uiSourceCode) || (header.isInline && !header.hasSou rceURL))
172 return;
173 this._styleFiles.set(uiSourceCode, new Bindings.StyleFile(uiSourceCode, this ));
174 Bindings.cssWorkspaceBinding.updateLocations(header);
175 }
176
177 /**
178 * @param {!Common.Event} event
179 */
180 _projectRemoved(event) {
181 var project = /** @type {!Workspace.Project} */ (event.data);
182 var uiSourceCodes = project.uiSourceCodes();
183 for (var i = 0; i < uiSourceCodes.length; ++i)
184 this._unbindUISourceCode(uiSourceCodes[i]);
185 }
186
187 /**
188 * @param {!Common.Event} event
189 */
190 _uiSourceCodeRemoved(event) {
191 var uiSourceCode = /** @type {!Workspace.UISourceCode} */ (event.data);
192 this._unbindUISourceCode(uiSourceCode);
193 }
194
195 /**
196 * @param {!Workspace.UISourceCode} uiSourceCode
197 * @param {string} content
198 * @param {boolean} majorChange
199 * @return {!Promise<?string>}
200 */
201 _setStyleContent(uiSourceCode, content, majorChange) {
202 var styleSheetIds = this._cssModel.styleSheetIdsForURL(uiSourceCode.url());
203 if (!styleSheetIds.length)
204 return Promise.resolve(/** @type {?string} */ ('No stylesheet found: ' + u iSourceCode.url()));
205
206 this._isSettingContent = true;
207
208 /**
209 * @param {?string} error
210 * @this {Bindings.StylesSourceMapping}
211 * @return {?string}
212 */
213 function callback(error) {
214 delete this._isSettingContent;
215 return error || null;
216 }
217
218 var promises = [];
219 for (var i = 0; i < styleSheetIds.length; ++i)
220 promises.push(this._cssModel.setStyleSheetText(styleSheetIds[i], content, majorChange));
221
222 return Promise.all(promises).spread(callback.bind(this));
223 } 123 }
224 124
225 /** 125 /**
226 * @param {!Common.Event} event 126 * @param {!Common.Event} event
227 */ 127 */
228 _styleSheetChanged(event) { 128 _styleSheetChanged(event) {
229 if (this._isSettingContent) 129 var styleSheetId = /** @type {!Protocol.CSS.StyleSheetId} */ (event.data.sty leSheetId);
230 return;
231
232 this._updateStyleSheetTextSoon(event.data.styleSheetId);
233 }
234
235 /**
236 * @param {!Protocol.CSS.StyleSheetId} styleSheetId
237 */
238 _updateStyleSheetTextSoon(styleSheetId) {
239 if (this._updateStyleSheetTextTimer)
240 clearTimeout(this._updateStyleSheetTextTimer);
241
242 this._updateStyleSheetTextTimer = setTimeout(
243 this._updateStyleSheetText.bind(this, styleSheetId), Bindings.StylesSour ceMapping.ChangeUpdateTimeoutMs);
244 }
245
246 /**
247 * @param {!Protocol.CSS.StyleSheetId} styleSheetId
248 */
249 _updateStyleSheetText(styleSheetId) {
250 if (this._updateStyleSheetTextTimer) {
251 clearTimeout(this._updateStyleSheetTextTimer);
252 delete this._updateStyleSheetTextTimer;
253 }
254
255 var header = this._cssModel.styleSheetHeaderForId(styleSheetId); 130 var header = this._cssModel.styleSheetHeaderForId(styleSheetId);
256 if (!header) 131 if (!header)
257 return; 132 return;
258 var styleSheetURL = header.resourceURL(); 133 var uiSourceCode = this._project.uiSourceCodeForURL(header.contentURL());
259 if (!styleSheetURL)
260 return;
261 var uiSourceCode = Bindings.NetworkProject.uiSourceCodeForStyleURL(this._wor kspace, styleSheetURL, header);
262 if (!uiSourceCode) 134 if (!uiSourceCode)
263 return; 135 return;
264 header.requestContent().then(callback.bind(this, uiSourceCode)); 136 var styleFile = this._styleFiles.get(uiSourceCode);
265 137 if (styleFile)
266 /** 138 styleFile.styleSheetChanged(header);
267 * @param {!Workspace.UISourceCode} uiSourceCode
268 * @param {?string} content
269 * @this {Bindings.StylesSourceMapping}
270 */
271 function callback(uiSourceCode, content) {
272 var styleFile = this._styleFiles.get(uiSourceCode);
273 if (typeof content === 'string' && styleFile)
274 styleFile.addRevision(content);
275 this._styleFileSyncedForTest();
276 }
277 }
278
279 _styleFileSyncedForTest() {
280 } 139 }
281 140
282 dispose() { 141 dispose() {
142 for (var styleFile of this._styleFiles.values())
143 styleFile.dispose();
144 this._styleFiles.clear();
145 this._project.removeProject();
283 Common.EventTarget.removeEventListeners(this._eventListeners); 146 Common.EventTarget.removeEventListeners(this._eventListeners);
284 } 147 }
285 }; 148 };
286 149
287 Bindings.StylesSourceMapping.ChangeUpdateTimeoutMs = 200;
288
289 /** 150 /**
290 * @unrestricted 151 * @unrestricted
291 */ 152 */
292 Bindings.StyleFile = class { 153 Bindings.StyleFile = class {
293 /** 154 /**
155 * @param {!SDK.CSSModel} cssModel
294 * @param {!Workspace.UISourceCode} uiSourceCode 156 * @param {!Workspace.UISourceCode} uiSourceCode
295 * @param {!Bindings.StylesSourceMapping} mapping
296 */ 157 */
297 constructor(uiSourceCode, mapping) { 158 constructor(cssModel, uiSourceCode) {
159 this._cssModel = cssModel;
298 this._uiSourceCode = uiSourceCode; 160 this._uiSourceCode = uiSourceCode;
299 this._mapping = mapping; 161 /** @type {!Set<!SDK.CSSStyleSheetHeader>} */
162 this._headers = new Set();
300 this._eventListeners = [ 163 this._eventListeners = [
301 this._uiSourceCode.addEventListener( 164 this._uiSourceCode.addEventListener(
302 Workspace.UISourceCode.Events.WorkingCopyChanged, this._workingCopyCha nged, this), 165 Workspace.UISourceCode.Events.WorkingCopyChanged, this._workingCopyCha nged, this),
303 this._uiSourceCode.addEventListener( 166 this._uiSourceCode.addEventListener(
304 Workspace.UISourceCode.Events.WorkingCopyCommitted, this._workingCopyC ommitted, this) 167 Workspace.UISourceCode.Events.WorkingCopyCommitted, this._workingCopyC ommitted, this)
305 ]; 168 ];
306 this._commitThrottler = new Common.Throttler(Bindings.StyleFile.updateTimeou t); 169 this._commitThrottler = new Common.Throttler(Bindings.StyleFile.updateTimeou t);
307 this._terminated = false; 170 this._terminated = false;
308 } 171 }
309 172
310 /** 173 /**
174 * @param {!SDK.CSSStyleSheetHeader} header
175 */
176 addHeader(header) {
177 this._headers.add(header);
178 }
179
180 /**
181 * @param {!SDK.CSSStyleSheetHeader} header
182 */
183 removeHeader(header) {
184 this._headers.delete(header);
185 }
186
187 /**
188 * @return {boolean}
189 */
190 hasHeaders() {
191 return !!this._headers.size;
192 }
193
194 /**
195 * @param {!SDK.CSSStyleSheetHeader} header
196 */
197 styleSheetChanged(header) {
198 if (this._isSettingStyleSheetContents)
199 return;
200
201 this._commitThrottler.schedule(this._syncStyleSheetChange.bind(this, header) , false);
202 }
203
204 /**
311 * @param {!Common.Event} event 205 * @param {!Common.Event} event
312 */ 206 */
313 _workingCopyCommitted(event) { 207 _workingCopyCommitted(event) {
314 if (this._isAddingRevision) 208 if (this._isAddingRevision)
315 return; 209 return;
316 210
317 this._isMajorChangePending = true; 211 this._isMajorChangePending = true;
318 this._commitThrottler.schedule(this._commitIncrementalEdit.bind(this), true) ; 212 this._commitThrottler.schedule(this._syncUISourceCodeChange.bind(this), true );
319 } 213 }
320 214
321 /** 215 /**
322 * @param {!Common.Event} event 216 * @param {!Common.Event} event
323 */ 217 */
324 _workingCopyChanged(event) { 218 _workingCopyChanged(event) {
325 if (this._isAddingRevision) 219 if (this._isAddingRevision)
326 return; 220 return;
327 221
328 this._commitThrottler.schedule(this._commitIncrementalEdit.bind(this), false ); 222 this._commitThrottler.schedule(this._syncUISourceCodeChange.bind(this), fals e);
329 } 223 }
330 224
331 _commitIncrementalEdit() { 225 /**
226 * @param {!SDK.CSSStyleSheetHeader} header
227 * @return {!Promise}
228 */
229 _syncStyleSheetChange(header) {
230 if (this._terminated || !this._headers.has(header))
231 return Promise.resolve();
232
233 return header.requestContent().then(onHeaderContent.bind(this));
234
235 /**
236 * @param {?string} content
237 * @return {!Promise}
238 * @this {Bindings.StyleFile}
239 */
240 function onHeaderContent(content) {
241 if (this._terminated || content === null) {
242 this._styleFileSyncedForTest();
243 return Promise.resolve();
244 }
245 this._isAddingRevision = true;
246 this._uiSourceCode.addRevision(content);
247 delete this._isAddingRevision;
248 return this._setStyleSheetContents(content, header, true);
249 }
250 }
251
252 /**
253 * @return {!Promise}
254 */
255 _syncUISourceCodeChange() {
332 if (this._terminated) 256 if (this._terminated)
333 return; 257 return Promise.resolve();
334 var promise = 258 var promise = this._setStyleSheetContents(this._uiSourceCode.workingCopy(), null, this._isMajorChangePending);
335 this._mapping._setStyleContent(this._uiSourceCode, this._uiSourceCode.wo rkingCopy(), this._isMajorChangePending)
336 .then(this._styleContentSet.bind(this));
337 this._isMajorChangePending = false; 259 this._isMajorChangePending = false;
338 return promise; 260 return promise;
339 } 261 }
340 262
341 /** 263 /**
342 * @param {?string} error 264 * @param {string} content
265 * @param {?SDK.CSSStyleSheetHeader} skipStyleSheet
266 * @param {boolean} majorChange
267 * @return {!Promise}
343 */ 268 */
344 _styleContentSet(error) { 269 _setStyleSheetContents(content, skipStyleSheet, majorChange) {
345 if (error) 270 this._isSettingStyleSheetContents = true;
346 console.error(error); 271 var promises = [];
272 for (var header of this._headers) {
273 if (header === skipStyleSheet)
274 continue;
275 promises.push(this._cssModel.setStyleSheetText(header.id, content, majorCh ange));
276 }
277
278 return Promise.all(promises).then(onStyleSheetContentsUpdated.bind(this));
279
280 /**
281 * @param {!Array<?Protocol.Error>} errors
282 * @this {Bindings.StyleFile}
283 */
284 function onStyleSheetContentsUpdated(errors) {
285 delete this._isSettingStyleSheetContents;
286 errors.filter(error => !!error).forEach(console.error);
287 this._styleFileSyncedForTest();
288 }
347 } 289 }
348 290
349 /** 291 _styleFileSyncedForTest() {
350 * @param {string} content
351 */
352 addRevision(content) {
353 this._isAddingRevision = true;
354 this._uiSourceCode.addRevision(content);
355 delete this._isAddingRevision;
356 } 292 }
357 293
358 dispose() { 294 dispose() {
359 if (this._terminated) 295 if (this._terminated)
360 return; 296 return;
361 this._terminated = true; 297 this._terminated = true;
362 Common.EventTarget.removeEventListeners(this._eventListeners); 298 Common.EventTarget.removeEventListeners(this._eventListeners);
363 } 299 }
364 }; 300 };
365 301
366 Bindings.StyleFile.updateTimeout = 200; 302 Bindings.StyleFile.updateTimeout = 200;
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698