1/*
2 * (C) 1999-2003 Lars Knoll (knoll@kde.org)
3 * (C) 2002-2003 Dirk Mueller (mueller@kde.org)
4 * Copyright (C) 2002-2017 Apple Inc. All rights reserved.
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Library General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Library General Public License for more details.
15 *
16 * You should have received a copy of the GNU Library General Public License
17 * along with this library; see the file COPYING.LIB. If not, write to
18 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 * Boston, MA 02110-1301, USA.
20 */
21
22#include "config.h"
23#include "StyleRuleImport.h"
24
25#include "CSSStyleSheet.h"
26#include "CachedCSSStyleSheet.h"
27#include "CachedResourceLoader.h"
28#include "CachedResourceRequest.h"
29#include "CachedResourceRequestInitiators.h"
30#include "Document.h"
31#include "MediaList.h"
32#include "MediaQueryParser.h"
33#include "SecurityOrigin.h"
34#include "StyleSheetContents.h"
35#include <wtf/StdLibExtras.h>
36
37namespace WebCore {
38
39Ref<StyleRuleImport> StyleRuleImport::create(const String& href, Ref<MediaQuerySet>&& media)
40{
41 return adoptRef(*new StyleRuleImport(href, WTFMove(media)));
42}
43
44StyleRuleImport::StyleRuleImport(const String& href, Ref<MediaQuerySet>&& media)
45 : StyleRuleBase(Import)
46 , m_parentStyleSheet(0)
47 , m_styleSheetClient(this)
48 , m_strHref(href)
49 , m_mediaQueries(WTFMove(media))
50 , m_cachedSheet(0)
51 , m_loading(false)
52{
53 if (!m_mediaQueries)
54 m_mediaQueries = MediaQuerySet::create(String(), MediaQueryParserContext());
55}
56
57StyleRuleImport::~StyleRuleImport()
58{
59 if (m_styleSheet)
60 m_styleSheet->clearOwnerRule();
61 if (m_cachedSheet)
62 m_cachedSheet->removeClient(m_styleSheetClient);
63}
64
65void StyleRuleImport::setCSSStyleSheet(const String& href, const URL& baseURL, const String& charset, const CachedCSSStyleSheet* cachedStyleSheet)
66{
67 if (m_styleSheet)
68 m_styleSheet->clearOwnerRule();
69
70 CSSParserContext context = m_parentStyleSheet ? m_parentStyleSheet->parserContext() : HTMLStandardMode;
71 context.charset = charset;
72 if (!baseURL.isNull())
73 context.baseURL = baseURL;
74
75 Document* document = m_parentStyleSheet ? m_parentStyleSheet->singleOwnerDocument() : nullptr;
76 m_styleSheet = StyleSheetContents::create(this, href, context);
77 if (m_parentStyleSheet->isContentOpaque() || !cachedStyleSheet->isCORSSameOrigin())
78 m_styleSheet->setAsOpaque();
79 m_styleSheet->parseAuthorStyleSheet(cachedStyleSheet, document ? &document->securityOrigin() : nullptr);
80
81 m_loading = false;
82
83 if (m_parentStyleSheet) {
84 m_parentStyleSheet->notifyLoadedSheet(cachedStyleSheet);
85 m_parentStyleSheet->checkLoaded();
86 }
87}
88
89bool StyleRuleImport::isLoading() const
90{
91 return m_loading || (m_styleSheet && m_styleSheet->isLoading());
92}
93
94void StyleRuleImport::requestStyleSheet()
95{
96 if (!m_parentStyleSheet)
97 return;
98 auto* document = m_parentStyleSheet->singleOwnerDocument();
99 if (!document)
100 return;
101 auto* page = document->page();
102 if (!page)
103 return;
104
105 URL absURL;
106 if (!m_parentStyleSheet->baseURL().isNull())
107 // use parent styleheet's URL as the base URL
108 absURL = URL(m_parentStyleSheet->baseURL(), m_strHref);
109 else
110 absURL = document->completeURL(m_strHref);
111
112 // Check for a cycle in our import chain. If we encounter a stylesheet
113 // in our parent chain with the same URL, then just bail.
114 StyleSheetContents* rootSheet = m_parentStyleSheet;
115 for (StyleSheetContents* sheet = m_parentStyleSheet; sheet; sheet = sheet->parentStyleSheet()) {
116 if (equalIgnoringFragmentIdentifier(absURL, sheet->baseURL())
117 || equalIgnoringFragmentIdentifier(absURL, document->completeURL(sheet->originalURL())))
118 return;
119 rootSheet = sheet;
120 }
121
122 // FIXME: Skip Content Security Policy check when stylesheet is in a user agent shadow tree.
123 // See <https://bugs.webkit.org/show_bug.cgi?id=146663>.
124 CachedResourceRequest request(absURL, CachedResourceLoader::defaultCachedResourceOptions(), WTF::nullopt, String(m_parentStyleSheet->charset()));
125 request.setInitiator(cachedResourceRequestInitiators().css);
126 if (m_cachedSheet)
127 m_cachedSheet->removeClient(m_styleSheetClient);
128 if (m_parentStyleSheet->isUserStyleSheet()) {
129 ResourceLoaderOptions options {
130 SendCallbackPolicy::DoNotSendCallbacks,
131 ContentSniffingPolicy::SniffContent,
132 DataBufferingPolicy::BufferData,
133 StoredCredentialsPolicy::Use,
134 ClientCredentialPolicy::MayAskClientForCredentials,
135 FetchOptions::Credentials::Include,
136 SecurityCheckPolicy::SkipSecurityCheck,
137 FetchOptions::Mode::NoCors,
138 CertificateInfoPolicy::DoNotIncludeCertificateInfo,
139 ContentSecurityPolicyImposition::SkipPolicyCheck,
140 DefersLoadingPolicy::AllowDefersLoading,
141 CachingPolicy::AllowCaching
142 };
143 options.loadedFromOpaqueSource = m_parentStyleSheet->isContentOpaque() ? LoadedFromOpaqueSource::Yes : LoadedFromOpaqueSource::No;
144
145 request.setOptions(WTFMove(options));
146
147 m_cachedSheet = document->cachedResourceLoader().requestUserCSSStyleSheet(*page, WTFMove(request));
148 } else {
149 auto options = request.options();
150 options.loadedFromOpaqueSource = m_parentStyleSheet->isContentOpaque() ? LoadedFromOpaqueSource::Yes : LoadedFromOpaqueSource::No;
151 request.setOptions(WTFMove(options));
152 m_cachedSheet = document->cachedResourceLoader().requestCSSStyleSheet(WTFMove(request)).value_or(nullptr);
153 }
154 if (m_cachedSheet) {
155 // if the import rule is issued dynamically, the sheet may be
156 // removed from the pending sheet count, so let the doc know
157 // the sheet being imported is pending.
158 if (m_parentStyleSheet && m_parentStyleSheet->loadCompleted() && rootSheet == m_parentStyleSheet)
159 m_parentStyleSheet->startLoadingDynamicSheet();
160 m_loading = true;
161 m_cachedSheet->addClient(m_styleSheetClient);
162 }
163}
164
165} // namespace WebCore
166