StyleSheetContents.cpp 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525
  1. /*
  2. * (C) 1999-2003 Lars Knoll (knoll@kde.org)
  3. * Copyright (C) 2004, 2006, 2007, 2012, 2013 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 "StyleSheetContents.h"
  22. #include "CSSImportRule.h"
  23. #include "CSSParser.h"
  24. #include "CSSStyleSheet.h"
  25. #include "CachedCSSStyleSheet.h"
  26. #include "Document.h"
  27. #include "MediaList.h"
  28. #include "Node.h"
  29. #include "RuleSet.h"
  30. #include "SecurityOrigin.h"
  31. #include "StylePropertySet.h"
  32. #include "StyleRule.h"
  33. #include "StyleRuleImport.h"
  34. #include <wtf/Deque.h>
  35. namespace WebCore {
  36. // Rough size estimate for the memory cache.
  37. unsigned StyleSheetContents::estimatedSizeInBytes() const
  38. {
  39. // Note that this does not take into account size of the strings hanging from various objects.
  40. // The assumption is that nearly all of of them are atomic and would exist anyway.
  41. unsigned size = sizeof(*this);
  42. // FIXME: This ignores the children of media and region rules.
  43. // Most rules are StyleRules.
  44. size += ruleCount() * StyleRule::averageSizeInBytes();
  45. for (unsigned i = 0; i < m_importRules.size(); ++i) {
  46. if (StyleSheetContents* sheet = m_importRules[i]->styleSheet())
  47. size += sheet->estimatedSizeInBytes();
  48. }
  49. return size;
  50. }
  51. StyleSheetContents::StyleSheetContents(StyleRuleImport* ownerRule, const String& originalURL, const CSSParserContext& context)
  52. : m_ownerRule(ownerRule)
  53. , m_originalURL(originalURL)
  54. , m_loadCompleted(false)
  55. , m_isUserStyleSheet(ownerRule && ownerRule->parentStyleSheet() && ownerRule->parentStyleSheet()->isUserStyleSheet())
  56. , m_hasSyntacticallyValidCSSHeader(true)
  57. , m_didLoadErrorOccur(false)
  58. , m_usesRemUnits(false)
  59. , m_isMutable(false)
  60. , m_isInMemoryCache(false)
  61. , m_parserContext(context)
  62. {
  63. }
  64. StyleSheetContents::StyleSheetContents(const StyleSheetContents& o)
  65. : RefCounted<StyleSheetContents>()
  66. , m_ownerRule(0)
  67. , m_originalURL(o.m_originalURL)
  68. , m_encodingFromCharsetRule(o.m_encodingFromCharsetRule)
  69. , m_importRules(o.m_importRules.size())
  70. , m_childRules(o.m_childRules.size())
  71. , m_namespaces(o.m_namespaces)
  72. , m_loadCompleted(true)
  73. , m_isUserStyleSheet(o.m_isUserStyleSheet)
  74. , m_hasSyntacticallyValidCSSHeader(o.m_hasSyntacticallyValidCSSHeader)
  75. , m_didLoadErrorOccur(false)
  76. , m_usesRemUnits(o.m_usesRemUnits)
  77. , m_isMutable(false)
  78. , m_isInMemoryCache(false)
  79. , m_parserContext(o.m_parserContext)
  80. {
  81. ASSERT(o.isCacheable());
  82. // FIXME: Copy import rules.
  83. ASSERT(o.m_importRules.isEmpty());
  84. for (unsigned i = 0; i < m_childRules.size(); ++i)
  85. m_childRules[i] = o.m_childRules[i]->copy();
  86. }
  87. StyleSheetContents::~StyleSheetContents()
  88. {
  89. clearRules();
  90. }
  91. bool StyleSheetContents::isCacheable() const
  92. {
  93. // FIXME: Support copying import rules.
  94. if (!m_importRules.isEmpty())
  95. return false;
  96. // FIXME: Support cached stylesheets in import rules.
  97. if (m_ownerRule)
  98. return false;
  99. // This would require dealing with multiple clients for load callbacks.
  100. if (!m_loadCompleted)
  101. return false;
  102. if (m_didLoadErrorOccur)
  103. return false;
  104. // It is not the original sheet anymore.
  105. if (m_isMutable)
  106. return false;
  107. // If the header is valid we are not going to need to check the SecurityOrigin.
  108. // FIXME: Valid mime type avoids the check too.
  109. if (!m_hasSyntacticallyValidCSSHeader)
  110. return false;
  111. return true;
  112. }
  113. void StyleSheetContents::parserAppendRule(PassRefPtr<StyleRuleBase> rule)
  114. {
  115. ASSERT(!rule->isCharsetRule());
  116. if (rule->isImportRule()) {
  117. // Parser enforces that @import rules come before anything else except @charset.
  118. ASSERT(m_childRules.isEmpty());
  119. m_importRules.append(static_cast<StyleRuleImport*>(rule.get()));
  120. m_importRules.last()->setParentStyleSheet(this);
  121. m_importRules.last()->requestStyleSheet();
  122. return;
  123. }
  124. #if ENABLE(RESOLUTION_MEDIA_QUERY)
  125. // Add warning message to inspector if dpi/dpcm values are used for screen media.
  126. if (rule->isMediaRule())
  127. reportMediaQueryWarningIfNeeded(singleOwnerDocument(), static_cast<StyleRuleMedia*>(rule.get())->mediaQueries());
  128. #endif
  129. // NOTE: The selector list has to fit into RuleData. <http://webkit.org/b/118369>
  130. // If we're adding a rule with a huge number of selectors, split it up into multiple rules
  131. if (rule->isStyleRule() && toStyleRule(rule.get())->selectorList().componentCount() > RuleData::maximumSelectorComponentCount) {
  132. Vector<RefPtr<StyleRule> > rules = toStyleRule(rule.get())->splitIntoMultipleRulesWithMaximumSelectorComponentCount(RuleData::maximumSelectorComponentCount);
  133. m_childRules.appendVector(rules);
  134. return;
  135. }
  136. m_childRules.append(rule);
  137. }
  138. StyleRuleBase* StyleSheetContents::ruleAt(unsigned index) const
  139. {
  140. ASSERT_WITH_SECURITY_IMPLICATION(index < ruleCount());
  141. unsigned childVectorIndex = index;
  142. if (hasCharsetRule()) {
  143. if (index == 0)
  144. return 0;
  145. --childVectorIndex;
  146. }
  147. if (childVectorIndex < m_importRules.size())
  148. return m_importRules[childVectorIndex].get();
  149. childVectorIndex -= m_importRules.size();
  150. return m_childRules[childVectorIndex].get();
  151. }
  152. unsigned StyleSheetContents::ruleCount() const
  153. {
  154. unsigned result = 0;
  155. result += hasCharsetRule() ? 1 : 0;
  156. result += m_importRules.size();
  157. result += m_childRules.size();
  158. return result;
  159. }
  160. void StyleSheetContents::clearCharsetRule()
  161. {
  162. m_encodingFromCharsetRule = String();
  163. }
  164. void StyleSheetContents::clearRules()
  165. {
  166. for (unsigned i = 0; i < m_importRules.size(); ++i) {
  167. ASSERT(m_importRules.at(i)->parentStyleSheet() == this);
  168. m_importRules[i]->clearParentStyleSheet();
  169. }
  170. m_importRules.clear();
  171. m_childRules.clear();
  172. clearCharsetRule();
  173. }
  174. void StyleSheetContents::parserSetEncodingFromCharsetRule(const String& encoding)
  175. {
  176. // Parser enforces that there is ever only one @charset.
  177. ASSERT(m_encodingFromCharsetRule.isNull());
  178. m_encodingFromCharsetRule = encoding;
  179. }
  180. bool StyleSheetContents::wrapperInsertRule(PassRefPtr<StyleRuleBase> rule, unsigned index)
  181. {
  182. ASSERT(m_isMutable);
  183. ASSERT_WITH_SECURITY_IMPLICATION(index <= ruleCount());
  184. // Parser::parseRule doesn't currently allow @charset so we don't need to deal with it.
  185. ASSERT(!rule->isCharsetRule());
  186. unsigned childVectorIndex = index;
  187. // m_childRules does not contain @charset which is always in index 0 if it exists.
  188. if (hasCharsetRule()) {
  189. if (childVectorIndex == 0) {
  190. // Nothing can be inserted before @charset.
  191. return false;
  192. }
  193. --childVectorIndex;
  194. }
  195. if (childVectorIndex < m_importRules.size() || (childVectorIndex == m_importRules.size() && rule->isImportRule())) {
  196. // Inserting non-import rule before @import is not allowed.
  197. if (!rule->isImportRule())
  198. return false;
  199. m_importRules.insert(childVectorIndex, static_cast<StyleRuleImport*>(rule.get()));
  200. m_importRules[childVectorIndex]->setParentStyleSheet(this);
  201. m_importRules[childVectorIndex]->requestStyleSheet();
  202. // FIXME: Stylesheet doesn't actually change meaningfully before the imported sheets are loaded.
  203. return true;
  204. }
  205. // Inserting @import rule after a non-import rule is not allowed.
  206. if (rule->isImportRule())
  207. return false;
  208. childVectorIndex -= m_importRules.size();
  209. m_childRules.insert(childVectorIndex, rule);
  210. return true;
  211. }
  212. void StyleSheetContents::wrapperDeleteRule(unsigned index)
  213. {
  214. ASSERT(m_isMutable);
  215. ASSERT_WITH_SECURITY_IMPLICATION(index < ruleCount());
  216. unsigned childVectorIndex = index;
  217. if (hasCharsetRule()) {
  218. if (childVectorIndex == 0) {
  219. clearCharsetRule();
  220. return;
  221. }
  222. --childVectorIndex;
  223. }
  224. if (childVectorIndex < m_importRules.size()) {
  225. m_importRules[childVectorIndex]->clearParentStyleSheet();
  226. m_importRules.remove(childVectorIndex);
  227. return;
  228. }
  229. childVectorIndex -= m_importRules.size();
  230. m_childRules.remove(childVectorIndex);
  231. }
  232. void StyleSheetContents::parserAddNamespace(const AtomicString& prefix, const AtomicString& uri)
  233. {
  234. if (uri.isNull() || prefix.isNull())
  235. return;
  236. PrefixNamespaceURIMap::AddResult result = m_namespaces.add(prefix, uri);
  237. if (result.isNewEntry)
  238. return;
  239. result.iterator->value = uri;
  240. }
  241. const AtomicString& StyleSheetContents::determineNamespace(const AtomicString& prefix)
  242. {
  243. if (prefix.isNull())
  244. return nullAtom; // No namespace. If an element/attribute has a namespace, we won't match it.
  245. if (prefix == starAtom)
  246. return starAtom; // We'll match any namespace.
  247. PrefixNamespaceURIMap::const_iterator it = m_namespaces.find(prefix);
  248. if (it == m_namespaces.end())
  249. return nullAtom;
  250. return it->value;
  251. }
  252. void StyleSheetContents::parseAuthorStyleSheet(const CachedCSSStyleSheet* cachedStyleSheet, const SecurityOrigin* securityOrigin)
  253. {
  254. // Check to see if we should enforce the MIME type of the CSS resource in strict mode.
  255. // Running in iWeb 2 is one example of where we don't want to - <rdar://problem/6099748>
  256. bool enforceMIMEType = isStrictParserMode(m_parserContext.mode) && m_parserContext.enforcesCSSMIMETypeInNoQuirksMode;
  257. bool hasValidMIMEType = false;
  258. String sheetText = cachedStyleSheet->sheetText(enforceMIMEType, &hasValidMIMEType);
  259. CSSParser p(parserContext());
  260. p.parseSheet(this, sheetText, 0, 0, true);
  261. // If we're loading a stylesheet cross-origin, and the MIME type is not standard, require the CSS
  262. // to at least start with a syntactically valid CSS rule.
  263. // This prevents an attacker playing games by injecting CSS strings into HTML, XML, JSON, etc. etc.
  264. if (!hasValidMIMEType && !hasSyntacticallyValidCSSHeader()) {
  265. bool isCrossOriginCSS = !securityOrigin || !securityOrigin->canRequest(baseURL());
  266. if (isCrossOriginCSS) {
  267. clearRules();
  268. return;
  269. }
  270. }
  271. if (m_parserContext.needsSiteSpecificQuirks && isStrictParserMode(m_parserContext.mode)) {
  272. // Work around <https://bugs.webkit.org/show_bug.cgi?id=28350>.
  273. DEFINE_STATIC_LOCAL(const String, mediaWikiKHTMLFixesStyleSheet, (ASCIILiteral("/* KHTML fix stylesheet */\n/* work around the horizontal scrollbars */\n#column-content { margin-left: 0; }\n\n")));
  274. // There are two variants of KHTMLFixes.css. One is equal to mediaWikiKHTMLFixesStyleSheet,
  275. // while the other lacks the second trailing newline.
  276. if (baseURL().string().endsWith("/KHTMLFixes.css") && !sheetText.isNull() && mediaWikiKHTMLFixesStyleSheet.startsWith(sheetText)
  277. && sheetText.length() >= mediaWikiKHTMLFixesStyleSheet.length() - 1)
  278. clearRules();
  279. }
  280. }
  281. bool StyleSheetContents::parseString(const String& sheetText)
  282. {
  283. return parseStringAtLine(sheetText, 0, false);
  284. }
  285. bool StyleSheetContents::parseStringAtLine(const String& sheetText, int startLineNumber, bool createdByParser)
  286. {
  287. CSSParser p(parserContext());
  288. p.parseSheet(this, sheetText, startLineNumber, 0, createdByParser);
  289. return true;
  290. }
  291. bool StyleSheetContents::isLoading() const
  292. {
  293. for (unsigned i = 0; i < m_importRules.size(); ++i) {
  294. if (m_importRules[i]->isLoading())
  295. return true;
  296. }
  297. return false;
  298. }
  299. void StyleSheetContents::checkLoaded()
  300. {
  301. if (isLoading())
  302. return;
  303. RefPtr<StyleSheetContents> protect(this);
  304. // Avoid |this| being deleted by scripts that run via
  305. // ScriptableDocumentParser::executeScriptsWaitingForStylesheets().
  306. // See <rdar://problem/6622300>.
  307. RefPtr<StyleSheetContents> protector(this);
  308. StyleSheetContents* parentSheet = parentStyleSheet();
  309. if (parentSheet) {
  310. parentSheet->checkLoaded();
  311. m_loadCompleted = true;
  312. return;
  313. }
  314. RefPtr<Node> ownerNode = singleOwnerNode();
  315. if (!ownerNode) {
  316. m_loadCompleted = true;
  317. return;
  318. }
  319. m_loadCompleted = ownerNode->sheetLoaded();
  320. if (m_loadCompleted)
  321. ownerNode->notifyLoadedSheetAndAllCriticalSubresources(m_didLoadErrorOccur);
  322. }
  323. void StyleSheetContents::notifyLoadedSheet(const CachedCSSStyleSheet* sheet)
  324. {
  325. ASSERT(sheet);
  326. m_didLoadErrorOccur |= sheet->errorOccurred();
  327. }
  328. void StyleSheetContents::startLoadingDynamicSheet()
  329. {
  330. if (Node* owner = singleOwnerNode())
  331. owner->startLoadingDynamicSheet();
  332. }
  333. StyleSheetContents* StyleSheetContents::rootStyleSheet() const
  334. {
  335. const StyleSheetContents* root = this;
  336. while (root->parentStyleSheet())
  337. root = root->parentStyleSheet();
  338. return const_cast<StyleSheetContents*>(root);
  339. }
  340. Node* StyleSheetContents::singleOwnerNode() const
  341. {
  342. StyleSheetContents* root = rootStyleSheet();
  343. if (root->m_clients.isEmpty())
  344. return 0;
  345. ASSERT(root->m_clients.size() == 1);
  346. return root->m_clients[0]->ownerNode();
  347. }
  348. Document* StyleSheetContents::singleOwnerDocument() const
  349. {
  350. Node* ownerNode = singleOwnerNode();
  351. return ownerNode ? ownerNode->document() : 0;
  352. }
  353. KURL StyleSheetContents::completeURL(const String& url) const
  354. {
  355. return CSSParser::completeURL(m_parserContext, url);
  356. }
  357. void StyleSheetContents::addSubresourceStyleURLs(ListHashSet<KURL>& urls)
  358. {
  359. Deque<StyleSheetContents*> styleSheetQueue;
  360. styleSheetQueue.append(this);
  361. while (!styleSheetQueue.isEmpty()) {
  362. StyleSheetContents* styleSheet = styleSheetQueue.takeFirst();
  363. for (unsigned i = 0; i < styleSheet->m_importRules.size(); ++i) {
  364. StyleRuleImport* importRule = styleSheet->m_importRules[i].get();
  365. if (importRule->styleSheet()) {
  366. styleSheetQueue.append(importRule->styleSheet());
  367. addSubresourceURL(urls, importRule->styleSheet()->baseURL());
  368. }
  369. }
  370. for (unsigned i = 0; i < styleSheet->m_childRules.size(); ++i) {
  371. StyleRuleBase* rule = styleSheet->m_childRules[i].get();
  372. if (rule->isStyleRule())
  373. static_cast<StyleRule*>(rule)->properties()->addSubresourceStyleURLs(urls, this);
  374. else if (rule->isFontFaceRule())
  375. static_cast<StyleRuleFontFace*>(rule)->properties()->addSubresourceStyleURLs(urls, this);
  376. }
  377. }
  378. }
  379. static bool childRulesHaveFailedOrCanceledSubresources(const Vector<RefPtr<StyleRuleBase> >& rules)
  380. {
  381. for (unsigned i = 0; i < rules.size(); ++i) {
  382. const StyleRuleBase* rule = rules[i].get();
  383. switch (rule->type()) {
  384. case StyleRuleBase::Style:
  385. if (static_cast<const StyleRule*>(rule)->properties()->hasFailedOrCanceledSubresources())
  386. return true;
  387. break;
  388. case StyleRuleBase::FontFace:
  389. if (static_cast<const StyleRuleFontFace*>(rule)->properties()->hasFailedOrCanceledSubresources())
  390. return true;
  391. break;
  392. case StyleRuleBase::Media:
  393. if (childRulesHaveFailedOrCanceledSubresources(static_cast<const StyleRuleMedia*>(rule)->childRules()))
  394. return true;
  395. break;
  396. case StyleRuleBase::Region:
  397. if (childRulesHaveFailedOrCanceledSubresources(static_cast<const StyleRuleRegion*>(rule)->childRules()))
  398. return true;
  399. break;
  400. #if ENABLE(SHADOW_DOM)
  401. case StyleRuleBase::HostInternal:
  402. if (childRulesHaveFailedOrCanceledSubresources(static_cast<const StyleRuleHost*>(rule)->childRules()))
  403. return true;
  404. break;
  405. #endif
  406. case StyleRuleBase::Import:
  407. ASSERT_NOT_REACHED();
  408. case StyleRuleBase::Page:
  409. case StyleRuleBase::Keyframes:
  410. case StyleRuleBase::Unknown:
  411. case StyleRuleBase::Charset:
  412. case StyleRuleBase::Keyframe:
  413. #if ENABLE(CSS3_CONDITIONAL_RULES)
  414. case StyleRuleBase::Supports:
  415. #endif
  416. #if ENABLE(CSS_DEVICE_ADAPTATION)
  417. case StyleRuleBase::Viewport:
  418. #endif
  419. #if ENABLE(CSS_SHADERS)
  420. case StyleRuleBase::Filter:
  421. #endif
  422. break;
  423. }
  424. }
  425. return false;
  426. }
  427. bool StyleSheetContents::hasFailedOrCanceledSubresources() const
  428. {
  429. ASSERT(isCacheable());
  430. return childRulesHaveFailedOrCanceledSubresources(m_childRules);
  431. }
  432. StyleSheetContents* StyleSheetContents::parentStyleSheet() const
  433. {
  434. return m_ownerRule ? m_ownerRule->parentStyleSheet() : 0;
  435. }
  436. void StyleSheetContents::registerClient(CSSStyleSheet* sheet)
  437. {
  438. ASSERT(!m_clients.contains(sheet));
  439. m_clients.append(sheet);
  440. }
  441. void StyleSheetContents::unregisterClient(CSSStyleSheet* sheet)
  442. {
  443. size_t position = m_clients.find(sheet);
  444. ASSERT(position != notFound);
  445. m_clients.remove(position);
  446. }
  447. void StyleSheetContents::addedToMemoryCache()
  448. {
  449. ASSERT(!m_isInMemoryCache);
  450. ASSERT(isCacheable());
  451. m_isInMemoryCache = true;
  452. }
  453. void StyleSheetContents::removedFromMemoryCache()
  454. {
  455. ASSERT(m_isInMemoryCache);
  456. ASSERT(isCacheable());
  457. m_isInMemoryCache = false;
  458. }
  459. void StyleSheetContents::shrinkToFit()
  460. {
  461. m_importRules.shrinkToFit();
  462. m_childRules.shrinkToFit();
  463. }
  464. }