CSSStyleModel.js 52 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714
  1. /*
  2. * Copyright (C) 2010 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. * @extends {WebInspector.Object}
  33. * @param {WebInspector.Workspace} workspace
  34. */
  35. WebInspector.CSSStyleModel = function(workspace)
  36. {
  37. this._workspace = workspace;
  38. this._pendingCommandsMajorState = [];
  39. WebInspector.domAgent.addEventListener(WebInspector.DOMAgent.Events.UndoRedoRequested, this._undoRedoRequested, this);
  40. WebInspector.domAgent.addEventListener(WebInspector.DOMAgent.Events.UndoRedoCompleted, this._undoRedoCompleted, this);
  41. WebInspector.resourceTreeModel.addEventListener(WebInspector.ResourceTreeModel.EventTypes.MainFrameCreatedOrNavigated, this._mainFrameCreatedOrNavigated, this);
  42. this._namedFlowCollections = {};
  43. WebInspector.domAgent.addEventListener(WebInspector.DOMAgent.Events.DocumentUpdated, this._resetNamedFlowCollections, this);
  44. InspectorBackend.registerCSSDispatcher(new WebInspector.CSSDispatcher(this));
  45. CSSAgent.enable();
  46. this._resetStyleSheets();
  47. }
  48. /**
  49. * @param {Array.<CSSAgent.CSSRule>} ruleArray
  50. */
  51. WebInspector.CSSStyleModel.parseRuleArrayPayload = function(ruleArray)
  52. {
  53. var result = [];
  54. for (var i = 0; i < ruleArray.length; ++i)
  55. result.push(WebInspector.CSSRule.parsePayload(ruleArray[i]));
  56. return result;
  57. }
  58. /**
  59. * @param {Array.<CSSAgent.RuleMatch>} matchArray
  60. */
  61. WebInspector.CSSStyleModel.parseRuleMatchArrayPayload = function(matchArray)
  62. {
  63. var result = [];
  64. for (var i = 0; i < matchArray.length; ++i)
  65. result.push(WebInspector.CSSRule.parsePayload(matchArray[i].rule, matchArray[i].matchingSelectors));
  66. return result;
  67. }
  68. WebInspector.CSSStyleModel.Events = {
  69. StyleSheetAdded: "StyleSheetAdded",
  70. StyleSheetChanged: "StyleSheetChanged",
  71. StyleSheetRemoved: "StyleSheetRemoved",
  72. MediaQueryResultChanged: "MediaQueryResultChanged",
  73. NamedFlowCreated: "NamedFlowCreated",
  74. NamedFlowRemoved: "NamedFlowRemoved",
  75. RegionLayoutUpdated: "RegionLayoutUpdated",
  76. RegionOversetChanged: "RegionOversetChanged"
  77. }
  78. WebInspector.CSSStyleModel.MediaTypes = ["all", "braille", "embossed", "handheld", "print", "projection", "screen", "speech", "tty", "tv"];
  79. WebInspector.CSSStyleModel.prototype = {
  80. /**
  81. * @param {DOMAgent.NodeId} nodeId
  82. * @param {boolean} needPseudo
  83. * @param {boolean} needInherited
  84. * @param {function(?*)} userCallback
  85. */
  86. getMatchedStylesAsync: function(nodeId, needPseudo, needInherited, userCallback)
  87. {
  88. /**
  89. * @param {function(?*)} userCallback
  90. * @param {?Protocol.Error} error
  91. * @param {Array.<CSSAgent.RuleMatch>=} matchedPayload
  92. * @param {Array.<CSSAgent.PseudoIdMatches>=} pseudoPayload
  93. * @param {Array.<CSSAgent.InheritedStyleEntry>=} inheritedPayload
  94. */
  95. function callback(userCallback, error, matchedPayload, pseudoPayload, inheritedPayload)
  96. {
  97. if (error) {
  98. if (userCallback)
  99. userCallback(null);
  100. return;
  101. }
  102. var result = {};
  103. if (matchedPayload)
  104. result.matchedCSSRules = WebInspector.CSSStyleModel.parseRuleMatchArrayPayload(matchedPayload);
  105. if (pseudoPayload) {
  106. result.pseudoElements = [];
  107. for (var i = 0; i < pseudoPayload.length; ++i) {
  108. var entryPayload = pseudoPayload[i];
  109. result.pseudoElements.push({ pseudoId: entryPayload.pseudoId, rules: WebInspector.CSSStyleModel.parseRuleMatchArrayPayload(entryPayload.matches) });
  110. }
  111. }
  112. if (inheritedPayload) {
  113. result.inherited = [];
  114. for (var i = 0; i < inheritedPayload.length; ++i) {
  115. var entryPayload = inheritedPayload[i];
  116. var entry = {};
  117. if (entryPayload.inlineStyle)
  118. entry.inlineStyle = WebInspector.CSSStyleDeclaration.parsePayload(entryPayload.inlineStyle);
  119. if (entryPayload.matchedCSSRules)
  120. entry.matchedCSSRules = WebInspector.CSSStyleModel.parseRuleMatchArrayPayload(entryPayload.matchedCSSRules);
  121. result.inherited.push(entry);
  122. }
  123. }
  124. if (userCallback)
  125. userCallback(result);
  126. }
  127. CSSAgent.getMatchedStylesForNode(nodeId, needPseudo, needInherited, callback.bind(null, userCallback));
  128. },
  129. /**
  130. * @param {DOMAgent.NodeId} nodeId
  131. * @param {function(?WebInspector.CSSStyleDeclaration)} userCallback
  132. */
  133. getComputedStyleAsync: function(nodeId, userCallback)
  134. {
  135. /**
  136. * @param {function(?WebInspector.CSSStyleDeclaration)} userCallback
  137. */
  138. function callback(userCallback, error, computedPayload)
  139. {
  140. if (error || !computedPayload)
  141. userCallback(null);
  142. else
  143. userCallback(WebInspector.CSSStyleDeclaration.parseComputedStylePayload(computedPayload));
  144. }
  145. CSSAgent.getComputedStyleForNode(nodeId, callback.bind(null, userCallback));
  146. },
  147. /**
  148. * @param {number} nodeId
  149. * @param {function(?String, ?Array.<CSSAgent.PlatformFontUsage>)} callback
  150. */
  151. getPlatformFontsForNode: function(nodeId, callback)
  152. {
  153. function platformFontsCallback(error, cssFamilyName, fonts)
  154. {
  155. if (error)
  156. callback(null, null);
  157. else
  158. callback(cssFamilyName, fonts);
  159. }
  160. CSSAgent.getPlatformFontsForNode(nodeId, platformFontsCallback);
  161. },
  162. /**
  163. * @param {DOMAgent.NodeId} nodeId
  164. * @param {function(?WebInspector.CSSStyleDeclaration, ?WebInspector.CSSStyleDeclaration)} userCallback
  165. */
  166. getInlineStylesAsync: function(nodeId, userCallback)
  167. {
  168. /**
  169. * @param {function(?WebInspector.CSSStyleDeclaration, ?WebInspector.CSSStyleDeclaration)} userCallback
  170. * @param {?Protocol.Error} error
  171. * @param {?CSSAgent.CSSStyle=} inlinePayload
  172. * @param {?CSSAgent.CSSStyle=} attributesStylePayload
  173. */
  174. function callback(userCallback, error, inlinePayload, attributesStylePayload)
  175. {
  176. if (error || !inlinePayload)
  177. userCallback(null, null);
  178. else
  179. userCallback(WebInspector.CSSStyleDeclaration.parsePayload(inlinePayload), attributesStylePayload ? WebInspector.CSSStyleDeclaration.parsePayload(attributesStylePayload) : null);
  180. }
  181. CSSAgent.getInlineStylesForNode(nodeId, callback.bind(null, userCallback));
  182. },
  183. /**
  184. * @param {DOMAgent.NodeId} nodeId
  185. * @param {?Array.<string>|undefined} forcedPseudoClasses
  186. * @param {function()=} userCallback
  187. */
  188. forcePseudoState: function(nodeId, forcedPseudoClasses, userCallback)
  189. {
  190. CSSAgent.forcePseudoState(nodeId, forcedPseudoClasses || [], userCallback);
  191. },
  192. /**
  193. * @param {DOMAgent.NodeId} documentNodeId
  194. * @param {function(?WebInspector.NamedFlowCollection)} userCallback
  195. */
  196. getNamedFlowCollectionAsync: function(documentNodeId, userCallback)
  197. {
  198. var namedFlowCollection = this._namedFlowCollections[documentNodeId];
  199. if (namedFlowCollection) {
  200. userCallback(namedFlowCollection);
  201. return;
  202. }
  203. /**
  204. * @param {function(?WebInspector.NamedFlowCollection)} userCallback
  205. * @param {?Protocol.Error} error
  206. * @param {?Array.<CSSAgent.NamedFlow>} namedFlowPayload
  207. */
  208. function callback(userCallback, error, namedFlowPayload)
  209. {
  210. if (error || !namedFlowPayload)
  211. userCallback(null);
  212. else {
  213. var namedFlowCollection = new WebInspector.NamedFlowCollection(namedFlowPayload);
  214. this._namedFlowCollections[documentNodeId] = namedFlowCollection;
  215. userCallback(namedFlowCollection);
  216. }
  217. }
  218. CSSAgent.getNamedFlowCollection(documentNodeId, callback.bind(this, userCallback));
  219. },
  220. /**
  221. * @param {DOMAgent.NodeId} documentNodeId
  222. * @param {string} flowName
  223. * @param {function(?WebInspector.NamedFlow)} userCallback
  224. */
  225. getFlowByNameAsync: function(documentNodeId, flowName, userCallback)
  226. {
  227. var namedFlowCollection = this._namedFlowCollections[documentNodeId];
  228. if (namedFlowCollection) {
  229. userCallback(namedFlowCollection.flowByName(flowName));
  230. return;
  231. }
  232. /**
  233. * @param {function(?WebInspector.NamedFlow)} userCallback
  234. * @param {?WebInspector.NamedFlowCollection} namedFlowCollection
  235. */
  236. function callback(userCallback, namedFlowCollection)
  237. {
  238. if (!namedFlowCollection)
  239. userCallback(null);
  240. else
  241. userCallback(namedFlowCollection.flowByName(flowName));
  242. }
  243. this.getNamedFlowCollectionAsync(documentNodeId, callback.bind(this, userCallback));
  244. },
  245. /**
  246. * @param {CSSAgent.CSSRuleId} ruleId
  247. * @param {DOMAgent.NodeId} nodeId
  248. * @param {string} newSelector
  249. * @param {function(WebInspector.CSSRule, boolean)} successCallback
  250. * @param {function()} failureCallback
  251. */
  252. setRuleSelector: function(ruleId, nodeId, newSelector, successCallback, failureCallback)
  253. {
  254. /**
  255. * @param {DOMAgent.NodeId} nodeId
  256. * @param {function(WebInspector.CSSRule, boolean)} successCallback
  257. * @param {CSSAgent.CSSRule} rulePayload
  258. * @param {?Array.<DOMAgent.NodeId>} selectedNodeIds
  259. */
  260. function checkAffectsCallback(nodeId, successCallback, rulePayload, selectedNodeIds)
  261. {
  262. if (!selectedNodeIds)
  263. return;
  264. var doesAffectSelectedNode = (selectedNodeIds.indexOf(nodeId) >= 0);
  265. var rule = WebInspector.CSSRule.parsePayload(rulePayload);
  266. successCallback(rule, doesAffectSelectedNode);
  267. }
  268. /**
  269. * @param {DOMAgent.NodeId} nodeId
  270. * @param {function(WebInspector.CSSRule, boolean)} successCallback
  271. * @param {function()} failureCallback
  272. * @param {?Protocol.Error} error
  273. * @param {string} newSelector
  274. * @param {?CSSAgent.CSSRule} rulePayload
  275. */
  276. function callback(nodeId, successCallback, failureCallback, newSelector, error, rulePayload)
  277. {
  278. this._pendingCommandsMajorState.pop();
  279. if (error)
  280. failureCallback();
  281. else {
  282. WebInspector.domAgent.markUndoableState();
  283. var ownerDocumentId = this._ownerDocumentId(nodeId);
  284. if (ownerDocumentId)
  285. WebInspector.domAgent.querySelectorAll(ownerDocumentId, newSelector, checkAffectsCallback.bind(this, nodeId, successCallback, rulePayload));
  286. else
  287. failureCallback();
  288. }
  289. }
  290. this._pendingCommandsMajorState.push(true);
  291. CSSAgent.setRuleSelector(ruleId, newSelector, callback.bind(this, nodeId, successCallback, failureCallback, newSelector));
  292. },
  293. /**
  294. * @param {DOMAgent.NodeId} nodeId
  295. * @param {string} selector
  296. * @param {function(WebInspector.CSSRule, boolean)} successCallback
  297. * @param {function()} failureCallback
  298. */
  299. addRule: function(nodeId, selector, successCallback, failureCallback)
  300. {
  301. /**
  302. * @param {DOMAgent.NodeId} nodeId
  303. * @param {function(WebInspector.CSSRule, boolean)} successCallback
  304. * @param {CSSAgent.CSSRule} rulePayload
  305. * @param {?Array.<DOMAgent.NodeId>} selectedNodeIds
  306. */
  307. function checkAffectsCallback(nodeId, successCallback, rulePayload, selectedNodeIds)
  308. {
  309. if (!selectedNodeIds)
  310. return;
  311. var doesAffectSelectedNode = (selectedNodeIds.indexOf(nodeId) >= 0);
  312. var rule = WebInspector.CSSRule.parsePayload(rulePayload);
  313. successCallback(rule, doesAffectSelectedNode);
  314. }
  315. /**
  316. * @param {function(WebInspector.CSSRule, boolean)} successCallback
  317. * @param {function()} failureCallback
  318. * @param {string} selector
  319. * @param {?Protocol.Error} error
  320. * @param {?CSSAgent.CSSRule} rulePayload
  321. */
  322. function callback(successCallback, failureCallback, selector, error, rulePayload)
  323. {
  324. this._pendingCommandsMajorState.pop();
  325. if (error) {
  326. // Invalid syntax for a selector
  327. failureCallback();
  328. } else {
  329. WebInspector.domAgent.markUndoableState();
  330. var ownerDocumentId = this._ownerDocumentId(nodeId);
  331. if (ownerDocumentId)
  332. WebInspector.domAgent.querySelectorAll(ownerDocumentId, selector, checkAffectsCallback.bind(this, nodeId, successCallback, rulePayload));
  333. else
  334. failureCallback();
  335. }
  336. }
  337. this._pendingCommandsMajorState.push(true);
  338. CSSAgent.addRule(nodeId, selector, callback.bind(this, successCallback, failureCallback, selector));
  339. },
  340. mediaQueryResultChanged: function()
  341. {
  342. this.dispatchEventToListeners(WebInspector.CSSStyleModel.Events.MediaQueryResultChanged);
  343. },
  344. /**
  345. * @param {!CSSAgent.StyleSheetId} id
  346. * @return {WebInspector.CSSStyleSheetHeader}
  347. */
  348. styleSheetHeaderForId: function(id)
  349. {
  350. return this._styleSheetIdToHeader[id];
  351. },
  352. /**
  353. * @return {Array.<WebInspector.CSSStyleSheetHeader>}
  354. */
  355. styleSheetHeaders: function()
  356. {
  357. return Object.values(this._styleSheetIdToHeader);
  358. },
  359. /**
  360. * @param {DOMAgent.NodeId} nodeId
  361. */
  362. _ownerDocumentId: function(nodeId)
  363. {
  364. var node = WebInspector.domAgent.nodeForId(nodeId);
  365. if (!node)
  366. return null;
  367. return node.ownerDocument ? node.ownerDocument.id : null;
  368. },
  369. /**
  370. * @param {CSSAgent.StyleSheetId} styleSheetId
  371. */
  372. _fireStyleSheetChanged: function(styleSheetId)
  373. {
  374. if (!this._pendingCommandsMajorState.length)
  375. return;
  376. var majorChange = this._pendingCommandsMajorState[this._pendingCommandsMajorState.length - 1];
  377. if (!majorChange || !styleSheetId || !this.hasEventListeners(WebInspector.CSSStyleModel.Events.StyleSheetChanged))
  378. return;
  379. this.dispatchEventToListeners(WebInspector.CSSStyleModel.Events.StyleSheetChanged, { styleSheetId: styleSheetId, majorChange: majorChange });
  380. },
  381. /**
  382. * @param {!CSSAgent.CSSStyleSheetHeader} header
  383. */
  384. _styleSheetAdded: function(header)
  385. {
  386. console.assert(!this._styleSheetIdToHeader[header.styleSheetId]);
  387. var styleSheetHeader = new WebInspector.CSSStyleSheetHeader(header);
  388. this._styleSheetIdToHeader[header.styleSheetId] = styleSheetHeader;
  389. var url = styleSheetHeader.resourceURL();
  390. if (!this._styleSheetIdsForURL[url])
  391. this._styleSheetIdsForURL[url] = {};
  392. var frameIdToStyleSheetIds = this._styleSheetIdsForURL[url];
  393. var styleSheetIds = frameIdToStyleSheetIds[styleSheetHeader.frameId];
  394. if (!styleSheetIds) {
  395. styleSheetIds = [];
  396. frameIdToStyleSheetIds[styleSheetHeader.frameId] = styleSheetIds;
  397. }
  398. styleSheetIds.push(styleSheetHeader.id);
  399. this.dispatchEventToListeners(WebInspector.CSSStyleModel.Events.StyleSheetAdded, styleSheetHeader);
  400. },
  401. /**
  402. * @param {!CSSAgent.StyleSheetId} id
  403. */
  404. _styleSheetRemoved: function(id)
  405. {
  406. var header = this._styleSheetIdToHeader[id];
  407. console.assert(header);
  408. delete this._styleSheetIdToHeader[id];
  409. var url = header.resourceURL();
  410. var frameIdToStyleSheetIds = this._styleSheetIdsForURL[url];
  411. frameIdToStyleSheetIds[header.frameId].remove(id);
  412. if (!frameIdToStyleSheetIds[header.frameId].length) {
  413. delete frameIdToStyleSheetIds[header.frameId];
  414. if (!Object.keys(this._styleSheetIdsForURL[url]).length)
  415. delete this._styleSheetIdsForURL[url];
  416. }
  417. this.dispatchEventToListeners(WebInspector.CSSStyleModel.Events.StyleSheetRemoved, header);
  418. },
  419. /**
  420. * @param {string} url
  421. * @return {Array.<CSSAgent.StyleSheetId>}
  422. */
  423. styleSheetIdsForURL: function(url)
  424. {
  425. var frameIdToStyleSheetIds = this._styleSheetIdsForURL[url];
  426. if (!frameIdToStyleSheetIds)
  427. return [];
  428. var result = [];
  429. for (var frameId in frameIdToStyleSheetIds)
  430. result = result.concat(frameIdToStyleSheetIds[frameId]);
  431. return result;
  432. },
  433. /**
  434. * @param {string} url
  435. * @return {Object.<PageAgent.FrameId, Array.<CSSAgent.StyleSheetId>>}
  436. */
  437. styleSheetIdsByFrameIdForURL: function(url)
  438. {
  439. var styleSheetIdsForFrame = this._styleSheetIdsForURL[url];
  440. if (!styleSheetIdsForFrame)
  441. return {};
  442. return styleSheetIdsForFrame;
  443. },
  444. /**
  445. * @param {CSSAgent.NamedFlow} namedFlowPayload
  446. */
  447. _namedFlowCreated: function(namedFlowPayload)
  448. {
  449. var namedFlow = WebInspector.NamedFlow.parsePayload(namedFlowPayload);
  450. var namedFlowCollection = this._namedFlowCollections[namedFlow.documentNodeId];
  451. if (!namedFlowCollection)
  452. return;
  453. namedFlowCollection._appendNamedFlow(namedFlow);
  454. this.dispatchEventToListeners(WebInspector.CSSStyleModel.Events.NamedFlowCreated, namedFlow);
  455. },
  456. /**
  457. * @param {DOMAgent.NodeId} documentNodeId
  458. * @param {string} flowName
  459. */
  460. _namedFlowRemoved: function(documentNodeId, flowName)
  461. {
  462. var namedFlowCollection = this._namedFlowCollections[documentNodeId];
  463. if (!namedFlowCollection)
  464. return;
  465. namedFlowCollection._removeNamedFlow(flowName);
  466. this.dispatchEventToListeners(WebInspector.CSSStyleModel.Events.NamedFlowRemoved, { documentNodeId: documentNodeId, flowName: flowName });
  467. },
  468. /**
  469. * @param {CSSAgent.NamedFlow} namedFlowPayload
  470. */
  471. _regionLayoutUpdated: function(namedFlowPayload)
  472. {
  473. var namedFlow = WebInspector.NamedFlow.parsePayload(namedFlowPayload);
  474. var namedFlowCollection = this._namedFlowCollections[namedFlow.documentNodeId];
  475. if (!namedFlowCollection)
  476. return;
  477. namedFlowCollection._appendNamedFlow(namedFlow);
  478. this.dispatchEventToListeners(WebInspector.CSSStyleModel.Events.RegionLayoutUpdated, namedFlow);
  479. },
  480. /**
  481. * @param {CSSAgent.NamedFlow} namedFlowPayload
  482. */
  483. _regionOversetChanged: function(namedFlowPayload)
  484. {
  485. var namedFlow = WebInspector.NamedFlow.parsePayload(namedFlowPayload);
  486. var namedFlowCollection = this._namedFlowCollections[namedFlow.documentNodeId];
  487. if (!namedFlowCollection)
  488. return;
  489. namedFlowCollection._appendNamedFlow(namedFlow);
  490. this.dispatchEventToListeners(WebInspector.CSSStyleModel.Events.RegionOversetChanged, namedFlow);
  491. },
  492. /**
  493. * @param {CSSAgent.StyleSheetId} styleSheetId
  494. * @param {string} newText
  495. * @param {boolean} majorChange
  496. * @param {function(?string)} userCallback
  497. */
  498. setStyleSheetText: function(styleSheetId, newText, majorChange, userCallback)
  499. {
  500. function callback(error)
  501. {
  502. this._pendingCommandsMajorState.pop();
  503. if (!error && majorChange)
  504. WebInspector.domAgent.markUndoableState();
  505. if (!error && userCallback)
  506. userCallback(error);
  507. }
  508. this._pendingCommandsMajorState.push(majorChange);
  509. CSSAgent.setStyleSheetText(styleSheetId, newText, callback.bind(this));
  510. },
  511. _undoRedoRequested: function()
  512. {
  513. this._pendingCommandsMajorState.push(true);
  514. },
  515. _undoRedoCompleted: function()
  516. {
  517. this._pendingCommandsMajorState.pop();
  518. },
  519. _mainFrameCreatedOrNavigated: function()
  520. {
  521. this._resetStyleSheets();
  522. },
  523. _resetStyleSheets: function()
  524. {
  525. /** @type {!Object.<string, !Object.<PageAgent.FrameId, !Array.<!CSSAgent.StyleSheetId>>>} */
  526. this._styleSheetIdsForURL = {};
  527. /** @type {!Object.<CSSAgent.StyleSheetId, !WebInspector.CSSStyleSheetHeader>} */
  528. this._styleSheetIdToHeader = {};
  529. },
  530. _resetNamedFlowCollections: function()
  531. {
  532. this._namedFlowCollections = {};
  533. },
  534. updateLocations: function()
  535. {
  536. var headers = Object.values(this._styleSheetIdToHeader);
  537. for (var i = 0; i < headers.length; ++i)
  538. headers[i].updateLocations();
  539. },
  540. /**
  541. * @param {CSSAgent.StyleSheetId} styleSheetId
  542. * @param {WebInspector.CSSLocation} rawLocation
  543. * @param {function(WebInspector.UILocation):(boolean|undefined)} updateDelegate
  544. * @return {?WebInspector.LiveLocation}
  545. */
  546. createLiveLocation: function(styleSheetId, rawLocation, updateDelegate)
  547. {
  548. if (!rawLocation)
  549. return null;
  550. var header = this.styleSheetHeaderForId(styleSheetId);
  551. if (!header)
  552. return null;
  553. return header.createLiveLocation(rawLocation, updateDelegate);
  554. },
  555. /**
  556. * @param {WebInspector.CSSLocation} rawLocation
  557. * @return {?WebInspector.UILocation}
  558. */
  559. rawLocationToUILocation: function(rawLocation)
  560. {
  561. var frameIdToSheetIds = this._styleSheetIdsForURL[rawLocation.url];
  562. if (!frameIdToSheetIds)
  563. return null;
  564. var styleSheetIds = [];
  565. for (var frameId in frameIdToSheetIds)
  566. styleSheetIds = styleSheetIds.concat(frameIdToSheetIds[frameId]);
  567. var uiLocation;
  568. for (var i = 0; !uiLocation && i < styleSheetIds.length; ++i) {
  569. var header = this.styleSheetHeaderForId(styleSheetIds[i]);
  570. console.assert(header);
  571. uiLocation = header.rawLocationToUILocation(rawLocation.lineNumber, rawLocation.columnNumber);
  572. }
  573. return uiLocation || null;
  574. },
  575. __proto__: WebInspector.Object.prototype
  576. }
  577. /**
  578. * @constructor
  579. * @extends {WebInspector.LiveLocation}
  580. * @param {WebInspector.CSSLocation} rawLocation
  581. * @param {function(WebInspector.UILocation):(boolean|undefined)} updateDelegate
  582. */
  583. WebInspector.CSSStyleModel.LiveLocation = function(rawLocation, updateDelegate, header)
  584. {
  585. WebInspector.LiveLocation.call(this, rawLocation, updateDelegate);
  586. this._header = header;
  587. }
  588. WebInspector.CSSStyleModel.LiveLocation.prototype = {
  589. /**
  590. * @return {WebInspector.UILocation}
  591. */
  592. uiLocation: function()
  593. {
  594. var cssLocation = /** @type WebInspector.CSSLocation */ (this.rawLocation());
  595. return this._header.rawLocationToUILocation(cssLocation.lineNumber, cssLocation.columnNumber);
  596. },
  597. dispose: function()
  598. {
  599. WebInspector.LiveLocation.prototype.dispose.call(this);
  600. this._header._removeLocation(this);
  601. },
  602. __proto__: WebInspector.LiveLocation.prototype
  603. }
  604. /**
  605. * @constructor
  606. * @implements {WebInspector.RawLocation}
  607. * @param {string} url
  608. * @param {number} lineNumber
  609. * @param {number=} columnNumber
  610. */
  611. WebInspector.CSSLocation = function(url, lineNumber, columnNumber)
  612. {
  613. this.url = url;
  614. this.lineNumber = lineNumber;
  615. this.columnNumber = columnNumber || 0;
  616. }
  617. /**
  618. * @constructor
  619. * @param {CSSAgent.CSSStyle} payload
  620. */
  621. WebInspector.CSSStyleDeclaration = function(payload)
  622. {
  623. this.id = payload.styleId;
  624. this.width = payload.width;
  625. this.height = payload.height;
  626. this.range = payload.range;
  627. this._shorthandValues = WebInspector.CSSStyleDeclaration.buildShorthandValueMap(payload.shorthandEntries);
  628. this._livePropertyMap = {}; // LIVE properties (source-based or style-based) : { name -> CSSProperty }
  629. this._allProperties = []; // ALL properties: [ CSSProperty ]
  630. this.__disabledProperties = {}; // DISABLED properties: { index -> CSSProperty }
  631. var payloadPropertyCount = payload.cssProperties.length;
  632. var propertyIndex = 0;
  633. for (var i = 0; i < payloadPropertyCount; ++i) {
  634. var property = WebInspector.CSSProperty.parsePayload(this, i, payload.cssProperties[i]);
  635. this._allProperties.push(property);
  636. if (property.disabled)
  637. this.__disabledProperties[i] = property;
  638. if (!property.active && !property.styleBased)
  639. continue;
  640. var name = property.name;
  641. this[propertyIndex] = name;
  642. this._livePropertyMap[name] = property;
  643. ++propertyIndex;
  644. }
  645. this.length = propertyIndex;
  646. if ("cssText" in payload)
  647. this.cssText = payload.cssText;
  648. }
  649. /**
  650. * @param {Array.<CSSAgent.ShorthandEntry>} shorthandEntries
  651. * @return {Object}
  652. */
  653. WebInspector.CSSStyleDeclaration.buildShorthandValueMap = function(shorthandEntries)
  654. {
  655. var result = {};
  656. for (var i = 0; i < shorthandEntries.length; ++i)
  657. result[shorthandEntries[i].name] = shorthandEntries[i].value;
  658. return result;
  659. }
  660. /**
  661. * @param {CSSAgent.CSSStyle} payload
  662. * @return {WebInspector.CSSStyleDeclaration}
  663. */
  664. WebInspector.CSSStyleDeclaration.parsePayload = function(payload)
  665. {
  666. return new WebInspector.CSSStyleDeclaration(payload);
  667. }
  668. /**
  669. * @param {Array.<CSSAgent.CSSComputedStyleProperty>} payload
  670. * @return {WebInspector.CSSStyleDeclaration}
  671. */
  672. WebInspector.CSSStyleDeclaration.parseComputedStylePayload = function(payload)
  673. {
  674. var newPayload = /** @type {CSSAgent.CSSStyle} */ ({ cssProperties: [], shorthandEntries: [], width: "", height: "" });
  675. if (payload)
  676. newPayload.cssProperties = payload;
  677. return new WebInspector.CSSStyleDeclaration(newPayload);
  678. }
  679. WebInspector.CSSStyleDeclaration.prototype = {
  680. get allProperties()
  681. {
  682. return this._allProperties;
  683. },
  684. /**
  685. * @param {string} name
  686. * @return {WebInspector.CSSProperty|undefined}
  687. */
  688. getLiveProperty: function(name)
  689. {
  690. return this._livePropertyMap[name];
  691. },
  692. /**
  693. * @param {string} name
  694. * @return {string}
  695. */
  696. getPropertyValue: function(name)
  697. {
  698. var property = this._livePropertyMap[name];
  699. return property ? property.value : "";
  700. },
  701. /**
  702. * @param {string} name
  703. * @return {string}
  704. */
  705. getPropertyPriority: function(name)
  706. {
  707. var property = this._livePropertyMap[name];
  708. return property ? property.priority : "";
  709. },
  710. /**
  711. * @param {string} name
  712. * @return {boolean}
  713. */
  714. isPropertyImplicit: function(name)
  715. {
  716. var property = this._livePropertyMap[name];
  717. return property ? property.implicit : "";
  718. },
  719. /**
  720. * @param {string} name
  721. * @return {Array.<WebInspector.CSSProperty>}
  722. */
  723. longhandProperties: function(name)
  724. {
  725. var longhands = WebInspector.CSSMetadata.cssPropertiesMetainfo.longhands(name);
  726. var result = [];
  727. for (var i = 0; longhands && i < longhands.length; ++i) {
  728. var property = this._livePropertyMap[longhands[i]];
  729. if (property)
  730. result.push(property);
  731. }
  732. return result;
  733. },
  734. /**
  735. * @param {string} shorthandProperty
  736. * @return {string}
  737. */
  738. shorthandValue: function(shorthandProperty)
  739. {
  740. return this._shorthandValues[shorthandProperty];
  741. },
  742. /**
  743. * @param {number} index
  744. * @return {?WebInspector.CSSProperty}
  745. */
  746. propertyAt: function(index)
  747. {
  748. return (index < this.allProperties.length) ? this.allProperties[index] : null;
  749. },
  750. /**
  751. * @return {number}
  752. */
  753. pastLastSourcePropertyIndex: function()
  754. {
  755. for (var i = this.allProperties.length - 1; i >= 0; --i) {
  756. var property = this.allProperties[i];
  757. if (property.active || property.disabled)
  758. return i + 1;
  759. }
  760. return 0;
  761. },
  762. /**
  763. * @param {number=} index
  764. */
  765. newBlankProperty: function(index)
  766. {
  767. index = (typeof index === "undefined") ? this.pastLastSourcePropertyIndex() : index;
  768. return new WebInspector.CSSProperty(this, index, "", "", "", "active", true, false, "");
  769. },
  770. /**
  771. * @param {number} index
  772. * @param {string} name
  773. * @param {string} value
  774. * @param {function(?WebInspector.CSSStyleDeclaration)=} userCallback
  775. */
  776. insertPropertyAt: function(index, name, value, userCallback)
  777. {
  778. /**
  779. * @param {?string} error
  780. * @param {CSSAgent.CSSStyle} payload
  781. */
  782. function callback(error, payload)
  783. {
  784. WebInspector.cssModel._pendingCommandsMajorState.pop();
  785. if (!userCallback)
  786. return;
  787. if (error) {
  788. console.error(error);
  789. userCallback(null);
  790. } else
  791. userCallback(WebInspector.CSSStyleDeclaration.parsePayload(payload));
  792. }
  793. if (!this.id)
  794. throw "No style id";
  795. WebInspector.cssModel._pendingCommandsMajorState.push(true);
  796. CSSAgent.setPropertyText(this.id, index, name + ": " + value + ";", false, callback.bind(this));
  797. },
  798. /**
  799. * @param {string} name
  800. * @param {string} value
  801. * @param {function(?WebInspector.CSSStyleDeclaration)=} userCallback
  802. */
  803. appendProperty: function(name, value, userCallback)
  804. {
  805. this.insertPropertyAt(this.allProperties.length, name, value, userCallback);
  806. },
  807. /**
  808. * @param {string} text
  809. * @param {function(?WebInspector.CSSStyleDeclaration)=} userCallback
  810. */
  811. setText: function(text, userCallback)
  812. {
  813. /**
  814. * @param {?string} error
  815. * @param {CSSAgent.CSSStyle} payload
  816. */
  817. function callback(error, payload)
  818. {
  819. WebInspector.cssModel._pendingCommandsMajorState.pop();
  820. if (!userCallback)
  821. return;
  822. if (error) {
  823. console.error(error);
  824. userCallback(null);
  825. } else
  826. userCallback(WebInspector.CSSStyleDeclaration.parsePayload(payload));
  827. }
  828. if (!this.id)
  829. throw "No style id";
  830. if (typeof this.cssText === "undefined") {
  831. userCallback(null);
  832. return;
  833. }
  834. WebInspector.cssModel._pendingCommandsMajorState.push(true);
  835. CSSAgent.setStyleText(this.id, text, callback);
  836. }
  837. }
  838. /**
  839. * @constructor
  840. * @param {CSSAgent.CSSRule} payload
  841. * @param {Array.<number>=} matchingSelectors
  842. */
  843. WebInspector.CSSRule = function(payload, matchingSelectors)
  844. {
  845. this.id = payload.ruleId;
  846. if (matchingSelectors)
  847. this.matchingSelectors = matchingSelectors;
  848. this.selectors = payload.selectorList.selectors;
  849. this.selectorText = this.selectors.join(", ");
  850. this.selectorRange = payload.selectorList.range;
  851. this.sourceURL = payload.sourceURL;
  852. this.origin = payload.origin;
  853. this.style = WebInspector.CSSStyleDeclaration.parsePayload(payload.style);
  854. this.style.parentRule = this;
  855. if (payload.media)
  856. this.media = WebInspector.CSSMedia.parseMediaArrayPayload(payload.media);
  857. this._setRawLocationAndFrameId();
  858. }
  859. /**
  860. * @param {CSSAgent.CSSRule} payload
  861. * @param {Array.<number>=} matchingIndices
  862. * @return {WebInspector.CSSRule}
  863. */
  864. WebInspector.CSSRule.parsePayload = function(payload, matchingIndices)
  865. {
  866. return new WebInspector.CSSRule(payload, matchingIndices);
  867. }
  868. WebInspector.CSSRule.prototype = {
  869. _setRawLocationAndFrameId: function()
  870. {
  871. if (!this.id)
  872. return;
  873. var styleSheetHeader = WebInspector.cssModel.styleSheetHeaderForId(this.id.styleSheetId);
  874. this.frameId = styleSheetHeader.frameId;
  875. var url = styleSheetHeader.resourceURL();
  876. if (!url)
  877. return;
  878. this.rawLocation = new WebInspector.CSSLocation(url, this.lineNumberInSource(), this.columnNumberInSource());
  879. },
  880. /**
  881. * @return {string}
  882. */
  883. resourceURL: function()
  884. {
  885. if (!this.id)
  886. return "";
  887. var styleSheetHeader = WebInspector.cssModel.styleSheetHeaderForId(this.id.styleSheetId);
  888. return styleSheetHeader.resourceURL();
  889. },
  890. /**
  891. * @return {number}
  892. */
  893. lineNumberInSource: function()
  894. {
  895. if (!this.selectorRange)
  896. return 0;
  897. var styleSheetHeader = WebInspector.cssModel.styleSheetHeaderForId(this.id.styleSheetId);
  898. return styleSheetHeader.lineNumberInSource(this.selectorRange.startLine);
  899. },
  900. /**
  901. * @return {number|undefined}
  902. */
  903. columnNumberInSource: function()
  904. {
  905. if (!this.selectorRange)
  906. return undefined;
  907. var styleSheetHeader = WebInspector.cssModel.styleSheetHeaderForId(this.id.styleSheetId);
  908. console.assert(styleSheetHeader);
  909. return styleSheetHeader.columnNumberInSource(this.selectorRange.startLine, this.selectorRange.startColumn);
  910. },
  911. get isUserAgent()
  912. {
  913. return this.origin === "user-agent";
  914. },
  915. get isUser()
  916. {
  917. return this.origin === "user";
  918. },
  919. get isViaInspector()
  920. {
  921. return this.origin === "inspector";
  922. },
  923. get isRegular()
  924. {
  925. return this.origin === "regular";
  926. }
  927. }
  928. /**
  929. * @constructor
  930. * @param {?WebInspector.CSSStyleDeclaration} ownerStyle
  931. * @param {number} index
  932. * @param {string} name
  933. * @param {string} value
  934. * @param {?string} priority
  935. * @param {string} status
  936. * @param {boolean} parsedOk
  937. * @param {boolean} implicit
  938. * @param {?string=} text
  939. * @param {CSSAgent.SourceRange=} range
  940. */
  941. WebInspector.CSSProperty = function(ownerStyle, index, name, value, priority, status, parsedOk, implicit, text, range)
  942. {
  943. this.ownerStyle = ownerStyle;
  944. this.index = index;
  945. this.name = name;
  946. this.value = value;
  947. this.priority = priority;
  948. this.status = status;
  949. this.parsedOk = parsedOk;
  950. this.implicit = implicit;
  951. this.text = text;
  952. this.range = range;
  953. }
  954. /**
  955. * @param {?WebInspector.CSSStyleDeclaration} ownerStyle
  956. * @param {number} index
  957. * @param {CSSAgent.CSSProperty} payload
  958. * @return {WebInspector.CSSProperty}
  959. */
  960. WebInspector.CSSProperty.parsePayload = function(ownerStyle, index, payload)
  961. {
  962. // The following default field values are used in the payload:
  963. // priority: ""
  964. // parsedOk: true
  965. // implicit: false
  966. // status: "style"
  967. var result = new WebInspector.CSSProperty(
  968. ownerStyle, index, payload.name, payload.value, payload.priority || "", payload.status || "style", ("parsedOk" in payload) ? !!payload.parsedOk : true, !!payload.implicit, payload.text, payload.range);
  969. return result;
  970. }
  971. WebInspector.CSSProperty.prototype = {
  972. get propertyText()
  973. {
  974. if (this.text !== undefined)
  975. return this.text;
  976. if (this.name === "")
  977. return "";
  978. return this.name + ": " + this.value + (this.priority ? " !" + this.priority : "") + ";";
  979. },
  980. get isLive()
  981. {
  982. return this.active || this.styleBased;
  983. },
  984. get active()
  985. {
  986. return this.status === "active";
  987. },
  988. get styleBased()
  989. {
  990. return this.status === "style";
  991. },
  992. get inactive()
  993. {
  994. return this.status === "inactive";
  995. },
  996. get disabled()
  997. {
  998. return this.status === "disabled";
  999. },
  1000. /**
  1001. * Replaces "propertyName: propertyValue [!important];" in the stylesheet by an arbitrary propertyText.
  1002. *
  1003. * @param {string} propertyText
  1004. * @param {boolean} majorChange
  1005. * @param {boolean} overwrite
  1006. * @param {function(?WebInspector.CSSStyleDeclaration)=} userCallback
  1007. */
  1008. setText: function(propertyText, majorChange, overwrite, userCallback)
  1009. {
  1010. /**
  1011. * @param {?WebInspector.CSSStyleDeclaration} style
  1012. */
  1013. function enabledCallback(style)
  1014. {
  1015. if (userCallback)
  1016. userCallback(style);
  1017. }
  1018. /**
  1019. * @param {?string} error
  1020. * @param {?CSSAgent.CSSStyle} stylePayload
  1021. */
  1022. function callback(error, stylePayload)
  1023. {
  1024. WebInspector.cssModel._pendingCommandsMajorState.pop();
  1025. if (!error) {
  1026. if (majorChange)
  1027. WebInspector.domAgent.markUndoableState();
  1028. this.text = propertyText;
  1029. var style = WebInspector.CSSStyleDeclaration.parsePayload(stylePayload);
  1030. var newProperty = style.allProperties[this.index];
  1031. if (newProperty && this.disabled && !propertyText.match(/^\s*$/)) {
  1032. newProperty.setDisabled(false, enabledCallback);
  1033. return;
  1034. }
  1035. if (userCallback)
  1036. userCallback(style);
  1037. } else {
  1038. if (userCallback)
  1039. userCallback(null);
  1040. }
  1041. }
  1042. if (!this.ownerStyle)
  1043. throw "No ownerStyle for property";
  1044. if (!this.ownerStyle.id)
  1045. throw "No owner style id";
  1046. // An index past all the properties adds a new property to the style.
  1047. WebInspector.cssModel._pendingCommandsMajorState.push(majorChange);
  1048. CSSAgent.setPropertyText(this.ownerStyle.id, this.index, propertyText, overwrite, callback.bind(this));
  1049. },
  1050. /**
  1051. * @param {string} newValue
  1052. * @param {boolean} majorChange
  1053. * @param {boolean} overwrite
  1054. * @param {function(?WebInspector.CSSStyleDeclaration)=} userCallback
  1055. */
  1056. setValue: function(newValue, majorChange, overwrite, userCallback)
  1057. {
  1058. var text = this.name + ": " + newValue + (this.priority ? " !" + this.priority : "") + ";"
  1059. this.setText(text, majorChange, overwrite, userCallback);
  1060. },
  1061. /**
  1062. * @param {boolean} disabled
  1063. * @param {function(?WebInspector.CSSStyleDeclaration)=} userCallback
  1064. */
  1065. setDisabled: function(disabled, userCallback)
  1066. {
  1067. if (!this.ownerStyle && userCallback)
  1068. userCallback(null);
  1069. if (disabled === this.disabled && userCallback)
  1070. userCallback(this.ownerStyle);
  1071. /**
  1072. * @param {?string} error
  1073. * @param {CSSAgent.CSSStyle} stylePayload
  1074. */
  1075. function callback(error, stylePayload)
  1076. {
  1077. WebInspector.cssModel._pendingCommandsMajorState.pop();
  1078. if (error) {
  1079. if (userCallback)
  1080. userCallback(null);
  1081. return;
  1082. }
  1083. WebInspector.domAgent.markUndoableState();
  1084. if (userCallback) {
  1085. var style = WebInspector.CSSStyleDeclaration.parsePayload(stylePayload);
  1086. userCallback(style);
  1087. }
  1088. }
  1089. if (!this.ownerStyle.id)
  1090. throw "No owner style id";
  1091. WebInspector.cssModel._pendingCommandsMajorState.push(false);
  1092. CSSAgent.toggleProperty(this.ownerStyle.id, this.index, disabled, callback.bind(this));
  1093. },
  1094. /**
  1095. * @param {boolean} forName
  1096. * @return {WebInspector.UILocation}
  1097. */
  1098. uiLocation: function(forName)
  1099. {
  1100. if (!this.range || !this.ownerStyle || !this.ownerStyle.parentRule)
  1101. return null;
  1102. var url = this.ownerStyle.parentRule.resourceURL();
  1103. if (!url)
  1104. return null;
  1105. var range = this.range;
  1106. var line = forName ? range.startLine : range.endLine;
  1107. // End of range is exclusive, so subtract 1 from the end offset.
  1108. var column = forName ? range.startColumn : range.endColumn - (this.text && this.text.endsWith(";") ? 2 : 1);
  1109. var rawLocation = new WebInspector.CSSLocation(url, line, column);
  1110. return WebInspector.cssModel.rawLocationToUILocation(rawLocation);
  1111. }
  1112. }
  1113. /**
  1114. * @constructor
  1115. * @param {CSSAgent.CSSMedia} payload
  1116. */
  1117. WebInspector.CSSMedia = function(payload)
  1118. {
  1119. this.text = payload.text;
  1120. this.source = payload.source;
  1121. this.sourceURL = payload.sourceURL || "";
  1122. this.range = payload.range;
  1123. this.parentStyleSheetId = payload.parentStyleSheetId;
  1124. }
  1125. WebInspector.CSSMedia.Source = {
  1126. LINKED_SHEET: "linkedSheet",
  1127. INLINE_SHEET: "inlineSheet",
  1128. MEDIA_RULE: "mediaRule",
  1129. IMPORT_RULE: "importRule"
  1130. };
  1131. /**
  1132. * @param {CSSAgent.CSSMedia} payload
  1133. * @return {WebInspector.CSSMedia}
  1134. */
  1135. WebInspector.CSSMedia.parsePayload = function(payload)
  1136. {
  1137. return new WebInspector.CSSMedia(payload);
  1138. }
  1139. /**
  1140. * @param {Array.<CSSAgent.CSSMedia>} payload
  1141. * @return {Array.<WebInspector.CSSMedia>}
  1142. */
  1143. WebInspector.CSSMedia.parseMediaArrayPayload = function(payload)
  1144. {
  1145. var result = [];
  1146. for (var i = 0; i < payload.length; ++i)
  1147. result.push(WebInspector.CSSMedia.parsePayload(payload[i]));
  1148. return result;
  1149. }
  1150. WebInspector.CSSMedia.prototype = {
  1151. /**
  1152. * @return {number|undefined}
  1153. */
  1154. lineNumberInSource: function()
  1155. {
  1156. if (!this.range)
  1157. return undefined;
  1158. var header = this.header();
  1159. if (!header)
  1160. return undefined;
  1161. return header.lineNumberInSource(this.range.startLine);
  1162. },
  1163. /**
  1164. * @return {number|undefined}
  1165. */
  1166. columnNumberInSource: function()
  1167. {
  1168. if (!this.range)
  1169. return undefined;
  1170. var header = this.header();
  1171. if (!header)
  1172. return undefined;
  1173. return header.columnNumberInSource(this.range.startLine, this.range.startColumn);
  1174. },
  1175. /**
  1176. * @return {?WebInspector.CSSStyleSheetHeader}
  1177. */
  1178. header: function()
  1179. {
  1180. return this.parentStyleSheetId ? WebInspector.cssModel.styleSheetHeaderForId(this.parentStyleSheetId) : null;
  1181. }
  1182. }
  1183. /**
  1184. * @constructor
  1185. * @implements {WebInspector.ContentProvider}
  1186. * @param {CSSAgent.CSSStyleSheetHeader} payload
  1187. */
  1188. WebInspector.CSSStyleSheetHeader = function(payload)
  1189. {
  1190. this.id = payload.styleSheetId;
  1191. this.frameId = payload.frameId;
  1192. this.sourceURL = payload.sourceURL;
  1193. this.hasSourceURL = !!payload.hasSourceURL;
  1194. this.sourceMapURL = payload.sourceMapURL;
  1195. this.origin = payload.origin;
  1196. this.title = payload.title;
  1197. this.disabled = payload.disabled;
  1198. this.isInline = payload.isInline;
  1199. this.startLine = payload.startLine;
  1200. this.startColumn = payload.startColumn;
  1201. /** @type {!Set.<!WebInspector.CSSStyleModel.LiveLocation>} */
  1202. this._locations = new Set();
  1203. /** @type {!Array.<!WebInspector.SourceMapping>} */
  1204. this._sourceMappings = [];
  1205. }
  1206. WebInspector.CSSStyleSheetHeader.prototype = {
  1207. /**
  1208. * @return {string}
  1209. */
  1210. resourceURL: function()
  1211. {
  1212. return this.origin === "inspector" ? this._viaInspectorResourceURL() : this.sourceURL;
  1213. },
  1214. /**
  1215. * @param {WebInspector.CSSLocation} rawLocation
  1216. * @param {function(WebInspector.UILocation):(boolean|undefined)} updateDelegate
  1217. * @return {?WebInspector.LiveLocation}
  1218. */
  1219. createLiveLocation: function(rawLocation, updateDelegate)
  1220. {
  1221. var location = new WebInspector.CSSStyleModel.LiveLocation(rawLocation, updateDelegate, this);
  1222. this._locations.add(location);
  1223. location.update();
  1224. return location;
  1225. },
  1226. updateLocations: function()
  1227. {
  1228. var items = this._locations.items();
  1229. for (var i = 0; i < items.length; ++i)
  1230. items[i].update();
  1231. },
  1232. /**
  1233. * @param {!WebInspector.CSSStyleModel.LiveLocation} location
  1234. */
  1235. _removeLocation: function(location)
  1236. {
  1237. this._locations.remove(location);
  1238. },
  1239. /**
  1240. * @param {number} lineNumber
  1241. * @param {number=} columnNumber
  1242. * @return {?WebInspector.UILocation}
  1243. */
  1244. rawLocationToUILocation: function(lineNumber, columnNumber)
  1245. {
  1246. var uiLocation;
  1247. var rawLocation = new WebInspector.CSSLocation(this.resourceURL(), lineNumber, columnNumber || 0);
  1248. for (var i = this._sourceMappings.length - 1; !uiLocation && i >= 0; --i)
  1249. uiLocation = this._sourceMappings[i].rawLocationToUILocation(rawLocation);
  1250. return uiLocation || null;
  1251. },
  1252. /**
  1253. * @param {!WebInspector.SourceMapping} sourceMapping
  1254. */
  1255. pushSourceMapping: function(sourceMapping)
  1256. {
  1257. this._sourceMappings.push(sourceMapping);
  1258. this.updateLocations();
  1259. },
  1260. /**
  1261. * @return {string}
  1262. */
  1263. _key: function()
  1264. {
  1265. return this.frameId + ":" + this.resourceURL();
  1266. },
  1267. /**
  1268. * @return {string}
  1269. */
  1270. _viaInspectorResourceURL: function()
  1271. {
  1272. var frame = WebInspector.resourceTreeModel.frameForId(this.frameId);
  1273. console.assert(frame);
  1274. var parsedURL = new WebInspector.ParsedURL(frame.url);
  1275. var fakeURL = "inspector://" + parsedURL.host + parsedURL.folderPathComponents;
  1276. if (!fakeURL.endsWith("/"))
  1277. fakeURL += "/";
  1278. fakeURL += "inspector-stylesheet";
  1279. return fakeURL;
  1280. },
  1281. /**
  1282. * @param {number} lineNumberInStyleSheet
  1283. * @return {number}
  1284. */
  1285. lineNumberInSource: function(lineNumberInStyleSheet)
  1286. {
  1287. return this.startLine + lineNumberInStyleSheet;
  1288. },
  1289. /**
  1290. * @param {number} lineNumberInStyleSheet
  1291. * @param {number} columnNumberInStyleSheet
  1292. * @return {number|undefined}
  1293. */
  1294. columnNumberInSource: function(lineNumberInStyleSheet, columnNumberInStyleSheet)
  1295. {
  1296. return (lineNumberInStyleSheet ? 0 : this.startColumn) + columnNumberInStyleSheet;
  1297. },
  1298. /**
  1299. * @override
  1300. */
  1301. contentURL: function()
  1302. {
  1303. return this.resourceURL();
  1304. },
  1305. /**
  1306. * @override
  1307. */
  1308. contentType: function()
  1309. {
  1310. return WebInspector.resourceTypes.Stylesheet;
  1311. },
  1312. /**
  1313. * @override
  1314. */
  1315. requestContent: function(callback)
  1316. {
  1317. CSSAgent.getStyleSheetText(this.id, textCallback.bind(this));
  1318. function textCallback(error, text)
  1319. {
  1320. if (error) {
  1321. WebInspector.log("Failed to get text for stylesheet " + this.id + ": " + error);
  1322. text = "";
  1323. // Fall through.
  1324. }
  1325. callback(text, false, "text/css");
  1326. }
  1327. },
  1328. /**
  1329. * @override
  1330. */
  1331. searchInContent: function(query, caseSensitive, isRegex, callback)
  1332. {
  1333. function performSearch(content)
  1334. {
  1335. callback(WebInspector.ContentProvider.performSearchInContent(content, query, caseSensitive, isRegex));
  1336. }
  1337. // searchInContent should call back later.
  1338. this.requestContent(performSearch);
  1339. }
  1340. }
  1341. /**
  1342. * @constructor
  1343. * @param {CSSAgent.CSSStyleSheetBody} payload
  1344. */
  1345. WebInspector.CSSStyleSheet = function(payload)
  1346. {
  1347. this.id = payload.styleSheetId;
  1348. this.rules = [];
  1349. this.styles = {};
  1350. for (var i = 0; i < payload.rules.length; ++i) {
  1351. var rule = WebInspector.CSSRule.parsePayload(payload.rules[i]);
  1352. this.rules.push(rule);
  1353. if (rule.style)
  1354. this.styles[rule.style.id] = rule.style;
  1355. }
  1356. if ("text" in payload)
  1357. this._text = payload.text;
  1358. }
  1359. /**
  1360. * @param {CSSAgent.StyleSheetId} styleSheetId
  1361. * @param {function(?WebInspector.CSSStyleSheet)} userCallback
  1362. */
  1363. WebInspector.CSSStyleSheet.createForId = function(styleSheetId, userCallback)
  1364. {
  1365. /**
  1366. * @param {?string} error
  1367. * @param {CSSAgent.CSSStyleSheetBody} styleSheetPayload
  1368. */
  1369. function callback(error, styleSheetPayload)
  1370. {
  1371. if (error)
  1372. userCallback(null);
  1373. else
  1374. userCallback(new WebInspector.CSSStyleSheet(styleSheetPayload));
  1375. }
  1376. CSSAgent.getStyleSheet(styleSheetId, callback.bind(this));
  1377. }
  1378. WebInspector.CSSStyleSheet.prototype = {
  1379. /**
  1380. * @return {string|undefined}
  1381. */
  1382. getText: function()
  1383. {
  1384. return this._text;
  1385. },
  1386. /**
  1387. * @param {string} newText
  1388. * @param {boolean} majorChange
  1389. * @param {function(?string)=} userCallback
  1390. */
  1391. setText: function(newText, majorChange, userCallback)
  1392. {
  1393. /**
  1394. * @param {?string} error
  1395. */
  1396. function callback(error)
  1397. {
  1398. if (!error)
  1399. WebInspector.domAgent.markUndoableState();
  1400. WebInspector.cssModel._pendingCommandsMajorState.pop();
  1401. if (userCallback)
  1402. userCallback(error);
  1403. }
  1404. WebInspector.cssModel._pendingCommandsMajorState.push(majorChange);
  1405. CSSAgent.setStyleSheetText(this.id, newText, callback.bind(this));
  1406. }
  1407. }
  1408. /**
  1409. * @constructor
  1410. * @implements {CSSAgent.Dispatcher}
  1411. * @param {WebInspector.CSSStyleModel} cssModel
  1412. */
  1413. WebInspector.CSSDispatcher = function(cssModel)
  1414. {
  1415. this._cssModel = cssModel;
  1416. }
  1417. WebInspector.CSSDispatcher.prototype = {
  1418. mediaQueryResultChanged: function()
  1419. {
  1420. this._cssModel.mediaQueryResultChanged();
  1421. },
  1422. /**
  1423. * @param {CSSAgent.StyleSheetId} styleSheetId
  1424. */
  1425. styleSheetChanged: function(styleSheetId)
  1426. {
  1427. this._cssModel._fireStyleSheetChanged(styleSheetId);
  1428. },
  1429. /**
  1430. * @param {CSSAgent.CSSStyleSheetHeader} header
  1431. */
  1432. styleSheetAdded: function(header)
  1433. {
  1434. this._cssModel._styleSheetAdded(header);
  1435. },
  1436. /**
  1437. * @param {CSSAgent.StyleSheetId} id
  1438. */
  1439. styleSheetRemoved: function(id)
  1440. {
  1441. this._cssModel._styleSheetRemoved(id);
  1442. },
  1443. /**
  1444. * @param {CSSAgent.NamedFlow} namedFlowPayload
  1445. */
  1446. namedFlowCreated: function(namedFlowPayload)
  1447. {
  1448. this._cssModel._namedFlowCreated(namedFlowPayload);
  1449. },
  1450. /**
  1451. * @param {DOMAgent.NodeId} documentNodeId
  1452. * @param {string} flowName
  1453. */
  1454. namedFlowRemoved: function(documentNodeId, flowName)
  1455. {
  1456. this._cssModel._namedFlowRemoved(documentNodeId, flowName);
  1457. },
  1458. /**
  1459. * @param {CSSAgent.NamedFlow} namedFlowPayload
  1460. */
  1461. regionLayoutUpdated: function(namedFlowPayload)
  1462. {
  1463. this._cssModel._regionLayoutUpdated(namedFlowPayload);
  1464. },
  1465. /**
  1466. * @param {CSSAgent.NamedFlow} namedFlowPayload
  1467. */
  1468. regionOversetChanged: function(namedFlowPayload)
  1469. {
  1470. this._cssModel._regionOversetChanged(namedFlowPayload);
  1471. }
  1472. }
  1473. /**
  1474. * @constructor
  1475. * @param {CSSAgent.NamedFlow} payload
  1476. */
  1477. WebInspector.NamedFlow = function(payload)
  1478. {
  1479. this.documentNodeId = payload.documentNodeId;
  1480. this.name = payload.name;
  1481. this.overset = payload.overset;
  1482. this.content = payload.content;
  1483. this.regions = payload.regions;
  1484. }
  1485. /**
  1486. * @param {CSSAgent.NamedFlow} payload
  1487. * @return {WebInspector.NamedFlow}
  1488. */
  1489. WebInspector.NamedFlow.parsePayload = function(payload)
  1490. {
  1491. return new WebInspector.NamedFlow(payload);
  1492. }
  1493. /**
  1494. * @constructor
  1495. * @param {Array.<CSSAgent.NamedFlow>} payload
  1496. */
  1497. WebInspector.NamedFlowCollection = function(payload)
  1498. {
  1499. /** @type {Object.<string, WebInspector.NamedFlow>} */
  1500. this.namedFlowMap = {};
  1501. for (var i = 0; i < payload.length; ++i) {
  1502. var namedFlow = WebInspector.NamedFlow.parsePayload(payload[i]);
  1503. this.namedFlowMap[namedFlow.name] = namedFlow;
  1504. }
  1505. }
  1506. WebInspector.NamedFlowCollection.prototype = {
  1507. /**
  1508. * @param {WebInspector.NamedFlow} namedFlow
  1509. */
  1510. _appendNamedFlow: function(namedFlow)
  1511. {
  1512. this.namedFlowMap[namedFlow.name] = namedFlow;
  1513. },
  1514. /**
  1515. * @param {string} flowName
  1516. */
  1517. _removeNamedFlow: function(flowName)
  1518. {
  1519. delete this.namedFlowMap[flowName];
  1520. },
  1521. /**
  1522. * @param {string} flowName
  1523. * @return {WebInspector.NamedFlow}
  1524. */
  1525. flowByName: function(flowName)
  1526. {
  1527. var namedFlow = this.namedFlowMap[flowName];
  1528. if (!namedFlow)
  1529. return null;
  1530. return namedFlow;
  1531. }
  1532. }
  1533. /**
  1534. * @type {WebInspector.CSSStyleModel}
  1535. */
  1536. WebInspector.cssModel = null;