Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/base/nsStyleLinkElement.cpp
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
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/. */
6
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
 */
12
13
#include "nsStyleLinkElement.h"
14
15
#include "mozilla/StyleSheet.h"
16
#include "mozilla/StyleSheetInlines.h"
17
#include "mozilla/css/Loader.h"
18
#include "mozilla/dom/Element.h"
19
#include "mozilla/dom/FragmentOrElement.h"
20
#include "mozilla/dom/HTMLLinkElement.h"
21
#include "mozilla/dom/ShadowRoot.h"
22
#include "mozilla/dom/SRILogHelper.h"
23
#include "mozilla/Preferences.h"
24
#include "nsIContent.h"
25
#include "nsIDocument.h"
26
#include "nsUnicharUtils.h"
27
#include "nsCRT.h"
28
#include "nsXPCOMCIDInternal.h"
29
#include "nsUnicharInputStream.h"
30
#include "nsContentUtils.h"
31
#include "nsStyleUtil.h"
32
#include "nsQueryObject.h"
33
34
using namespace mozilla;
35
using namespace mozilla::dom;
36
37
nsStyleLinkElement::SheetInfo::SheetInfo(
38
  const nsIDocument& aDocument,
39
  nsIContent* aContent,
40
  already_AddRefed<nsIURI> aURI,
41
  already_AddRefed<nsIPrincipal> aTriggeringPrincipal,
42
  mozilla::net::ReferrerPolicy aReferrerPolicy,
43
  mozilla::CORSMode aCORSMode,
44
  const nsAString& aTitle,
45
  const nsAString& aMedia,
46
  HasAlternateRel aHasAlternateRel,
47
  IsInline aIsInline
48
)
49
  : mContent(aContent)
50
  , mURI(aURI)
51
  , mTriggeringPrincipal(aTriggeringPrincipal)
52
  , mReferrerPolicy(aReferrerPolicy)
53
  , mCORSMode(aCORSMode)
54
  , mTitle(aTitle)
55
  , mMedia(aMedia)
56
  , mHasAlternateRel(aHasAlternateRel == HasAlternateRel::Yes)
57
  , mIsInline(aIsInline == IsInline::Yes)
58
0
{
59
0
  MOZ_ASSERT(!mIsInline || aContent);
60
0
  MOZ_ASSERT_IF(aContent, aContent->OwnerDoc() == &aDocument);
61
0
62
0
  if (mReferrerPolicy == net::ReferrerPolicy::RP_Unset) {
63
0
    mReferrerPolicy = aDocument.GetReferrerPolicy();
64
0
  }
65
0
66
0
  if (!mIsInline && aContent && aContent->IsElement()) {
67
0
    aContent->AsElement()->GetAttr(kNameSpaceID_None,
68
0
                                   nsGkAtoms::integrity,
69
0
                                   mIntegrity);
70
0
  }
71
0
}
72
73
0
nsStyleLinkElement::SheetInfo::~SheetInfo() = default;
74
75
nsStyleLinkElement::nsStyleLinkElement()
76
  : mDontLoadStyle(false)
77
  , mUpdatesEnabled(true)
78
  , mLineNumber(1)
79
  , mColumnNumber(1)
80
0
{
81
0
}
82
83
nsStyleLinkElement::~nsStyleLinkElement()
84
0
{
85
0
  nsStyleLinkElement::SetStyleSheet(nullptr);
86
0
}
87
88
void
89
nsStyleLinkElement::GetTitleAndMediaForElement(const Element& aSelf,
90
                                               nsString& aTitle,
91
                                               nsString& aMedia)
