StylesSourceMapping.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334
  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. * @constructor
  32. * @implements {WebInspector.SourceMapping}
  33. * @param {WebInspector.CSSStyleModel} cssModel
  34. * @param {WebInspector.Workspace} workspace
  35. */
  36. WebInspector.StylesSourceMapping = function(cssModel, workspace)
  37. {
  38. this._cssModel = cssModel;
  39. this._workspace = workspace;
  40. this._workspace.addEventListener(WebInspector.Workspace.Events.ProjectWillReset, this._projectWillReset, this);
  41. this._workspace.addEventListener(WebInspector.UISourceCodeProvider.Events.UISourceCodeAdded, this._uiSourceCodeAddedToWorkspace, this);
  42. WebInspector.resourceTreeModel.addEventListener(WebInspector.ResourceTreeModel.EventTypes.MainFrameCreatedOrNavigated, this._mainFrameCreatedOrNavigated, this);
  43. WebInspector.resourceTreeModel.addEventListener(WebInspector.ResourceTreeModel.EventTypes.ResourceAdded, this._resourceAdded, this);
  44. this._initialize();
  45. }
  46. WebInspector.StylesSourceMapping.prototype = {
  47. /**
  48. * @param {WebInspector.RawLocation} rawLocation
  49. * @return {WebInspector.UILocation}
  50. */
  51. rawLocationToUILocation: function(rawLocation)
  52. {
  53. var location = /** @type WebInspector.CSSLocation */ (rawLocation);
  54. var uiSourceCode = this._workspace.uiSourceCodeForURL(location.url);
  55. return new WebInspector.UILocation(uiSourceCode, location.lineNumber, 0);
  56. },
  57. /**
  58. * @param {WebInspector.UISourceCode} uiSourceCode
  59. * @param {number} lineNumber
  60. * @param {number} columnNumber
  61. * @return {WebInspector.RawLocation}
  62. */
  63. uiLocationToRawLocation: function(uiSourceCode, lineNumber, columnNumber)
  64. {
  65. return new WebInspector.CSSLocation(uiSourceCode.url || "", lineNumber);
  66. },
  67. /**
  68. * @return {boolean}
  69. */
  70. isIdentity: function()
  71. {
  72. return true;
  73. },
  74. _resourceAdded: function(event)
  75. {
  76. var resource = /** @type {WebInspector.UISourceCode} */ (event.data);
  77. if (resource.contentType() !== WebInspector.resourceTypes.Stylesheet)
  78. return;
  79. if (!resource.url)
  80. return;
  81. var uiSourceCode = this._workspace.uiSourceCodeForURL(resource.url);
  82. if (!uiSourceCode)
  83. return;
  84. this._bindUISourceCode(uiSourceCode);
  85. },
  86. _uiSourceCodeAddedToWorkspace: function(event)
  87. {
  88. var uiSourceCode = /** @type {WebInspector.UISourceCode} */ (event.data);
  89. if (uiSourceCode.contentType() !== WebInspector.resourceTypes.Stylesheet)
  90. return;
  91. if (!uiSourceCode.url || !WebInspector.resourceForURL(uiSourceCode.url))
  92. return;
  93. this._bindUISourceCode(uiSourceCode);
  94. },
  95. _bindUISourceCode: function(uiSourceCode)
  96. {
  97. if (this._mappedURLs[uiSourceCode.url])
  98. return;
  99. this._mappedURLs[uiSourceCode.url] = true;
  100. uiSourceCode.setSourceMapping(this);
  101. var styleFile = new WebInspector.StyleFile(uiSourceCode);
  102. uiSourceCode.setStyleFile(styleFile);
  103. this._cssModel.setSourceMapping(uiSourceCode.url, this);
  104. },
  105. _projectWillReset: function(event)
  106. {
  107. var project = event.data;
  108. var uiSourceCodes = project.uiSourceCodes();
  109. for (var i = 0; i < uiSourceCodes; ++i)
  110. delete this._mappedURLs[uiSourceCodes[i].url];
  111. },
  112. _initialize: function()
  113. {
  114. /** {Object.<string, boolean>} */
  115. this._mappedURLs = {};
  116. },
  117. /**
  118. * @param {WebInspector.Event} event
  119. */
  120. _mainFrameCreatedOrNavigated: function(event)
  121. {
  122. for (var mappedURL in this._mappedURLs) {
  123. var uiSourceCode = this._workspace.uiSourceCodeForURL(mappedURL);
  124. if (!uiSourceCode)
  125. continue;
  126. uiSourceCode.styleFile().dispose();
  127. uiSourceCode.setStyleFile(null);
  128. uiSourceCode.setSourceMapping(null);
  129. }
  130. this._initialize();
  131. }
  132. }
  133. /**
  134. * @constructor
  135. * @param {WebInspector.UISourceCode} uiSourceCode
  136. */
  137. WebInspector.StyleFile = function(uiSourceCode)
  138. {
  139. this._uiSourceCode = uiSourceCode;
  140. this._uiSourceCode.addEventListener(WebInspector.UISourceCode.Events.WorkingCopyChanged, this._workingCopyChanged, this);
  141. this._uiSourceCode.addEventListener(WebInspector.UISourceCode.Events.WorkingCopyCommitted, this._workingCopyCommitted, this);
  142. }
  143. WebInspector.StyleFile.updateTimeout = 200;
  144. WebInspector.StyleFile.prototype = {
  145. _workingCopyCommitted: function(event)
  146. {
  147. if (this._isAddingRevision)
  148. return;
  149. this._commitIncrementalEdit(true);
  150. },
  151. _workingCopyChanged: function(event)
  152. {
  153. if (this._isAddingRevision)
  154. return;
  155. // FIXME: Extensions tests override updateTimeout because extensions don't have any control over applying changes to domain specific bindings.
  156. if (WebInspector.StyleFile.updateTimeout >= 0) {
  157. this._incrementalUpdateTimer = setTimeout(this._commitIncrementalEdit.bind(this, false), WebInspector.StyleFile.updateTimeout)
  158. } else
  159. this._commitIncrementalEdit(false);
  160. },
  161. /**
  162. * @param {boolean} majorChange
  163. */
  164. _commitIncrementalEdit: function(majorChange)
  165. {
  166. this._clearIncrementalUpdateTimer();
  167. WebInspector.styleContentBinding.setStyleContent(this._uiSourceCode, this._uiSourceCode.workingCopy(), majorChange, this._styleContentSet.bind(this));
  168. },
  169. /**
  170. * @param {?string} error
  171. */
  172. _styleContentSet: function(error)
  173. {
  174. if (error)
  175. WebInspector.showErrorMessage(error);
  176. },
  177. _clearIncrementalUpdateTimer: function()
  178. {
  179. if (!this._incrementalUpdateTimer)
  180. return;
  181. clearTimeout(this._incrementalUpdateTimer);
  182. delete this._incrementalUpdateTimer;
  183. },
  184. /**
  185. * @param {string} content
  186. */
  187. addRevision: function(content)
  188. {
  189. this._isAddingRevision = true;
  190. this._uiSourceCode.addRevision(content);
  191. delete this._isAddingRevision;
  192. },
  193. dispose: function()
  194. {
  195. this._uiSourceCode.removeEventListener(WebInspector.UISourceCode.Events.WorkingCopyCommitted, this._workingCopyCommitted, this);
  196. this._uiSourceCode.removeEventListener(WebInspector.UISourceCode.Events.WorkingCopyChanged, this._workingCopyChanged, this);
  197. }
  198. }
  199. /**
  200. * @constructor
  201. * @param {WebInspector.CSSStyleModel} cssModel
  202. */
  203. WebInspector.StyleContentBinding = function(cssModel, workspace)
  204. {
  205. this._cssModel = cssModel;
  206. this._workspace = workspace;
  207. this._cssModel.addEventListener(WebInspector.CSSStyleModel.Events.StyleSheetChanged, this._styleSheetChanged, this);
  208. }
  209. WebInspector.StyleContentBinding.prototype = {
  210. /**
  211. * @param {WebInspector.UISourceCode} uiSourceCode
  212. * @param {string} content
  213. * @param {boolean} majorChange
  214. * @param {function(?string)} userCallback
  215. */
  216. setStyleContent: function(uiSourceCode, content, majorChange, userCallback)
  217. {
  218. var resource = WebInspector.resourceForURL(uiSourceCode.url);
  219. if (!resource) {
  220. userCallback("No resource found: " + uiSourceCode.url);
  221. return;
  222. }
  223. this._cssModel.resourceBinding().requestStyleSheetIdForResource(resource, callback.bind(this));
  224. /**
  225. * @param {?CSSAgent.StyleSheetId} styleSheetId
  226. */
  227. function callback(styleSheetId)
  228. {
  229. if (!styleSheetId) {
  230. userCallback("No stylesheet found: " + resource.frameId + ":" + resource.url);
  231. return;
  232. }
  233. this._innerSetContent(styleSheetId, content, majorChange, userCallback, null);
  234. }
  235. },
  236. /**
  237. * @param {CSSAgent.StyleSheetId} styleSheetId
  238. * @param {string} content
  239. * @param {boolean} majorChange
  240. * @param {function(?string)} userCallback
  241. */
  242. _innerSetContent: function(styleSheetId, content, majorChange, userCallback)
  243. {
  244. this._isSettingContent = true;
  245. function callback(error)
  246. {
  247. userCallback(error);
  248. delete this._isSettingContent;
  249. }
  250. this._cssModel.setStyleSheetText(styleSheetId, content, majorChange, callback.bind(this));
  251. },
  252. /**
  253. * @param {WebInspector.Event} event
  254. */
  255. _styleSheetChanged: function(event)
  256. {
  257. if (this._isSettingContent)
  258. return;
  259. if (!event.data.majorChange)
  260. return;
  261. /**
  262. * @param {?string} error
  263. * @param {string} content
  264. */
  265. function callback(error, content)
  266. {
  267. if (!error)
  268. this._innerStyleSheetChanged(event.data.styleSheetId, content);
  269. }
  270. CSSAgent.getStyleSheetText(event.data.styleSheetId, callback.bind(this));
  271. },
  272. /**
  273. * @param {CSSAgent.StyleSheetId} styleSheetId
  274. * @param {string} content
  275. */
  276. _innerStyleSheetChanged: function(styleSheetId, content)
  277. {
  278. /**
  279. * @param {?string} styleSheetURL
  280. */
  281. function callback(styleSheetURL)
  282. {
  283. if (typeof styleSheetURL !== "string")
  284. return;
  285. var uiSourceCode = this._workspace.uiSourceCodeForURL(styleSheetURL);
  286. if (!uiSourceCode)
  287. return;
  288. if (uiSourceCode.styleFile())
  289. uiSourceCode.styleFile().addRevision(content);
  290. }
  291. this._cssModel.resourceBinding().requestResourceURLForStyleSheetId(styleSheetId, callback.bind(this));
  292. },
  293. }
  294. /**
  295. * @type {?WebInspector.StyleContentBinding}
  296. */
  297. WebInspector.styleContentBinding = null;