PageRenderTime 26ms CodeModel.GetById 1ms RepoModel.GetById 0ms app.codeStats 0ms

/Source/core/css/CSSStyleSheet.cpp

https://repo.or.cz/blink.git
C++ | 460 lines | 351 code | 71 blank | 38 comment | 53 complexity | b7b6b3a6d551a80ebbf9efbf2a90ac8e MD5 | raw file
Possible License(s): BSD-3-Clause, Unlicense, AGPL-1.0, Apache-2.0
  1. /*
  2. * (C) 1999-2003 Lars Knoll (knoll@kde.org)
  3. * Copyright (C) 2004, 2006, 2007, 2012 Apple Inc. All rights reserved.
  4. *
  5. * This library is free software; you can redistribute it and/or
  6. * modify it under the terms of the GNU Library General Public
  7. * License as published by the Free Software Foundation; either
  8. * version 2 of the License, or (at your option) any later version.
  9. *
  10. * This library is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  13. * Library General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU Library General Public License
  16. * along with this library; see the file COPYING.LIB. If not, write to
  17. * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
  18. * Boston, MA 02110-1301, USA.
  19. */
  20. #include "config.h"
  21. #include "core/css/CSSStyleSheet.h"
  22. #include "bindings/core/v8/ExceptionState.h"
  23. #include "bindings/core/v8/V8Binding.h"
  24. #include "bindings/core/v8/V8PerIsolateData.h"
  25. #include "core/HTMLNames.h"
  26. #include "core/SVGNames.h"
  27. #include "core/css/CSSImportRule.h"
  28. #include "core/css/CSSRuleList.h"
  29. #include "core/css/MediaList.h"
  30. #include "core/css/StyleRule.h"
  31. #include "core/css/StyleSheetContents.h"
  32. #include "core/css/parser/CSSParser.h"
  33. #include "core/dom/Document.h"
  34. #include "core/dom/ExceptionCode.h"
  35. #include "core/dom/Node.h"
  36. #include "core/frame/UseCounter.h"
  37. #include "core/html/HTMLStyleElement.h"
  38. #include "core/inspector/InspectorInstrumentation.h"
  39. #include "core/svg/SVGStyleElement.h"
  40. #include "platform/weborigin/SecurityOrigin.h"
  41. #include "wtf/text/StringBuilder.h"
  42. namespace blink {
  43. class StyleSheetCSSRuleList final : public CSSRuleList {
  44. public:
  45. static PassOwnPtrWillBeRawPtr<StyleSheetCSSRuleList> create(CSSStyleSheet* sheet)
  46. {
  47. return adoptPtrWillBeNoop(new StyleSheetCSSRuleList(sheet));
  48. }
  49. DEFINE_INLINE_VIRTUAL_TRACE()
  50. {
  51. visitor->trace(m_styleSheet);
  52. CSSRuleList::trace(visitor);
  53. }
  54. private:
  55. StyleSheetCSSRuleList(CSSStyleSheet* sheet) : m_styleSheet(sheet) { }
  56. #if !ENABLE(OILPAN)
  57. void ref() override { m_styleSheet->ref(); }
  58. void deref() override { m_styleSheet->deref(); }
  59. #endif
  60. unsigned length() const override { return m_styleSheet->length(); }
  61. CSSRule* item(unsigned index) const override { return m_styleSheet->item(index); }
  62. CSSStyleSheet* styleSheet() const override { return m_styleSheet; }
  63. RawPtrWillBeMember<CSSStyleSheet> m_styleSheet;
  64. };
  65. #if ENABLE(ASSERT)
  66. static bool isAcceptableCSSStyleSheetParent(Node* parentNode)
  67. {
  68. // Only these nodes can be parents of StyleSheets, and they need to call
  69. // clearOwnerNode() when moved out of document.
  70. // Destruction of the style sheet counts as being "moved out of the
  71. // document", but only in the non-oilpan version of blink. I.e. don't call
  72. // clearOwnerNode() in the owner's destructor in oilpan.
  73. return !parentNode
  74. || parentNode->isDocumentNode()
  75. || isHTMLLinkElement(*parentNode)
  76. || isHTMLStyleElement(*parentNode)
  77. || isSVGStyleElement(*parentNode)
  78. || parentNode->nodeType() == Node::PROCESSING_INSTRUCTION_NODE;
  79. }
  80. #endif
  81. PassRefPtrWillBeRawPtr<CSSStyleSheet> CSSStyleSheet::create(PassRefPtrWillBeRawPtr<StyleSheetContents> sheet, CSSImportRule* ownerRule)
  82. {
  83. return adoptRefWillBeNoop(new CSSStyleSheet(sheet, ownerRule));
  84. }
  85. PassRefPtrWillBeRawPtr<CSSStyleSheet> CSSStyleSheet::create(PassRefPtrWillBeRawPtr<StyleSheetContents> sheet, Node* ownerNode)
  86. {
  87. return adoptRefWillBeNoop(new CSSStyleSheet(sheet, ownerNode, false, TextPosition::minimumPosition()));
  88. }
  89. PassRefPtrWillBeRawPtr<CSSStyleSheet> CSSStyleSheet::createInline(PassRefPtrWillBeRawPtr<StyleSheetContents> sheet, Node* ownerNode, const TextPosition& startPosition)
  90. {
  91. ASSERT(sheet);
  92. return adoptRefWillBeNoop(new CSSStyleSheet(sheet, ownerNode, true, startPosition));
  93. }
  94. PassRefPtrWillBeRawPtr<CSSStyleSheet> CSSStyleSheet::createInline(Node* ownerNode, const KURL& baseURL, const TextPosition& startPosition, const String& encoding)
  95. {
  96. CSSParserContext parserContext(ownerNode->document(), 0, baseURL, encoding);
  97. RefPtrWillBeRawPtr<StyleSheetContents> sheet = StyleSheetContents::create(baseURL.string(), parserContext);
  98. return adoptRefWillBeNoop(new CSSStyleSheet(sheet.release(), ownerNode, true, startPosition));
  99. }
  100. CSSStyleSheet::CSSStyleSheet(PassRefPtrWillBeRawPtr<StyleSheetContents> contents, CSSImportRule* ownerRule)
  101. : m_contents(contents)
  102. , m_isInlineStylesheet(false)
  103. , m_isDisabled(false)
  104. , m_ownerNode(nullptr)
  105. , m_ownerRule(ownerRule)
  106. , m_startPosition(TextPosition::minimumPosition())
  107. , m_loadCompleted(false)
  108. {
  109. m_contents->registerClient(this);
  110. }
  111. CSSStyleSheet::CSSStyleSheet(PassRefPtrWillBeRawPtr<StyleSheetContents> contents, Node* ownerNode, bool isInlineStylesheet, const TextPosition& startPosition)
  112. : m_contents(contents)
  113. , m_isInlineStylesheet(isInlineStylesheet)
  114. , m_isDisabled(false)
  115. , m_ownerNode(ownerNode)
  116. , m_ownerRule(nullptr)
  117. , m_startPosition(startPosition)
  118. , m_loadCompleted(false)
  119. {
  120. ASSERT(isAcceptableCSSStyleSheetParent(ownerNode));
  121. m_contents->registerClient(this);
  122. }
  123. CSSStyleSheet::~CSSStyleSheet()
  124. {
  125. // With oilpan the parent style sheet pointer is strong and the sheet and
  126. // its RuleCSSOMWrappers die together and we don't need to clear them here.
  127. // Also with oilpan the StyleSheetContents client pointers are weak and
  128. // therefore do not need to be cleared here.
  129. #if !ENABLE(OILPAN)
  130. // For style rules outside the document, .parentStyleSheet can become null even if the style rule
  131. // is still observable from JavaScript. This matches the behavior of .parentNode for nodes, but
  132. // it's not ideal because it makes the CSSOM's behavior depend on the timing of garbage collection.
  133. for (unsigned i = 0; i < m_childRuleCSSOMWrappers.size(); ++i) {
  134. if (m_childRuleCSSOMWrappers[i])
  135. m_childRuleCSSOMWrappers[i]->setParentStyleSheet(0);
  136. }
  137. if (m_mediaCSSOMWrapper)
  138. m_mediaCSSOMWrapper->clearParentStyleSheet();
  139. m_contents->unregisterClient(this);
  140. #endif
  141. }
  142. void CSSStyleSheet::willMutateRules()
  143. {
  144. // If we are the only client it is safe to mutate.
  145. if (m_contents->clientSize() <= 1 && !m_contents->isInMemoryCache()) {
  146. m_contents->clearRuleSet();
  147. if (Document* document = ownerDocument())
  148. m_contents->removeSheetFromCache(document);
  149. m_contents->setMutable();
  150. return;
  151. }
  152. // Only cacheable stylesheets should have multiple clients.
  153. ASSERT(m_contents->isCacheable());
  154. // Copy-on-write.
  155. m_contents->unregisterClient(this);
  156. m_contents = m_contents->copy();
  157. m_contents->registerClient(this);
  158. m_contents->setMutable();
  159. // Any existing CSSOM wrappers need to be connected to the copied child rules.
  160. reattachChildRuleCSSOMWrappers();
  161. }
  162. void CSSStyleSheet::didMutateRules()
  163. {
  164. ASSERT(m_contents->isMutable());
  165. ASSERT(m_contents->clientSize() <= 1);
  166. didMutate(PartialRuleUpdate);
  167. }
  168. void CSSStyleSheet::didMutate(StyleSheetUpdateType updateType)
  169. {
  170. Document* owner = ownerDocument();
  171. if (!owner)
  172. return;
  173. // Need FullStyleUpdate when insertRule or deleteRule,
  174. // because StyleSheetCollection::analyzeStyleSheetChange cannot detect partial rule update.
  175. StyleResolverUpdateMode updateMode = updateType != PartialRuleUpdate ? AnalyzedStyleUpdate : FullStyleUpdate;
  176. owner->modifiedStyleSheet(this, updateMode);
  177. }
  178. void CSSStyleSheet::reattachChildRuleCSSOMWrappers()
  179. {
  180. for (unsigned i = 0; i < m_childRuleCSSOMWrappers.size(); ++i) {
  181. if (!m_childRuleCSSOMWrappers[i])
  182. continue;
  183. m_childRuleCSSOMWrappers[i]->reattach(m_contents->ruleAt(i));
  184. }
  185. }
  186. void CSSStyleSheet::setDisabled(bool disabled)
  187. {
  188. if (disabled == m_isDisabled)
  189. return;
  190. m_isDisabled = disabled;
  191. didMutate();
  192. }
  193. void CSSStyleSheet::setMediaQueries(PassRefPtrWillBeRawPtr<MediaQuerySet> mediaQueries)
  194. {
  195. m_mediaQueries = mediaQueries;
  196. if (m_mediaCSSOMWrapper && m_mediaQueries)
  197. m_mediaCSSOMWrapper->reattach(m_mediaQueries.get());
  198. }
  199. unsigned CSSStyleSheet::length() const
  200. {
  201. return m_contents->ruleCount();
  202. }
  203. CSSRule* CSSStyleSheet::item(unsigned index)
  204. {
  205. unsigned ruleCount = length();
  206. if (index >= ruleCount)
  207. return nullptr;
  208. if (m_childRuleCSSOMWrappers.isEmpty())
  209. m_childRuleCSSOMWrappers.grow(ruleCount);
  210. ASSERT(m_childRuleCSSOMWrappers.size() == ruleCount);
  211. RefPtrWillBeMember<CSSRule>& cssRule = m_childRuleCSSOMWrappers[index];
  212. if (!cssRule)
  213. cssRule = m_contents->ruleAt(index)->createCSSOMWrapper(this);
  214. return cssRule.get();
  215. }
  216. void CSSStyleSheet::clearOwnerNode()
  217. {
  218. didMutate(EntireStyleSheetUpdate);
  219. if (m_ownerNode)
  220. m_contents->unregisterClient(this);
  221. m_ownerNode = nullptr;
  222. }
  223. bool CSSStyleSheet::canAccessRules() const
  224. {
  225. if (m_isInlineStylesheet)
  226. return true;
  227. KURL baseURL = m_contents->baseURL();
  228. if (baseURL.isEmpty())
  229. return true;
  230. Document* document = ownerDocument();
  231. if (!document)
  232. return true;
  233. if (document->securityOrigin()->canRequestNoSuborigin(baseURL))
  234. return true;
  235. if (m_allowRuleAccessFromOrigin && document->securityOrigin()->canAccessCheckSuborigins(m_allowRuleAccessFromOrigin.get()))
  236. return true;
  237. return false;
  238. }
  239. PassRefPtrWillBeRawPtr<CSSRuleList> CSSStyleSheet::rules()
  240. {
  241. return cssRules();
  242. }
  243. unsigned CSSStyleSheet::insertRule(const String& ruleString, unsigned index, ExceptionState& exceptionState)
  244. {
  245. ASSERT(m_childRuleCSSOMWrappers.isEmpty() || m_childRuleCSSOMWrappers.size() == m_contents->ruleCount());
  246. if (index > length()) {
  247. exceptionState.throwDOMException(IndexSizeError, "The index provided (" + String::number(index) + ") is larger than the maximum index (" + String::number(length()) + ").");
  248. return 0;
  249. }
  250. CSSParserContext context(m_contents->parserContext(), UseCounter::getFrom(this));
  251. RefPtrWillBeRawPtr<StyleRuleBase> rule = CSSParser::parseRule(context, m_contents.get(), ruleString);
  252. if (!rule) {
  253. exceptionState.throwDOMException(SyntaxError, "Failed to parse the rule '" + ruleString + "'.");
  254. return 0;
  255. }
  256. RuleMutationScope mutationScope(this);
  257. bool success = m_contents->wrapperInsertRule(rule, index);
  258. if (!success) {
  259. if (rule->isNamespaceRule())
  260. exceptionState.throwDOMException(InvalidStateError, "Failed to insert the rule");
  261. else
  262. exceptionState.throwDOMException(HierarchyRequestError, "Failed to insert the rule.");
  263. return 0;
  264. }
  265. if (!m_childRuleCSSOMWrappers.isEmpty())
  266. m_childRuleCSSOMWrappers.insert(index, RefPtrWillBeMember<CSSRule>(nullptr));
  267. return index;
  268. }
  269. unsigned CSSStyleSheet::insertRule(const String& rule, ExceptionState& exceptionState)
  270. {
  271. UseCounter::countDeprecation(callingExecutionContext(V8PerIsolateData::mainThreadIsolate()), UseCounter::CSSStyleSheetInsertRuleOptionalArg);
  272. return insertRule(rule, 0, exceptionState);
  273. }
  274. void CSSStyleSheet::deleteRule(unsigned index, ExceptionState& exceptionState)
  275. {
  276. ASSERT(m_childRuleCSSOMWrappers.isEmpty() || m_childRuleCSSOMWrappers.size() == m_contents->ruleCount());
  277. if (index >= length()) {
  278. exceptionState.throwDOMException(IndexSizeError, "The index provided (" + String::number(index) + ") is larger than the maximum index (" + String::number(length() - 1) + ").");
  279. return;
  280. }
  281. RuleMutationScope mutationScope(this);
  282. bool success = m_contents->wrapperDeleteRule(index);
  283. if (!success) {
  284. exceptionState.throwDOMException(InvalidStateError, "Failed to delete rule");
  285. return;
  286. }
  287. if (!m_childRuleCSSOMWrappers.isEmpty()) {
  288. if (m_childRuleCSSOMWrappers[index])
  289. m_childRuleCSSOMWrappers[index]->setParentStyleSheet(0);
  290. m_childRuleCSSOMWrappers.remove(index);
  291. }
  292. }
  293. int CSSStyleSheet::addRule(const String& selector, const String& style, int index, ExceptionState& exceptionState)
  294. {
  295. StringBuilder text;
  296. text.append(selector);
  297. text.appendLiteral(" { ");
  298. text.append(style);
  299. if (!style.isEmpty())
  300. text.append(' ');
  301. text.append('}');
  302. insertRule(text.toString(), index, exceptionState);
  303. // As per Microsoft documentation, always return -1.
  304. return -1;
  305. }
  306. int CSSStyleSheet::addRule(const String& selector, const String& style, ExceptionState& exceptionState)
  307. {
  308. return addRule(selector, style, length(), exceptionState);
  309. }
  310. PassRefPtrWillBeRawPtr<CSSRuleList> CSSStyleSheet::cssRules()
  311. {
  312. if (!canAccessRules())
  313. return nullptr;
  314. if (!m_ruleListCSSOMWrapper)
  315. m_ruleListCSSOMWrapper = StyleSheetCSSRuleList::create(this);
  316. return m_ruleListCSSOMWrapper.get();
  317. }
  318. String CSSStyleSheet::href() const
  319. {
  320. return m_contents->originalURL();
  321. }
  322. KURL CSSStyleSheet::baseURL() const
  323. {
  324. return m_contents->baseURL();
  325. }
  326. bool CSSStyleSheet::isLoading() const
  327. {
  328. return m_contents->isLoading();
  329. }
  330. MediaList* CSSStyleSheet::media() const
  331. {
  332. if (!m_mediaQueries)
  333. return nullptr;
  334. if (!m_mediaCSSOMWrapper)
  335. m_mediaCSSOMWrapper = MediaList::create(m_mediaQueries.get(), const_cast<CSSStyleSheet*>(this));
  336. return m_mediaCSSOMWrapper.get();
  337. }
  338. CSSStyleSheet* CSSStyleSheet::parentStyleSheet() const
  339. {
  340. return m_ownerRule ? m_ownerRule->parentStyleSheet() : nullptr;
  341. }
  342. Document* CSSStyleSheet::ownerDocument() const
  343. {
  344. const CSSStyleSheet* root = this;
  345. while (root->parentStyleSheet())
  346. root = root->parentStyleSheet();
  347. return root->ownerNode() ? &root->ownerNode()->document() : nullptr;
  348. }
  349. void CSSStyleSheet::setAllowRuleAccessFromOrigin(PassRefPtr<SecurityOrigin> allowedOrigin)
  350. {
  351. m_allowRuleAccessFromOrigin = allowedOrigin;
  352. }
  353. void CSSStyleSheet::clearChildRuleCSSOMWrappers()
  354. {
  355. m_childRuleCSSOMWrappers.clear();
  356. }
  357. bool CSSStyleSheet::sheetLoaded()
  358. {
  359. ASSERT(m_ownerNode);
  360. setLoadCompleted(m_ownerNode->sheetLoaded());
  361. return m_loadCompleted;
  362. }
  363. void CSSStyleSheet::startLoadingDynamicSheet()
  364. {
  365. setLoadCompleted(false);
  366. m_ownerNode->startLoadingDynamicSheet();
  367. }
  368. void CSSStyleSheet::setLoadCompleted(bool completed)
  369. {
  370. if (completed == m_loadCompleted)
  371. return;
  372. m_loadCompleted = completed;
  373. if (completed)
  374. m_contents->clientLoadCompleted(this);
  375. else
  376. m_contents->clientLoadStarted(this);
  377. }
  378. DEFINE_TRACE(CSSStyleSheet)
  379. {
  380. visitor->trace(m_contents);
  381. visitor->trace(m_mediaQueries);
  382. visitor->trace(m_ownerNode);
  383. visitor->trace(m_ownerRule);
  384. visitor->trace(m_mediaCSSOMWrapper);
  385. visitor->trace(m_childRuleCSSOMWrappers);
  386. visitor->trace(m_ruleListCSSOMWrapper);
  387. StyleSheet::trace(visitor);
  388. }
  389. } // namespace blink