92
0
{
93
0
  // Only honor title as stylesheet name for elements in the document (that is,
94
0
  // ignore for Shadow DOM), per [1] and [2]. See [3].
95
0
  //
96
0
  // [1]: https://html.spec.whatwg.org/#attr-link-title
97
0
  // [2]: https://html.spec.whatwg.org/#attr-style-title
98
0
  // [3]: https://github.com/w3c/webcomponents/issues/535
99
0
  if (aSelf.IsInUncomposedDoc()) {
100
0
    aSelf.GetAttr(kNameSpaceID_None, nsGkAtoms::title, aTitle);
101
0
    aTitle.CompressWhitespace();
102
0
  }
103
0
104
0
  aSelf.GetAttr(kNameSpaceID_None, nsGkAtoms::media, aMedia);
105
0
  // The HTML5 spec is formulated in terms of the CSSOM spec, which specifies
106
0
  // that media queries should be ASCII lowercased during serialization.
107
0
  //
108
0
  // FIXME(emilio): How does it matter? This is going to be parsed anyway, CSS
109
0
  // should take care of serializing it properly.
110
0
  nsContentUtils::ASCIIToLower(aMedia);
111
0
}
112
113
bool
114
nsStyleLinkElement::IsCSSMimeTypeAttribute(const Element& aSelf)
115
0
{
116
0
  nsAutoString type;
117
0
  nsAutoString mimeType;
118
0
  nsAutoString notUsed;
119
0
  aSelf.GetAttr(kNameSpaceID_None, nsGkAtoms::type, type);
120
0
  nsContentUtils::SplitMimeType(type, mimeType, notUsed);
121
0
  return mimeType.IsEmpty() || mimeType.LowerCaseEqualsLiteral("text/css");
122
0
}
123
124
void
125
nsStyleLinkElement::Unlink()
126
0
{
127
0
  nsStyleLinkElement::SetStyleSheet(nullptr);
128
0
}
129
130
void
131
nsStyleLinkElement::Traverse(nsCycleCollectionTraversalCallback &cb)
132
0
{
133
0
  nsStyleLinkElement* tmp = this;
134
0
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStyleSheet);
135
0
}
136
137
void
138
nsStyleLinkElement::SetStyleSheet(StyleSheet* aStyleSheet)
139
0
{
140
0
  if (mStyleSheet) {
141
0
    mStyleSheet->SetOwningNode(nullptr);
142
0
  }
143
0
144
0
  mStyleSheet = aStyleSheet;
145
0
  if (mStyleSheet) {
146
0
    nsCOMPtr<nsINode> node = do_QueryObject(this);
147
0
    if (node) {
148
0
      mStyleSheet->SetOwningNode(node);
149
0
    }
150
0
  }
151
0
}
152
153
StyleSheet*
154
nsStyleLinkElement::GetStyleSheet()
155
0
{
156
0
  return mStyleSheet;
157
0
}
158
159
void
160
nsStyleLinkElement::InitStyleLinkElement(bool aDontLoadStyle)
161
0
{
162
0
  mDontLoadStyle = aDontLoadStyle;
163
0
}
164
165
void
166
nsStyleLinkElement::SetEnableUpdates(bool aEnableUpdates)
167
0
{
168
0
  mUpdatesEnabled = aEnableUpdates;
169
0
}
170
171
void
172
nsStyleLinkElement::GetCharset(nsAString& aCharset)
173
0
{
174
0
  aCharset.Truncate();
175
0
}
176
177
/* virtual */ void
178
nsStyleLinkElement::OverrideBaseURI(nsIURI* aNewBaseURI)
179
0
{
180
0
  MOZ_ASSERT_UNREACHABLE("Base URI can't be overriden in this implementation "
181
0
                         "of nsIStyleSheetLinkingElement.");
182
0
}
183
184
/* virtual */ void
185
nsStyleLinkElement::SetLineNumber(uint32_t aLineNumber)
186
0
{
187
0
  mLineNumber = aLineNumber;
188
0
}
189
190
/* virtual */ uint32_t
191
nsStyleLinkElement::GetLineNumber()
192
0
{
193
0
  return mLineNumber;
194
0
}
195
196
/* virtual */ void
197
nsStyleLinkElement::SetColumnNumber(uint32_t aColumnNumber)
198
0
{
199
0
  mColumnNumber = aColumnNumber;
200
0
}
201
202
/* virtual */ uint32_t
203
nsStyleLinkElement::GetColumnNumber()
204
0
{
205
0
  return mColumnNumber;
206
0
}
207
208
static uint32_t ToLinkMask(const nsAString& aLink)
209
0
{
210
0
  // Keep this in sync with sRelValues in HTMLLinkElement.cpp
211
0
  if (aLink.EqualsLiteral("prefetch"))
212
0
    return nsStyleLinkElement::ePREFETCH;
213
0
  else if (aLink.EqualsLiteral("dns-prefetch"))
214
0
    return nsStyleLinkElement::eDNS_PREFETCH;
215
0
  else if (aLink.EqualsLiteral("stylesheet"))
216
0
    return nsStyleLinkElement::eSTYLESHEET;
217
0
  else if (aLink.EqualsLiteral("next"))
218
0
    return nsStyleLinkElement::eNEXT;
219
0
  else if (aLink.EqualsLiteral("alternate"))
220
0
    return nsStyleLinkElement::eALTERNATE;
221
0
  else if (aLink.EqualsLiteral("preconnect"))
222
0
    return nsStyleLinkElement::ePRECONNECT;
223
0
  else if (aLink.EqualsLiteral("preload"))
224
0
    return nsStyleLinkElement::ePRELOAD;
225
0
  else
226
0
    return 0;
227
0
}
228
229
uint32_t nsStyleLinkElement::ParseLinkTypes(const nsAString& aTypes)
230
0
{
231
0
  uint32_t linkMask = 0;
232
0
  nsAString::const_iterator start, done;
233
0
  aTypes.BeginReading(start);
234
0
  aTypes.EndReading(done);
235
0
  if (start == done)
236
0
    return linkMask;
237
0
238
0
  nsAString::const_iterator current(start);
239
0
  bool inString = !nsContentUtils::IsHTMLWhitespace(*current);
240
0
  nsAutoString subString;
241
0
242
0
  while (current != done) {
243
0
    if (nsContentUtils::IsHTMLWhitespace(*current)) {
244
0
      if (inString) {
245
0
        nsContentUtils::ASCIIToLower(Substring(start, current), subString);
246
0
        linkMask |= ToLinkMask(subString);
247
0
        inString = false;
248
0
      }
249
0
    }
250
0
    else {
251
0
      if (!inString) {
252
0
        start = current;
253
0
        inString = true;
254
0
      }
255
0
    }
256
0
    ++current;
257
0
  }
258
0
  if (inString) {
259
0
    nsContentUtils::ASCIIToLower(Substring(start, current), subString);
260
0
    linkMask |= ToLinkMask(subString);
261
0
  }
262
0
  return linkMask;
263
0
}
264
265
Result<nsStyleLinkElement::Update, nsresult>
266
nsStyleLinkElement::UpdateStyleSheet(nsICSSLoaderObserver* aObserver)
267
0
{
268
0
  return DoUpdateStyleSheet(nullptr, nullptr, aObserver, ForceUpdate::No);
269
0
}
270
271
Result<nsStyleLinkElement::Update, nsresult>
272
nsStyleLinkElement::UpdateStyleSheetInternal(nsIDocument* aOldDocument,
273
                                             ShadowRoot* aOldShadowRoot,
274
                                             ForceUpdate aForceUpdate)
