content/base/src/nsStyleLinkElement.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
     2  *
     3  * This Source Code Form is subject to the terms of the Mozilla Public
     4  * License, v. 2.0. If a copy of the MPL was not distributed with this
     5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     7 /*
     8  * A base class which implements nsIStyleSheetLinkingElement and can
     9  * be subclassed by various content nodes that want to load
    10  * stylesheets (<style>, <link>, processing instructions, etc).
    11  */
    13 #include "nsStyleLinkElement.h"
    15 #include "mozilla/css/Loader.h"
    16 #include "mozilla/dom/Element.h"
    17 #include "mozilla/dom/ShadowRoot.h"
    18 #include "nsCSSStyleSheet.h"
    19 #include "nsIContent.h"
    20 #include "nsIDocument.h"
    21 #include "nsIDOMComment.h"
    22 #include "nsIDOMNode.h"
    23 #include "nsIDOMStyleSheet.h"
    24 #include "nsNetUtil.h"
    25 #include "nsUnicharUtils.h"
    26 #include "nsCRT.h"
    27 #include "nsXPCOMCIDInternal.h"
    28 #include "nsUnicharInputStream.h"
    29 #include "nsContentUtils.h"
    30 #include "nsStyleUtil.h"
    32 using namespace mozilla;
    33 using namespace mozilla::dom;
    35 nsStyleLinkElement::nsStyleLinkElement()
    36   : mDontLoadStyle(false)
    37   , mUpdatesEnabled(true)
    38   , mLineNumber(1)
    39 {
    40 }
    42 nsStyleLinkElement::~nsStyleLinkElement()
    43 {
    44   nsStyleLinkElement::SetStyleSheet(nullptr);
    45 }
    47 void
    48 nsStyleLinkElement::Unlink()
    49 {
    50   mStyleSheet = nullptr;
    51 }
    53 void
    54 nsStyleLinkElement::Traverse(nsCycleCollectionTraversalCallback &cb)
    55 {
    56   nsStyleLinkElement* tmp = this;
    57   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStyleSheet);
    58 }
    60 NS_IMETHODIMP 
    61 nsStyleLinkElement::SetStyleSheet(nsCSSStyleSheet* aStyleSheet)
    62 {
    63   if (mStyleSheet) {
    64     mStyleSheet->SetOwningNode(nullptr);
    65   }
    67   mStyleSheet = aStyleSheet;
    68   if (mStyleSheet) {
    69     nsCOMPtr<nsINode> node = do_QueryObject(this);
    70     if (node) {
    71       mStyleSheet->SetOwningNode(node);
    72     }
    73   }
    75   return NS_OK;
    76 }
    78 NS_IMETHODIMP 
    79 nsStyleLinkElement::GetStyleSheet(nsIStyleSheet*& aStyleSheet)
    80 {
    81   aStyleSheet = mStyleSheet;
    82   NS_IF_ADDREF(aStyleSheet);
    84   return NS_OK;
    85 }
    87 NS_IMETHODIMP 
    88 nsStyleLinkElement::InitStyleLinkElement(bool aDontLoadStyle)
    89 {
    90   mDontLoadStyle = aDontLoadStyle;
    92   return NS_OK;
    93 }
    95 NS_IMETHODIMP
    96 nsStyleLinkElement::SetEnableUpdates(bool aEnableUpdates)
    97 {
    98   mUpdatesEnabled = aEnableUpdates;
   100   return NS_OK;
   101 }
   103 NS_IMETHODIMP
   104 nsStyleLinkElement::GetCharset(nsAString& aCharset)
   105 {
   106   // descendants have to implement this themselves
   107   return NS_ERROR_NOT_IMPLEMENTED;
   108 }
   110 /* virtual */ void
   111 nsStyleLinkElement::OverrideBaseURI(nsIURI* aNewBaseURI)
   112 {
   113   NS_NOTREACHED("Base URI can't be overriden in this implementation "
   114                 "of nsIStyleSheetLinkingElement.");
   115 }
   117 /* virtual */ void
   118 nsStyleLinkElement::SetLineNumber(uint32_t aLineNumber)
   119 {
   120   mLineNumber = aLineNumber;
   121 }
   123 static uint32_t ToLinkMask(const nsAString& aLink)
   124 { 
   125   if (aLink.EqualsLiteral("prefetch"))
   126     return nsStyleLinkElement::ePREFETCH;
   127   else if (aLink.EqualsLiteral("dns-prefetch"))
   128     return nsStyleLinkElement::eDNS_PREFETCH;
   129   else if (aLink.EqualsLiteral("stylesheet"))
   130     return nsStyleLinkElement::eSTYLESHEET;
   131   else if (aLink.EqualsLiteral("next"))
   132     return nsStyleLinkElement::eNEXT;
   133   else if (aLink.EqualsLiteral("alternate"))
   134     return nsStyleLinkElement::eALTERNATE;
   135   else 
   136     return 0;
   137 }
   139 uint32_t nsStyleLinkElement::ParseLinkTypes(const nsAString& aTypes)
   140 {
   141   uint32_t linkMask = 0;
   142   nsAString::const_iterator start, done;
   143   aTypes.BeginReading(start);
   144   aTypes.EndReading(done);
   145   if (start == done)
   146     return linkMask;
   148   nsAString::const_iterator current(start);
   149   bool inString = !nsContentUtils::IsHTMLWhitespace(*current);
   150   nsAutoString subString;
   152   while (current != done) {
   153     if (nsContentUtils::IsHTMLWhitespace(*current)) {
   154       if (inString) {
   155         nsContentUtils::ASCIIToLower(Substring(start, current), subString);
   156         linkMask |= ToLinkMask(subString);
   157         inString = false;
   158       }
   159     }
   160     else {
   161       if (!inString) {
   162         start = current;
   163         inString = true;
   164       }
   165     }
   166     ++current;
   167   }
   168   if (inString) {
   169     nsContentUtils::ASCIIToLower(Substring(start, current), subString);
   170     linkMask |= ToLinkMask(subString);
   171   }
   172   return linkMask;
   173 }
   175 NS_IMETHODIMP
   176 nsStyleLinkElement::UpdateStyleSheet(nsICSSLoaderObserver* aObserver,
   177                                      bool* aWillNotify,
   178                                      bool* aIsAlternate)
   179 {
   180   return DoUpdateStyleSheet(nullptr, nullptr, aObserver, aWillNotify,
   181                             aIsAlternate, false);
   182 }
   184 nsresult
   185 nsStyleLinkElement::UpdateStyleSheetInternal(nsIDocument *aOldDocument,
   186                                              ShadowRoot *aOldShadowRoot,
   187                                              bool aForceUpdate)
   188 {
   189   bool notify, alternate;
   190   return DoUpdateStyleSheet(aOldDocument, aOldShadowRoot, nullptr, &notify,
   191                             &alternate, aForceUpdate);
   192 }
   194 static bool
   195 IsScopedStyleElement(nsIContent* aContent)
   196 {
   197   // This is quicker than, say, QIing aContent to nsStyleLinkElement
   198   // and then calling its virtual GetStyleSheetInfo method to find out
   199   // if it is scoped.
   200   return (aContent->IsHTML(nsGkAtoms::style) ||
   201           aContent->IsSVG(nsGkAtoms::style)) &&
   202          aContent->HasAttr(kNameSpaceID_None, nsGkAtoms::scoped);
   203 }
   205 static void
   206 SetIsElementInStyleScopeFlagOnSubtree(Element* aElement)
   207 {
   208   if (aElement->IsElementInStyleScope()) {
   209     return;
   210   }
   212   aElement->SetIsElementInStyleScope();
   214   nsIContent* n = aElement->GetNextNode(aElement);
   215   while (n) {
   216     if (n->IsElementInStyleScope()) {
   217       n = n->GetNextNonChildNode(aElement);
   218     } else {
   219       if (n->IsElement()) {
   220         n->SetIsElementInStyleScope();
   221       }
   222       n = n->GetNextNode(aElement);
   223     }
   224   }
   225 }
   227 static bool
   228 HasScopedStyleSheetChild(nsIContent* aContent)
   229 {
   230   for (nsIContent* n = aContent->GetFirstChild(); n; n = n->GetNextSibling()) {
   231     if (IsScopedStyleElement(n)) {
   232       return true;
   233     }
   234   }
   235   return false;
   236 }
   238 // Called when aElement has had a <style scoped> child removed.
   239 static void
   240 UpdateIsElementInStyleScopeFlagOnSubtree(Element* aElement)
   241 {
   242   NS_ASSERTION(aElement->IsElementInStyleScope(),
   243                "only call UpdateIsElementInStyleScopeFlagOnSubtree on a "
   244                "subtree that has IsElementInStyleScope boolean flag set");
   246   if (HasScopedStyleSheetChild(aElement)) {
   247     return;
   248   }
   250   aElement->ClearIsElementInStyleScope();
   252   nsIContent* n = aElement->GetNextNode(aElement);
   253   while (n) {
   254     if (HasScopedStyleSheetChild(n)) {
   255       n = n->GetNextNonChildNode(aElement);
   256     } else {
   257       if (n->IsElement()) {
   258         n->ClearIsElementInStyleScope();
   259       }
   260       n = n->GetNextNode(aElement);
   261     }
   262   }
   263 }
   265 static Element*
   266 GetScopeElement(nsIStyleSheet* aSheet)
   267 {
   268   nsRefPtr<nsCSSStyleSheet> cssStyleSheet = do_QueryObject(aSheet);
   269   if (!cssStyleSheet) {
   270     return nullptr;
   271   }
   273   return cssStyleSheet->GetScopeElement();
   274 }
   276 nsresult
   277 nsStyleLinkElement::DoUpdateStyleSheet(nsIDocument* aOldDocument,
   278                                        ShadowRoot* aOldShadowRoot,
   279                                        nsICSSLoaderObserver* aObserver,
   280                                        bool* aWillNotify,
   281                                        bool* aIsAlternate,
   282                                        bool aForceUpdate)
   283 {
   284   *aWillNotify = false;
   286   nsCOMPtr<nsIContent> thisContent;
   287   CallQueryInterface(this, getter_AddRefs(thisContent));
   289   // All instances of nsStyleLinkElement should implement nsIContent.
   290   NS_ENSURE_TRUE(thisContent, NS_ERROR_FAILURE);
   292   // Check for a ShadowRoot because link elements are inert in a
   293   // ShadowRoot.
   294   ShadowRoot* containingShadow = thisContent->GetContainingShadow();
   295   if (thisContent->IsHTML(nsGkAtoms::link) &&
   296       (aOldShadowRoot || containingShadow)) {
   297     return NS_OK;
   298   }
   300   Element* oldScopeElement = GetScopeElement(mStyleSheet);
   302   if (mStyleSheet && aOldDocument) {
   303     // We're removing the link element from the document, unload the
   304     // stylesheet.  We want to do this even if updates are disabled, since
   305     // otherwise a sheet with a stale linking element pointer will be hanging
   306     // around -- not good!
   307     if (aOldShadowRoot) {
   308       aOldShadowRoot->RemoveSheet(mStyleSheet);
   309     } else {
   310       aOldDocument->BeginUpdate(UPDATE_STYLE);
   311       aOldDocument->RemoveStyleSheet(mStyleSheet);
   312       aOldDocument->EndUpdate(UPDATE_STYLE);
   313     }
   315     nsStyleLinkElement::SetStyleSheet(nullptr);
   316     if (oldScopeElement) {
   317       UpdateIsElementInStyleScopeFlagOnSubtree(oldScopeElement);
   318     }
   319   }
   321   // When static documents are created, stylesheets are cloned manually.
   322   if (mDontLoadStyle || !mUpdatesEnabled ||
   323       thisContent->OwnerDoc()->IsStaticDocument()) {
   324     return NS_OK;
   325   }
   327   nsCOMPtr<nsIDocument> doc = thisContent->GetDocument();
   329   if (!doc || !doc->CSSLoader()->GetEnabled()) {
   330     return NS_OK;
   331   }
   333   bool isInline;
   334   nsCOMPtr<nsIURI> uri = GetStyleSheetURL(&isInline);
   336   if (!aForceUpdate && mStyleSheet && !isInline && uri) {
   337     nsIURI* oldURI = mStyleSheet->GetSheetURI();
   338     if (oldURI) {
   339       bool equal;
   340       nsresult rv = oldURI->Equals(uri, &equal);
   341       if (NS_SUCCEEDED(rv) && equal) {
   342         return NS_OK; // We already loaded this stylesheet
   343       }
   344     }
   345   }
   347   if (mStyleSheet) {
   348     if (thisContent->HasFlag(NODE_IS_IN_SHADOW_TREE)) {
   349       ShadowRoot* containingShadow = thisContent->GetContainingShadow();
   350       containingShadow->RemoveSheet(mStyleSheet);
   351     } else {
   352       doc->BeginUpdate(UPDATE_STYLE);
   353       doc->RemoveStyleSheet(mStyleSheet);
   354       doc->EndUpdate(UPDATE_STYLE);
   355     }
   357     nsStyleLinkElement::SetStyleSheet(nullptr);
   358   }
   360   if (!uri && !isInline) {
   361     return NS_OK; // If href is empty and this is not inline style then just bail
   362   }
   364   nsAutoString title, type, media;
   365   bool isScoped;
   366   bool isAlternate;
   368   GetStyleSheetInfo(title, type, media, &isScoped, &isAlternate);
   370   if (!type.LowerCaseEqualsLiteral("text/css")) {
   371     return NS_OK;
   372   }
   374   Element* scopeElement = isScoped ? thisContent->GetParentElement() : nullptr;
   375   if (scopeElement) {
   376     NS_ASSERTION(isInline, "non-inline style must not have scope element");
   377     SetIsElementInStyleScopeFlagOnSubtree(scopeElement);
   378   }
   380   bool doneLoading = false;
   381   nsresult rv = NS_OK;
   382   if (isInline) {
   383     nsAutoString text;
   384     if (!nsContentUtils::GetNodeTextContent(thisContent, false, text)) {
   385       return NS_ERROR_OUT_OF_MEMORY;
   386     }
   388     MOZ_ASSERT(thisContent->Tag() != nsGkAtoms::link,
   389                "<link> is not 'inline', and needs different CSP checks");
   390     if (!nsStyleUtil::CSPAllowsInlineStyle(thisContent,
   391                                            thisContent->NodePrincipal(),
   392                                            doc->GetDocumentURI(),
   393                                            mLineNumber, text, &rv))
   394       return rv;
   396     // Parse the style sheet.
   397     rv = doc->CSSLoader()->
   398       LoadInlineStyle(thisContent, text, mLineNumber, title, media,
   399                       scopeElement, aObserver, &doneLoading, &isAlternate);
   400   }
   401   else {
   402     // XXXbz clone the URI here to work around content policies modifying URIs.
   403     nsCOMPtr<nsIURI> clonedURI;
   404     uri->Clone(getter_AddRefs(clonedURI));
   405     NS_ENSURE_TRUE(clonedURI, NS_ERROR_OUT_OF_MEMORY);
   406     rv = doc->CSSLoader()->
   407       LoadStyleLink(thisContent, clonedURI, title, media, isAlternate,
   408                     GetCORSMode(), aObserver, &isAlternate);
   409     if (NS_FAILED(rv)) {
   410       // Don't propagate LoadStyleLink() errors further than this, since some
   411       // consumers (e.g. nsXMLContentSink) will completely abort on innocuous
   412       // things like a stylesheet load being blocked by the security system.
   413       doneLoading = true;
   414       isAlternate = false;
   415       rv = NS_OK;
   416     }
   417   }
   419   NS_ENSURE_SUCCESS(rv, rv);
   421   *aWillNotify = !doneLoading;
   422   *aIsAlternate = isAlternate;
   424   return NS_OK;
   425 }
   427 void
   428 nsStyleLinkElement::UpdateStyleSheetScopedness(bool aIsNowScoped)
   429 {
   430   if (!mStyleSheet) {
   431     return;
   432   }
   434   nsCOMPtr<nsIContent> thisContent;
   435   CallQueryInterface(this, getter_AddRefs(thisContent));
   437   Element* oldScopeElement = mStyleSheet->GetScopeElement();
   438   Element* newScopeElement = aIsNowScoped ?
   439                                thisContent->GetParentElement() :
   440                                nullptr;
   442   if (oldScopeElement == newScopeElement) {
   443     return;
   444   }
   446   nsIDocument* document = thisContent->GetOwnerDocument();
   448   if (thisContent->HasFlag(NODE_IS_IN_SHADOW_TREE)) {
   449     ShadowRoot* containingShadow = thisContent->GetContainingShadow();
   450     containingShadow->RemoveSheet(mStyleSheet);
   452     mStyleSheet->SetScopeElement(newScopeElement);
   454     containingShadow->InsertSheet(mStyleSheet, thisContent);
   455   } else {
   456     document->BeginUpdate(UPDATE_STYLE);
   457     document->RemoveStyleSheet(mStyleSheet);
   459     mStyleSheet->SetScopeElement(newScopeElement);
   461     document->AddStyleSheet(mStyleSheet);
   462     document->EndUpdate(UPDATE_STYLE);
   463   }
   465   if (oldScopeElement) {
   466     UpdateIsElementInStyleScopeFlagOnSubtree(oldScopeElement);
   467   }
   468   if (newScopeElement) {
   469     SetIsElementInStyleScopeFlagOnSubtree(newScopeElement);
   470   }
   471 }

mercurial