1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 : *
3 : * ***** BEGIN LICENSE BLOCK *****
4 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 : *
6 : * The contents of this file are subject to the Mozilla Public License Version
7 : * 1.1 (the "License"); you may not use this file except in compliance with
8 : * the License. You may obtain a copy of the License at
9 : * http://www.mozilla.org/MPL/
10 : *
11 : * Software distributed under the License is distributed on an "AS IS" basis,
12 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 : * for the specific language governing rights and limitations under the
14 : * License.
15 : *
16 : * The Original Code is Mozilla Communicator client code, released
17 : * March 31, 1998.
18 : *
19 : * The Initial Developer of the Original Code is
20 : * Netscape Communications Corporation.
21 : * Portions created by the Initial Developer are Copyright (C) 1998-1999
22 : * the Initial Developer. All Rights Reserved.
23 : *
24 : * Contributor(s):
25 : *
26 : * Alternatively, the contents of this file may be used under the terms of
27 : * either of the GNU General Public License Version 2 or later (the "GPL"),
28 : * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 : * in which case the provisions of the GPL or the LGPL are applicable instead
30 : * of those above. If you wish to allow use of your version of this file only
31 : * under the terms of either the GPL or the LGPL, and not to allow others to
32 : * use your version of this file under the terms of the MPL, indicate your
33 : * decision by deleting the provisions above and replace them with the notice
34 : * and other provisions required by the GPL or the LGPL. If you do not delete
35 : * the provisions above, a recipient may use your version of this file under
36 : * the terms of any one of the MPL, the GPL or the LGPL.
37 : *
38 : * ***** END LICENSE BLOCK ***** */
39 :
40 : /*
41 : * A base class which implements nsIStyleSheetLinkingElement and can
42 : * be subclassed by various content nodes that want to load
43 : * stylesheets (<style>, <link>, processing instructions, etc).
44 : */
45 :
46 : #include "nsStyleLinkElement.h"
47 :
48 : #include "nsIContent.h"
49 : #include "mozilla/css/Loader.h"
50 : #include "nsCSSStyleSheet.h"
51 : #include "nsIDocument.h"
52 : #include "nsIDOMComment.h"
53 : #include "nsIDOMNode.h"
54 : #include "nsIDOMStyleSheet.h"
55 : #include "nsNetUtil.h"
56 : #include "nsUnicharUtils.h"
57 : #include "nsCRT.h"
58 : #include "nsXPCOMCIDInternal.h"
59 : #include "nsUnicharInputStream.h"
60 : #include "nsContentUtils.h"
61 :
62 62 : nsStyleLinkElement::nsStyleLinkElement()
63 : : mDontLoadStyle(false)
64 : , mUpdatesEnabled(true)
65 62 : , mLineNumber(1)
66 : {
67 62 : }
68 :
69 124 : nsStyleLinkElement::~nsStyleLinkElement()
70 : {
71 62 : nsStyleLinkElement::SetStyleSheet(nsnull);
72 124 : }
73 :
74 : NS_IMETHODIMP
75 62 : nsStyleLinkElement::SetStyleSheet(nsIStyleSheet* aStyleSheet)
76 : {
77 124 : nsRefPtr<nsCSSStyleSheet> cssSheet = do_QueryObject(mStyleSheet);
78 62 : if (cssSheet) {
79 0 : cssSheet->SetOwningNode(nsnull);
80 : }
81 :
82 62 : mStyleSheet = aStyleSheet;
83 62 : cssSheet = do_QueryObject(mStyleSheet);
84 62 : if (cssSheet) {
85 0 : nsCOMPtr<nsIDOMNode> node;
86 : CallQueryInterface(this,
87 0 : static_cast<nsIDOMNode**>(getter_AddRefs(node)));
88 0 : if (node) {
89 0 : cssSheet->SetOwningNode(node);
90 : }
91 : }
92 :
93 62 : return NS_OK;
94 : }
95 :
96 : NS_IMETHODIMP
97 0 : nsStyleLinkElement::GetStyleSheet(nsIStyleSheet*& aStyleSheet)
98 : {
99 0 : aStyleSheet = mStyleSheet;
100 0 : NS_IF_ADDREF(aStyleSheet);
101 :
102 0 : return NS_OK;
103 : }
104 :
105 : NS_IMETHODIMP
106 62 : nsStyleLinkElement::InitStyleLinkElement(bool aDontLoadStyle)
107 : {
108 62 : mDontLoadStyle = aDontLoadStyle;
109 :
110 62 : return NS_OK;
111 : }
112 :
113 : NS_IMETHODIMP
114 0 : nsStyleLinkElement::GetSheet(nsIDOMStyleSheet** aSheet)
115 : {
116 0 : NS_ENSURE_ARG_POINTER(aSheet);
117 0 : *aSheet = nsnull;
118 :
119 0 : if (mStyleSheet) {
120 0 : CallQueryInterface(mStyleSheet, aSheet);
121 : }
122 :
123 : // Always return NS_OK to avoid throwing JS exceptions if mStyleSheet
124 : // is not a nsIDOMStyleSheet
125 0 : return NS_OK;
126 : }
127 :
128 : NS_IMETHODIMP
129 124 : nsStyleLinkElement::SetEnableUpdates(bool aEnableUpdates)
130 : {
131 124 : mUpdatesEnabled = aEnableUpdates;
132 :
133 124 : return NS_OK;
134 : }
135 :
136 : NS_IMETHODIMP
137 0 : nsStyleLinkElement::GetCharset(nsAString& aCharset)
138 : {
139 : // descendants have to implement this themselves
140 0 : return NS_ERROR_NOT_IMPLEMENTED;
141 : }
142 :
143 : /* virtual */ void
144 0 : nsStyleLinkElement::OverrideBaseURI(nsIURI* aNewBaseURI)
145 : {
146 : NS_NOTREACHED("Base URI can't be overriden in this implementation "
147 0 : "of nsIStyleSheetLinkingElement.");
148 0 : }
149 :
150 : /* virtual */ void
151 0 : nsStyleLinkElement::SetLineNumber(PRUint32 aLineNumber)
152 : {
153 0 : mLineNumber = aLineNumber;
154 0 : }
155 :
156 0 : PRUint32 ToLinkMask(const nsAString& aLink)
157 : {
158 0 : if (aLink.EqualsLiteral("prefetch"))
159 0 : return PREFETCH;
160 0 : else if (aLink.EqualsLiteral("dns-prefetch"))
161 0 : return DNS_PREFETCH;
162 0 : else if (aLink.EqualsLiteral("stylesheet"))
163 0 : return STYLESHEET;
164 0 : else if (aLink.EqualsLiteral("next"))
165 0 : return NEXT;
166 0 : else if (aLink.EqualsLiteral("alternate"))
167 0 : return ALTERNATE;
168 : else
169 0 : return 0;
170 : }
171 :
172 0 : PRUint32 nsStyleLinkElement::ParseLinkTypes(const nsAString& aTypes)
173 : {
174 0 : PRUint32 linkMask = 0;
175 0 : nsAString::const_iterator start, done;
176 0 : aTypes.BeginReading(start);
177 0 : aTypes.EndReading(done);
178 0 : if (start == done)
179 0 : return linkMask;
180 :
181 0 : nsAString::const_iterator current(start);
182 0 : bool inString = !nsContentUtils::IsHTMLWhitespace(*current);
183 0 : nsAutoString subString;
184 :
185 0 : while (current != done) {
186 0 : if (nsContentUtils::IsHTMLWhitespace(*current)) {
187 0 : if (inString) {
188 0 : nsContentUtils::ASCIIToLower(Substring(start, current), subString);
189 0 : linkMask |= ToLinkMask(subString);
190 0 : inString = false;
191 : }
192 : }
193 : else {
194 0 : if (!inString) {
195 0 : start = current;
196 0 : inString = true;
197 : }
198 : }
199 0 : ++current;
200 : }
201 0 : if (inString) {
202 0 : nsContentUtils::ASCIIToLower(Substring(start, current), subString);
203 0 : linkMask |= ToLinkMask(subString);
204 : }
205 0 : return linkMask;
206 : }
207 :
208 : NS_IMETHODIMP
209 62 : nsStyleLinkElement::UpdateStyleSheet(nsICSSLoaderObserver* aObserver,
210 : bool* aWillNotify,
211 : bool* aIsAlternate)
212 : {
213 : return DoUpdateStyleSheet(nsnull, aObserver, aWillNotify, aIsAlternate,
214 62 : false);
215 : }
216 :
217 : nsresult
218 124 : nsStyleLinkElement::UpdateStyleSheetInternal(nsIDocument *aOldDocument,
219 : bool aForceUpdate)
220 : {
221 : bool notify, alternate;
222 : return DoUpdateStyleSheet(aOldDocument, nsnull, ¬ify, &alternate,
223 124 : aForceUpdate);
224 : }
225 :
226 : nsresult
227 186 : nsStyleLinkElement::DoUpdateStyleSheet(nsIDocument *aOldDocument,
228 : nsICSSLoaderObserver* aObserver,
229 : bool* aWillNotify,
230 : bool* aIsAlternate,
231 : bool aForceUpdate)
232 : {
233 186 : *aWillNotify = false;
234 :
235 186 : if (mStyleSheet && aOldDocument) {
236 : // We're removing the link element from the document, unload the
237 : // stylesheet. We want to do this even if updates are disabled, since
238 : // otherwise a sheet with a stale linking element pointer will be hanging
239 : // around -- not good!
240 0 : aOldDocument->BeginUpdate(UPDATE_STYLE);
241 0 : aOldDocument->RemoveStyleSheet(mStyleSheet);
242 0 : aOldDocument->EndUpdate(UPDATE_STYLE);
243 0 : nsStyleLinkElement::SetStyleSheet(nsnull);
244 : }
245 :
246 186 : if (mDontLoadStyle || !mUpdatesEnabled) {
247 62 : return NS_OK;
248 : }
249 :
250 248 : nsCOMPtr<nsIContent> thisContent;
251 124 : QueryInterface(NS_GET_IID(nsIContent), getter_AddRefs(thisContent));
252 :
253 124 : NS_ENSURE_TRUE(thisContent, NS_ERROR_FAILURE);
254 :
255 248 : nsCOMPtr<nsIDocument> doc = thisContent->GetDocument();
256 :
257 124 : if (!doc || !doc->CSSLoader()->GetEnabled()) {
258 124 : return NS_OK;
259 : }
260 :
261 : bool isInline;
262 0 : nsCOMPtr<nsIURI> uri = GetStyleSheetURL(&isInline);
263 :
264 0 : if (!aForceUpdate && mStyleSheet && !isInline && uri) {
265 0 : nsIURI* oldURI = mStyleSheet->GetSheetURI();
266 0 : if (oldURI) {
267 : bool equal;
268 0 : nsresult rv = oldURI->Equals(uri, &equal);
269 0 : if (NS_SUCCEEDED(rv) && equal) {
270 0 : return NS_OK; // We already loaded this stylesheet
271 : }
272 : }
273 : }
274 :
275 0 : if (mStyleSheet) {
276 0 : doc->BeginUpdate(UPDATE_STYLE);
277 0 : doc->RemoveStyleSheet(mStyleSheet);
278 0 : doc->EndUpdate(UPDATE_STYLE);
279 0 : nsStyleLinkElement::SetStyleSheet(nsnull);
280 : }
281 :
282 0 : if (!uri && !isInline) {
283 0 : return NS_OK; // If href is empty and this is not inline style then just bail
284 : }
285 :
286 0 : nsAutoString title, type, media;
287 : bool isAlternate;
288 :
289 0 : GetStyleSheetInfo(title, type, media, &isAlternate);
290 :
291 0 : if (!type.LowerCaseEqualsLiteral("text/css")) {
292 0 : return NS_OK;
293 : }
294 :
295 0 : bool doneLoading = false;
296 0 : nsresult rv = NS_OK;
297 0 : if (isInline) {
298 0 : nsAutoString text;
299 0 : nsContentUtils::GetNodeTextContent(thisContent, false, text);
300 :
301 : // Parse the style sheet.
302 : rv = doc->CSSLoader()->
303 : LoadInlineStyle(thisContent, text, mLineNumber, title, media,
304 0 : aObserver, &doneLoading, &isAlternate);
305 : }
306 : else {
307 : // XXXbz clone the URI here to work around content policies modifying URIs.
308 0 : nsCOMPtr<nsIURI> clonedURI;
309 0 : uri->Clone(getter_AddRefs(clonedURI));
310 0 : NS_ENSURE_TRUE(clonedURI, NS_ERROR_OUT_OF_MEMORY);
311 : rv = doc->CSSLoader()->
312 : LoadStyleLink(thisContent, clonedURI, title, media, isAlternate, aObserver,
313 0 : &isAlternate);
314 0 : if (NS_FAILED(rv)) {
315 : // Don't propagate LoadStyleLink() errors further than this, since some
316 : // consumers (e.g. nsXMLContentSink) will completely abort on innocuous
317 : // things like a stylesheet load being blocked by the security system.
318 0 : doneLoading = true;
319 0 : isAlternate = false;
320 0 : rv = NS_OK;
321 : }
322 : }
323 :
324 0 : NS_ENSURE_SUCCESS(rv, rv);
325 :
326 0 : *aWillNotify = !doneLoading;
327 0 : *aIsAlternate = isAlternate;
328 :
329 0 : return NS_OK;
330 : }
|