275
0
{
276
0
  return DoUpdateStyleSheet(
277
0
    aOldDocument, aOldShadowRoot, nullptr, aForceUpdate);
278
0
}
279
280
Result<nsStyleLinkElement::Update, nsresult>
281
nsStyleLinkElement::DoUpdateStyleSheet(nsIDocument* aOldDocument,
282
                                       ShadowRoot* aOldShadowRoot,
283
                                       nsICSSLoaderObserver* aObserver,
284
                                       ForceUpdate aForceUpdate)
285
0
{
286
0
  nsCOMPtr<nsIContent> thisContent = do_QueryInterface(this);
287
0
  // All instances of nsStyleLinkElement should implement nsIContent.
288
0
  MOZ_ASSERT(thisContent);
289
0
290
0
  if (thisContent->IsInSVGUseShadowTree()) {
291
0
    // Stylesheets in <use>-cloned subtrees are disabled until we figure out
292
0
    // how they should behave.
293
0
    return Update { };
294
0
  }
295
0
296
0
  if (mStyleSheet && (aOldDocument || aOldShadowRoot)) {
297
0
    MOZ_ASSERT(!(aOldDocument && aOldShadowRoot),
298
0
               "ShadowRoot content is never in document, thus "
299
0
               "there should not be a old document and old "
300
0
               "ShadowRoot simultaneously.");
301
0
302
0
    // We're removing the link element from the document or shadow tree,
303
0
    // unload the stylesheet.  We want to do this even if updates are
304
0
    // disabled, since otherwise a sheet with a stale linking element pointer
305
0
    // will be hanging around -- not good!
306
0
    if (aOldShadowRoot) {
307
0
      aOldShadowRoot->RemoveSheet(mStyleSheet);
308
0
    } else {
309
0
      aOldDocument->RemoveStyleSheet(mStyleSheet);
310
0
    }
311
0
312
0
    SetStyleSheet(nullptr);
313
0
  }
314
0
315
0
  nsIDocument* doc = thisContent->GetComposedDoc();
316
0
317
0
  // Loader could be null during unlink, see bug 1425866.
318
0
  if (!doc || !doc->CSSLoader() || !doc->CSSLoader()->GetEnabled()) {
319
0
    return Update { };
320
0
  }
321
0
322
0
  // When static documents are created, stylesheets are cloned manually.
323
0
  if (mDontLoadStyle || !mUpdatesEnabled || doc->IsStaticDocument()) {
324
0
    return Update { };
325
0
  }
326
0
327
0
  Maybe<SheetInfo> info = GetStyleSheetInfo();
328
0
  if (aForceUpdate == ForceUpdate::No &&
329
0
      mStyleSheet &&
330
0
      info &&
331
0
      !info->mIsInline &&
332
0
      info->mURI) {
333
0
    if (nsIURI* oldURI = mStyleSheet->GetSheetURI()) {
334
0
      bool equal;
335
0
      nsresult rv = oldURI->Equals(info->mURI, &equal);
336
0
      if (NS_SUCCEEDED(rv) && equal) {
337
0
        return Update { };
338
0
      }
339
0
    }
340
0
  }
341
0
342
0
  if (mStyleSheet) {
343
0
    if (thisContent->IsInShadowTree()) {
344
0
      ShadowRoot* containingShadow = thisContent->GetContainingShadow();
345
0
      // Could be null only during unlink.
346
0
      if (MOZ_LIKELY(containingShadow)) {
347
0
        containingShadow->RemoveSheet(mStyleSheet);
348
0
      }
349
0
    } else {
350
0
      doc->RemoveStyleSheet(mStyleSheet);
351
0
    }
352
0
353
0
    nsStyleLinkElement::SetStyleSheet(nullptr);
354
0
  }
355
0
356
0
  if (!info) {
357
0
    return Update { };
358
0
  }
359
0
360
0
  MOZ_ASSERT(info->mReferrerPolicy != net::RP_Unset ||
361
0
             info->mReferrerPolicy == doc->GetReferrerPolicy());
362
0
  if (!info->mURI && !info->mIsInline) {
363
0
    // If href is empty and this is not inline style then just bail
364
0
    return Update { };
365
0
  }
366
0
367
0
  if (info->mIsInline) {
368
0
    nsAutoString text;
369
0
    if (!nsContentUtils::GetNodeTextContent(thisContent, false, text, fallible)) {
370
0
      return Err(NS_ERROR_OUT_OF_MEMORY);
371
0
    }
372
0
373
0
374
0
    MOZ_ASSERT(thisContent->NodeInfo()->NameAtom() != nsGkAtoms::link,
375
0
               "<link> is not 'inline', and needs different CSP checks");
376
0
    MOZ_ASSERT(thisContent->IsElement());
377
0
    nsresult rv = NS_OK;
378
0
    if (!nsStyleUtil::CSPAllowsInlineStyle(thisContent->AsElement(),
379
0
                                           thisContent->NodePrincipal(),
380
0
                                           info->mTriggeringPrincipal,
381
0
                                           doc->GetDocumentURI(),
382
0
                                           mLineNumber, mColumnNumber, text,
383
0
                                           &rv)) {
384
0
      if (NS_FAILED(rv)) {
385
0
        return Err(rv);
386
0
      }
387
0
      return Update { };
388
0
    }
389
0
390
0
    // Parse the style sheet.
391
0
    return doc->CSSLoader()->LoadInlineStyle(*info, text, mLineNumber, aObserver);
392
0
  }
393
0
  if (thisContent->IsElement()) {
394
0
    nsAutoString integrity;
395
0
    thisContent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::integrity,
396
0
                                      integrity);
397
0
    if (!integrity.IsEmpty()) {
398
0
      MOZ_LOG(SRILogHelper::GetSriLog(), mozilla::LogLevel::Debug,
399
0
              ("nsStyleLinkElement::DoUpdateStyleSheet, integrity=%s",
400
0
               NS_ConvertUTF16toUTF8(integrity).get()));
401
0
    }
402
0
  }
403
0
  auto resultOrError = doc->CSSLoader()->LoadStyleLink(*info, aObserver);
404
0
  if (resultOrError.isErr()) {
405
0
    // Don't propagate LoadStyleLink() errors further than this, since some
406
0
    // consumers (e.g. nsXMLContentSink) will completely abort on innocuous
407
0
    // things like a stylesheet load being blocked by the security system.
408
0
    return Update { };
409
0
  }
410
0
  return resultOrError;
411
0
}