1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* ***** BEGIN LICENSE BLOCK *****
3 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 : *
5 : * The contents of this file are subject to the Mozilla Public License Version
6 : * 1.1 (the "License"); you may not use this file except in compliance with
7 : * the License. You may obtain a copy of the License at
8 : * http://www.mozilla.org/MPL/
9 : *
10 : * Software distributed under the License is distributed on an "AS IS" basis,
11 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 : * for the specific language governing rights and limitations under the
13 : * License.
14 : *
15 : * The Original Code is Mozilla Communicator client code.
16 : *
17 : * The Initial Developer of the Original Code is
18 : * Netscape Communications Corporation.
19 : * Portions created by the Initial Developer are Copyright (C) 1998
20 : * the Initial Developer. All Rights Reserved.
21 : *
22 : * Contributor(s):
23 : *
24 : * Alternatively, the contents of this file may be used under the terms of
25 : * either of the GNU General Public License Version 2 or later (the "GPL"),
26 : * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27 : * in which case the provisions of the GPL or the LGPL are applicable instead
28 : * of those above. If you wish to allow use of your version of this file only
29 : * under the terms of either the GPL or the LGPL, and not to allow others to
30 : * use your version of this file under the terms of the MPL, indicate your
31 : * decision by deleting the provisions above and replace them with the notice
32 : * and other provisions required by the GPL or the LGPL. If you do not delete
33 : * the provisions above, a recipient may use your version of this file under
34 : * the terms of any one of the MPL, the GPL or the LGPL.
35 : *
36 : * ***** END LICENSE BLOCK ***** */
37 : #include "nsIDOMHTMLLinkElement.h"
38 : #include "nsIDOMLinkStyle.h"
39 : #include "nsGenericHTMLElement.h"
40 : #include "nsILink.h"
41 : #include "nsGkAtoms.h"
42 : #include "nsStyleConsts.h"
43 : #include "nsIDOMStyleSheet.h"
44 : #include "nsIStyleSheet.h"
45 : #include "nsIStyleSheetLinkingElement.h"
46 : #include "nsStyleLinkElement.h"
47 : #include "nsReadableUtils.h"
48 : #include "nsUnicharUtils.h"
49 : #include "nsIURL.h"
50 : #include "nsNetUtil.h"
51 : #include "nsIDocument.h"
52 : #include "nsIDOMEvent.h"
53 : #include "nsIPrivateDOMEvent.h"
54 : #include "nsIDOMEventTarget.h"
55 : #include "nsContentUtils.h"
56 : #include "nsPIDOMWindow.h"
57 : #include "nsAsyncDOMEvent.h"
58 :
59 : #include "Link.h"
60 : using namespace mozilla::dom;
61 :
62 : class nsHTMLLinkElement : public nsGenericHTMLElement,
63 : public nsIDOMHTMLLinkElement,
64 : public nsILink,
65 : public nsStyleLinkElement,
66 : public Link
67 : {
68 : public:
69 : nsHTMLLinkElement(already_AddRefed<nsINodeInfo> aNodeInfo);
70 : virtual ~nsHTMLLinkElement();
71 :
72 : // nsISupports
73 : NS_DECL_ISUPPORTS_INHERITED
74 :
75 : // nsIDOMNode
76 0 : NS_FORWARD_NSIDOMNODE(nsGenericHTMLElement::)
77 :
78 : // nsIDOMElement
79 0 : NS_FORWARD_NSIDOMELEMENT(nsGenericHTMLElement::)
80 :
81 : // nsIDOMHTMLElement
82 0 : NS_FORWARD_NSIDOMHTMLELEMENT(nsGenericHTMLElement::)
83 :
84 : // nsIDOMHTMLLinkElement
85 : NS_DECL_NSIDOMHTMLLINKELEMENT
86 :
87 : // DOM memory reporter participant
88 : NS_DECL_SIZEOF_EXCLUDING_THIS
89 :
90 : // nsILink
91 : NS_IMETHOD LinkAdded();
92 : NS_IMETHOD LinkRemoved();
93 :
94 : virtual nsresult BindToTree(nsIDocument* aDocument, nsIContent* aParent,
95 : nsIContent* aBindingParent,
96 : bool aCompileEventHandlers);
97 : virtual void UnbindFromTree(bool aDeep = true,
98 : bool aNullParent = true);
99 : void CreateAndDispatchEvent(nsIDocument* aDoc, const nsAString& aEventName);
100 : nsresult SetAttr(PRInt32 aNameSpaceID, nsIAtom* aName,
101 : const nsAString& aValue, bool aNotify)
102 : {
103 : return SetAttr(aNameSpaceID, aName, nsnull, aValue, aNotify);
104 : }
105 : virtual nsresult SetAttr(PRInt32 aNameSpaceID, nsIAtom* aName,
106 : nsIAtom* aPrefix, const nsAString& aValue,
107 : bool aNotify);
108 : virtual nsresult UnsetAttr(PRInt32 aNameSpaceID, nsIAtom* aAttribute,
109 : bool aNotify);
110 :
111 : virtual nsresult PreHandleEvent(nsEventChainPreVisitor& aVisitor);
112 : virtual nsresult PostHandleEvent(nsEventChainPostVisitor& aVisitor);
113 : virtual bool IsLink(nsIURI** aURI) const;
114 : virtual void GetLinkTarget(nsAString& aTarget);
115 : virtual nsLinkState GetLinkState() const;
116 : virtual already_AddRefed<nsIURI> GetHrefURI() const;
117 :
118 : virtual nsresult Clone(nsINodeInfo *aNodeInfo, nsINode **aResult) const;
119 :
120 : virtual nsEventStates IntrinsicState() const;
121 :
122 : virtual nsXPCClassInfo* GetClassInfo();
123 : protected:
124 : virtual already_AddRefed<nsIURI> GetStyleSheetURL(bool* aIsInline);
125 : virtual void GetStyleSheetInfo(nsAString& aTitle,
126 : nsAString& aType,
127 : nsAString& aMedia,
128 : bool* aIsAlternate);
129 : };
130 :
131 :
132 0 : NS_IMPL_NS_NEW_HTML_ELEMENT(Link)
133 :
134 :
135 0 : nsHTMLLinkElement::nsHTMLLinkElement(already_AddRefed<nsINodeInfo> aNodeInfo)
136 : : nsGenericHTMLElement(aNodeInfo),
137 0 : Link(this)
138 : {
139 0 : }
140 :
141 0 : nsHTMLLinkElement::~nsHTMLLinkElement()
142 : {
143 0 : }
144 :
145 :
146 0 : NS_IMPL_ADDREF_INHERITED(nsHTMLLinkElement, nsGenericElement)
147 0 : NS_IMPL_RELEASE_INHERITED(nsHTMLLinkElement, nsGenericElement)
148 :
149 :
150 0 : DOMCI_NODE_DATA(HTMLLinkElement, nsHTMLLinkElement)
151 :
152 : // QueryInterface implementation for nsHTMLLinkElement
153 0 : NS_INTERFACE_TABLE_HEAD(nsHTMLLinkElement)
154 0 : NS_HTML_CONTENT_INTERFACE_TABLE5(nsHTMLLinkElement,
155 : nsIDOMHTMLLinkElement,
156 : nsIDOMLinkStyle,
157 : nsILink,
158 : nsIStyleSheetLinkingElement,
159 : Link)
160 0 : NS_HTML_CONTENT_INTERFACE_TABLE_TO_MAP_SEGUE(nsHTMLLinkElement,
161 : nsGenericHTMLElement)
162 0 : NS_HTML_CONTENT_INTERFACE_TABLE_TAIL_CLASSINFO(HTMLLinkElement)
163 :
164 :
165 0 : NS_IMPL_ELEMENT_CLONE(nsHTMLLinkElement)
166 :
167 :
168 : NS_IMETHODIMP
169 0 : nsHTMLLinkElement::GetDisabled(bool* aDisabled)
170 : {
171 0 : nsCOMPtr<nsIDOMStyleSheet> ss = do_QueryInterface(GetStyleSheet());
172 0 : nsresult result = NS_OK;
173 :
174 0 : if (ss) {
175 0 : result = ss->GetDisabled(aDisabled);
176 : } else {
177 0 : *aDisabled = false;
178 : }
179 :
180 0 : return result;
181 : }
182 :
183 : NS_IMETHODIMP
184 0 : nsHTMLLinkElement::SetDisabled(bool aDisabled)
185 : {
186 0 : nsCOMPtr<nsIDOMStyleSheet> ss = do_QueryInterface(GetStyleSheet());
187 0 : nsresult result = NS_OK;
188 :
189 0 : if (ss) {
190 0 : result = ss->SetDisabled(aDisabled);
191 : }
192 :
193 0 : return result;
194 : }
195 :
196 :
197 0 : NS_IMPL_STRING_ATTR(nsHTMLLinkElement, Charset, charset)
198 0 : NS_IMPL_URI_ATTR(nsHTMLLinkElement, Href, href)
199 0 : NS_IMPL_STRING_ATTR(nsHTMLLinkElement, Hreflang, hreflang)
200 0 : NS_IMPL_STRING_ATTR(nsHTMLLinkElement, Media, media)
201 0 : NS_IMPL_STRING_ATTR(nsHTMLLinkElement, Rel, rel)
202 0 : NS_IMPL_STRING_ATTR(nsHTMLLinkElement, Rev, rev)
203 0 : NS_IMPL_STRING_ATTR(nsHTMLLinkElement, Target, target)
204 0 : NS_IMPL_STRING_ATTR(nsHTMLLinkElement, Type, type)
205 :
206 : nsresult
207 0 : nsHTMLLinkElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
208 : nsIContent* aBindingParent,
209 : bool aCompileEventHandlers)
210 : {
211 0 : Link::ResetLinkState(false);
212 :
213 : nsresult rv = nsGenericHTMLElement::BindToTree(aDocument, aParent,
214 : aBindingParent,
215 0 : aCompileEventHandlers);
216 0 : NS_ENSURE_SUCCESS(rv, rv);
217 :
218 0 : if (aDocument) {
219 0 : aDocument->RegisterPendingLinkUpdate(this);
220 : }
221 :
222 0 : void (nsHTMLLinkElement::*update)() = &nsHTMLLinkElement::UpdateStyleSheetInternal;
223 0 : nsContentUtils::AddScriptRunner(NS_NewRunnableMethod(this, update));
224 :
225 0 : CreateAndDispatchEvent(aDocument, NS_LITERAL_STRING("DOMLinkAdded"));
226 :
227 0 : return rv;
228 : }
229 :
230 : NS_IMETHODIMP
231 0 : nsHTMLLinkElement::LinkAdded()
232 : {
233 0 : CreateAndDispatchEvent(OwnerDoc(), NS_LITERAL_STRING("DOMLinkAdded"));
234 0 : return NS_OK;
235 : }
236 :
237 : NS_IMETHODIMP
238 0 : nsHTMLLinkElement::LinkRemoved()
239 : {
240 0 : CreateAndDispatchEvent(OwnerDoc(), NS_LITERAL_STRING("DOMLinkRemoved"));
241 0 : return NS_OK;
242 : }
243 :
244 : void
245 0 : nsHTMLLinkElement::UnbindFromTree(bool aDeep, bool aNullParent)
246 : {
247 : // If this link is ever reinserted into a document, it might
248 : // be under a different xml:base, so forget the cached state now.
249 0 : Link::ResetLinkState(false);
250 :
251 : // Once we have XPCOMGC we shouldn't need to call UnbindFromTree during Unlink
252 : // and so this messy event dispatch can go away.
253 0 : nsCOMPtr<nsIDocument> oldDoc = GetCurrentDoc();
254 0 : if (oldDoc) {
255 0 : oldDoc->UnregisterPendingLinkUpdate(this);
256 : }
257 0 : CreateAndDispatchEvent(oldDoc, NS_LITERAL_STRING("DOMLinkRemoved"));
258 0 : nsGenericHTMLElement::UnbindFromTree(aDeep, aNullParent);
259 0 : UpdateStyleSheetInternal(oldDoc);
260 0 : }
261 :
262 : void
263 0 : nsHTMLLinkElement::CreateAndDispatchEvent(nsIDocument* aDoc,
264 : const nsAString& aEventName)
265 : {
266 0 : if (!aDoc)
267 0 : return;
268 :
269 : // In the unlikely case that both rev is specified *and* rel=stylesheet,
270 : // this code will cause the event to fire, on the principle that maybe the
271 : // page really does want to specify that its author is a stylesheet. Since
272 : // this should never actually happen and the performance hit is minimal,
273 : // doing the "right" thing costs virtually nothing here, even if it doesn't
274 : // make much sense.
275 : static nsIContent::AttrValuesArray strings[] =
276 : {&nsGkAtoms::_empty, &nsGkAtoms::stylesheet, nsnull};
277 :
278 0 : if (!nsContentUtils::HasNonEmptyAttr(this, kNameSpaceID_None,
279 0 : nsGkAtoms::rev) &&
280 : FindAttrValueIn(kNameSpaceID_None, nsGkAtoms::rel,
281 0 : strings, eIgnoreCase) != ATTR_VALUE_NO_MATCH)
282 0 : return;
283 :
284 : nsRefPtr<nsAsyncDOMEvent> event = new nsAsyncDOMEvent(this, aEventName, true,
285 0 : true);
286 : // Always run async in order to avoid running script when the content
287 : // sink isn't expecting it.
288 0 : event->PostDOMEvent();
289 : }
290 :
291 : nsresult
292 0 : nsHTMLLinkElement::SetAttr(PRInt32 aNameSpaceID, nsIAtom* aName,
293 : nsIAtom* aPrefix, const nsAString& aValue,
294 : bool aNotify)
295 : {
296 : nsresult rv = nsGenericHTMLElement::SetAttr(aNameSpaceID, aName, aPrefix,
297 0 : aValue, aNotify);
298 :
299 : // The ordering of the parent class's SetAttr call and Link::ResetLinkState
300 : // is important here! The attribute is not set until SetAttr returns, and
301 : // we will need the updated attribute value because notifying the document
302 : // that content states have changed will call IntrinsicState, which will try
303 : // to get updated information about the visitedness from Link.
304 0 : if (aName == nsGkAtoms::href && kNameSpaceID_None == aNameSpaceID) {
305 0 : Link::ResetLinkState(!!aNotify);
306 : }
307 :
308 0 : if (NS_SUCCEEDED(rv)) {
309 0 : bool dropSheet = false;
310 0 : if (aNameSpaceID == kNameSpaceID_None && aName == nsGkAtoms::rel &&
311 0 : GetStyleSheet()) {
312 0 : PRUint32 linkTypes = nsStyleLinkElement::ParseLinkTypes(aValue);
313 0 : dropSheet = !(linkTypes & STYLESHEET);
314 : }
315 :
316 : UpdateStyleSheetInternal(nsnull,
317 : dropSheet ||
318 : (aNameSpaceID == kNameSpaceID_None &&
319 : (aName == nsGkAtoms::title ||
320 : aName == nsGkAtoms::media ||
321 0 : aName == nsGkAtoms::type)));
322 : }
323 :
324 0 : return rv;
325 : }
326 :
327 : nsresult
328 0 : nsHTMLLinkElement::UnsetAttr(PRInt32 aNameSpaceID, nsIAtom* aAttribute,
329 : bool aNotify)
330 : {
331 : nsresult rv = nsGenericHTMLElement::UnsetAttr(aNameSpaceID, aAttribute,
332 0 : aNotify);
333 0 : if (NS_SUCCEEDED(rv)) {
334 : UpdateStyleSheetInternal(nsnull,
335 : aNameSpaceID == kNameSpaceID_None &&
336 : (aAttribute == nsGkAtoms::rel ||
337 : aAttribute == nsGkAtoms::title ||
338 : aAttribute == nsGkAtoms::media ||
339 0 : aAttribute == nsGkAtoms::type));
340 : }
341 :
342 : // The ordering of the parent class's UnsetAttr call and Link::ResetLinkState
343 : // is important here! The attribute is not unset until UnsetAttr returns, and
344 : // we will need the updated attribute value because notifying the document
345 : // that content states have changed will call IntrinsicState, which will try
346 : // to get updated information about the visitedness from Link.
347 0 : if (aAttribute == nsGkAtoms::href && kNameSpaceID_None == aNameSpaceID) {
348 0 : Link::ResetLinkState(!!aNotify);
349 : }
350 :
351 0 : return rv;
352 : }
353 :
354 : nsresult
355 0 : nsHTMLLinkElement::PreHandleEvent(nsEventChainPreVisitor& aVisitor)
356 : {
357 0 : return PreHandleEventForAnchors(aVisitor);
358 : }
359 :
360 : nsresult
361 0 : nsHTMLLinkElement::PostHandleEvent(nsEventChainPostVisitor& aVisitor)
362 : {
363 0 : return PostHandleEventForAnchors(aVisitor);
364 : }
365 :
366 : bool
367 0 : nsHTMLLinkElement::IsLink(nsIURI** aURI) const
368 : {
369 0 : return IsHTMLLink(aURI);
370 : }
371 :
372 : void
373 0 : nsHTMLLinkElement::GetLinkTarget(nsAString& aTarget)
374 : {
375 0 : GetAttr(kNameSpaceID_None, nsGkAtoms::target, aTarget);
376 0 : if (aTarget.IsEmpty()) {
377 0 : GetBaseTarget(aTarget);
378 : }
379 0 : }
380 :
381 : nsLinkState
382 0 : nsHTMLLinkElement::GetLinkState() const
383 : {
384 0 : return Link::GetLinkState();
385 : }
386 :
387 : already_AddRefed<nsIURI>
388 0 : nsHTMLLinkElement::GetHrefURI() const
389 : {
390 0 : return GetHrefURIForAnchors();
391 : }
392 :
393 : already_AddRefed<nsIURI>
394 0 : nsHTMLLinkElement::GetStyleSheetURL(bool* aIsInline)
395 : {
396 0 : *aIsInline = false;
397 0 : nsAutoString href;
398 0 : GetAttr(kNameSpaceID_None, nsGkAtoms::href, href);
399 0 : if (href.IsEmpty()) {
400 0 : return nsnull;
401 : }
402 0 : return Link::GetURI();
403 : }
404 :
405 : void
406 0 : nsHTMLLinkElement::GetStyleSheetInfo(nsAString& aTitle,
407 : nsAString& aType,
408 : nsAString& aMedia,
409 : bool* aIsAlternate)
410 : {
411 0 : aTitle.Truncate();
412 0 : aType.Truncate();
413 0 : aMedia.Truncate();
414 0 : *aIsAlternate = false;
415 :
416 0 : nsAutoString rel;
417 0 : GetAttr(kNameSpaceID_None, nsGkAtoms::rel, rel);
418 0 : PRUint32 linkTypes = nsStyleLinkElement::ParseLinkTypes(rel);
419 : // Is it a stylesheet link?
420 0 : if (!(linkTypes & STYLESHEET)) {
421 : return;
422 : }
423 :
424 0 : nsAutoString title;
425 0 : GetAttr(kNameSpaceID_None, nsGkAtoms::title, title);
426 0 : title.CompressWhitespace();
427 0 : aTitle.Assign(title);
428 :
429 : // If alternate, does it have title?
430 0 : if (linkTypes & ALTERNATE) {
431 0 : if (aTitle.IsEmpty()) { // alternates must have title
432 : return;
433 : } else {
434 0 : *aIsAlternate = true;
435 : }
436 : }
437 :
438 0 : GetAttr(kNameSpaceID_None, nsGkAtoms::media, aMedia);
439 : // The HTML5 spec is formulated in terms of the CSSOM spec, which specifies
440 : // that media queries should be ASCII lowercased during serialization.
441 0 : nsContentUtils::ASCIIToLower(aMedia);
442 :
443 0 : nsAutoString mimeType;
444 0 : nsAutoString notUsed;
445 0 : GetAttr(kNameSpaceID_None, nsGkAtoms::type, aType);
446 0 : nsContentUtils::SplitMimeType(aType, mimeType, notUsed);
447 0 : if (!mimeType.IsEmpty() && !mimeType.LowerCaseEqualsLiteral("text/css")) {
448 : return;
449 : }
450 :
451 : // If we get here we assume that we're loading a css file, so set the
452 : // type to 'text/css'
453 0 : aType.AssignLiteral("text/css");
454 :
455 : return;
456 : }
457 :
458 : nsEventStates
459 0 : nsHTMLLinkElement::IntrinsicState() const
460 : {
461 0 : return Link::LinkState() | nsGenericHTMLElement::IntrinsicState();
462 : }
463 :
464 : size_t
465 0 : nsHTMLLinkElement::SizeOfExcludingThis(nsMallocSizeOfFun aMallocSizeOf) const
466 : {
467 0 : return nsGenericHTMLElement::SizeOfExcludingThis(aMallocSizeOf) +
468 0 : Link::SizeOfExcludingThis(aMallocSizeOf);
469 : }
470 :
|