1.1.5
by Alexander Sack
Import upstream version 1.9~b4+nobinonly |
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 "nsIDOMEventTarget.h" |
|
40 |
#include "nsGenericHTMLElement.h" |
|
41 |
#include "nsILink.h" |
|
42 |
#include "nsGkAtoms.h" |
|
43 |
#include "nsStyleConsts.h" |
|
44 |
#include "nsPresContext.h" |
|
45 |
#include "nsIDOMStyleSheet.h" |
|
46 |
#include "nsIStyleSheet.h" |
|
47 |
#include "nsIStyleSheetLinkingElement.h" |
|
48 |
#include "nsStyleLinkElement.h" |
|
49 |
#include "nsReadableUtils.h" |
|
50 |
#include "nsUnicharUtils.h" |
|
51 |
#include "nsIURL.h" |
|
52 |
#include "nsNetUtil.h" |
|
53 |
#include "nsIDocument.h" |
|
54 |
#include "nsIDOMEvent.h" |
|
55 |
#include "nsIPrivateDOMEvent.h" |
|
56 |
#include "nsIDOMDocumentEvent.h" |
|
57 |
#include "nsIDOMEventTarget.h" |
|
58 |
#include "nsParserUtils.h" |
|
59 |
#include "nsContentUtils.h" |
|
60 |
#include "nsPIDOMWindow.h" |
|
61 |
#include "nsPLDOMEvent.h" |
|
62 |
||
63 |
class nsHTMLLinkElement : public nsGenericHTMLElement, |
|
64 |
public nsIDOMHTMLLinkElement, |
|
65 |
public nsILink, |
|
66 |
public nsStyleLinkElement |
|
67 |
{
|
|
68 |
public: |
|
69 |
nsHTMLLinkElement(nsINodeInfo *aNodeInfo); |
|
70 |
virtual ~nsHTMLLinkElement(); |
|
71 |
||
72 |
// nsISupports
|
|
73 |
NS_DECL_ISUPPORTS_INHERITED
|
|
74 |
||
75 |
// nsIDOMNode
|
|
76 |
NS_FORWARD_NSIDOMNODE(nsGenericHTMLElement::) |
|
77 |
||
78 |
// nsIDOMElement
|
|
79 |
NS_FORWARD_NSIDOMELEMENT(nsGenericHTMLElement::) |
|
80 |
||
81 |
// nsIDOMHTMLElement
|
|
82 |
NS_FORWARD_NSIDOMHTMLELEMENT(nsGenericHTMLElement::) |
|
83 |
||
84 |
// nsIDOMHTMLLinkElement
|
|
85 |
NS_DECL_NSIDOMHTMLLINKELEMENT
|
|
86 |
||
87 |
// nsILink
|
|
88 |
NS_IMETHOD GetLinkState(nsLinkState &aState); |
|
89 |
NS_IMETHOD SetLinkState(nsLinkState aState); |
|
90 |
NS_IMETHOD GetHrefURI(nsIURI** aURI); |
|
91 |
NS_IMETHOD LinkAdded(); |
|
92 |
NS_IMETHOD LinkRemoved(); |
|
93 |
||
94 |
virtual nsresult BindToTree(nsIDocument* aDocument, nsIContent* aParent, |
|
95 |
nsIContent* aBindingParent, |
|
96 |
PRBool aCompileEventHandlers); |
|
97 |
virtual void UnbindFromTree(PRBool aDeep = PR_TRUE, |
|
98 |
PRBool aNullParent = PR_TRUE); |
|
99 |
void CreateAndDispatchEvent(nsIDocument* aDoc, const nsAString& aEventName); |
|
100 |
nsresult SetAttr(PRInt32 aNameSpaceID, nsIAtom* aName, |
|
101 |
const nsAString& aValue, PRBool 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 |
PRBool aNotify); |
|
108 |
virtual nsresult UnsetAttr(PRInt32 aNameSpaceID, nsIAtom* aAttribute, |
|
109 |
PRBool aNotify); |
|
110 |
||
111 |
virtual nsresult PreHandleEvent(nsEventChainPreVisitor& aVisitor); |
|
112 |
virtual nsresult PostHandleEvent(nsEventChainPostVisitor& aVisitor); |
|
113 |
virtual PRBool IsLink(nsIURI** aURI) const; |
|
114 |
virtual void GetLinkTarget(nsAString& aTarget); |
|
115 |
||
116 |
virtual nsresult Clone(nsINodeInfo *aNodeInfo, nsINode **aResult) const; |
|
117 |
||
118 |
protected: |
|
119 |
virtual void GetStyleSheetURL(PRBool* aIsInline, |
|
120 |
nsIURI** aURI); |
|
121 |
virtual void GetStyleSheetInfo(nsAString& aTitle, |
|
122 |
nsAString& aType, |
|
123 |
nsAString& aMedia, |
|
124 |
PRBool* aIsAlternate); |
|
125 |
||
126 |
// The cached visited state
|
|
127 |
nsLinkState mLinkState; |
|
128 |
};
|
|
129 |
||
130 |
||
131 |
NS_IMPL_NS_NEW_HTML_ELEMENT(Link) |
|
132 |
||
133 |
||
134 |
nsHTMLLinkElement::nsHTMLLinkElement(nsINodeInfo *aNodeInfo) |
|
135 |
: nsGenericHTMLElement(aNodeInfo), |
|
136 |
mLinkState(eLinkState_Unknown) |
|
137 |
{
|
|
138 |
}
|
|
139 |
||
140 |
nsHTMLLinkElement::~nsHTMLLinkElement() |
|
141 |
{
|
|
142 |
}
|
|
143 |
||
144 |
||
145 |
NS_IMPL_ADDREF_INHERITED(nsHTMLLinkElement, nsGenericElement) |
|
146 |
NS_IMPL_RELEASE_INHERITED(nsHTMLLinkElement, nsGenericElement) |
|
147 |
||
148 |
||
149 |
// QueryInterface implementation for nsHTMLLinkElement
|
|
150 |
NS_HTML_CONTENT_INTERFACE_TABLE_HEAD(nsHTMLLinkElement, nsGenericHTMLElement) |
|
151 |
NS_INTERFACE_TABLE_INHERITED4(nsHTMLLinkElement, |
|
152 |
nsIDOMHTMLLinkElement, |
|
153 |
nsIDOMLinkStyle, |
|
154 |
nsILink, |
|
155 |
nsIStyleSheetLinkingElement) |
|
156 |
NS_HTML_CONTENT_INTERFACE_TABLE_TAIL_CLASSINFO(HTMLLinkElement) |
|
157 |
||
158 |
||
159 |
NS_IMPL_ELEMENT_CLONE(nsHTMLLinkElement) |
|
160 |
||
161 |
||
162 |
NS_IMETHODIMP
|
|
163 |
nsHTMLLinkElement::GetDisabled(PRBool* aDisabled) |
|
164 |
{
|
|
1.1.17
by Alexander Sack
Import upstream version 1.9.0.7+nobinonly |
165 |
nsCOMPtr<nsIDOMStyleSheet> ss(do_QueryInterface(GetStyleSheet())); |
1.1.5
by Alexander Sack
Import upstream version 1.9~b4+nobinonly |
166 |
nsresult result = NS_OK; |
167 |
||
168 |
if (ss) { |
|
169 |
result = ss->GetDisabled(aDisabled); |
|
170 |
} else { |
|
171 |
*aDisabled = PR_FALSE; |
|
172 |
}
|
|
173 |
||
174 |
return result; |
|
175 |
}
|
|
176 |
||
177 |
NS_IMETHODIMP
|
|
178 |
nsHTMLLinkElement::SetDisabled(PRBool aDisabled) |
|
179 |
{
|
|
1.1.17
by Alexander Sack
Import upstream version 1.9.0.7+nobinonly |
180 |
nsCOMPtr<nsIDOMStyleSheet> ss(do_QueryInterface(GetStyleSheet())); |
1.1.5
by Alexander Sack
Import upstream version 1.9~b4+nobinonly |
181 |
nsresult result = NS_OK; |
182 |
||
183 |
if (ss) { |
|
184 |
result = ss->SetDisabled(aDisabled); |
|
185 |
}
|
|
186 |
||
187 |
return result; |
|
188 |
}
|
|
189 |
||
190 |
||
191 |
NS_IMPL_STRING_ATTR(nsHTMLLinkElement, Charset, charset) |
|
192 |
NS_IMPL_URI_ATTR(nsHTMLLinkElement, Href, href) |
|
193 |
NS_IMPL_STRING_ATTR(nsHTMLLinkElement, Hreflang, hreflang) |
|
194 |
NS_IMPL_STRING_ATTR(nsHTMLLinkElement, Media, media) |
|
195 |
NS_IMPL_STRING_ATTR(nsHTMLLinkElement, Rel, rel) |
|
196 |
NS_IMPL_STRING_ATTR(nsHTMLLinkElement, Rev, rev) |
|
197 |
NS_IMPL_STRING_ATTR(nsHTMLLinkElement, Target, target) |
|
198 |
NS_IMPL_STRING_ATTR(nsHTMLLinkElement, Type, type) |
|
199 |
||
200 |
nsresult
|
|
201 |
nsHTMLLinkElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent, |
|
202 |
nsIContent* aBindingParent, |
|
203 |
PRBool aCompileEventHandlers) |
|
204 |
{
|
|
205 |
nsresult rv = nsGenericHTMLElement::BindToTree(aDocument, aParent, |
|
206 |
aBindingParent, |
|
207 |
aCompileEventHandlers); |
|
208 |
NS_ENSURE_SUCCESS(rv, rv); |
|
209 |
||
210 |
UpdateStyleSheetInternal(nsnull); |
|
211 |
||
212 |
CreateAndDispatchEvent(aDocument, NS_LITERAL_STRING("DOMLinkAdded")); |
|
213 |
||
214 |
return rv; |
|
215 |
}
|
|
216 |
||
217 |
NS_IMETHODIMP
|
|
218 |
nsHTMLLinkElement::LinkAdded() |
|
219 |
{
|
|
220 |
CreateAndDispatchEvent(GetOwnerDoc(), NS_LITERAL_STRING("DOMLinkAdded")); |
|
221 |
return NS_OK; |
|
222 |
}
|
|
223 |
||
224 |
NS_IMETHODIMP
|
|
225 |
nsHTMLLinkElement::LinkRemoved() |
|
226 |
{
|
|
227 |
CreateAndDispatchEvent(GetOwnerDoc(), NS_LITERAL_STRING("DOMLinkRemoved")); |
|
228 |
return NS_OK; |
|
229 |
}
|
|
230 |
||
231 |
void
|
|
232 |
nsHTMLLinkElement::UnbindFromTree(PRBool aDeep, PRBool aNullParent) |
|
233 |
{
|
|
234 |
nsCOMPtr<nsIDocument> oldDoc = GetCurrentDoc(); |
|
235 |
if (oldDoc) { |
|
236 |
GetCurrentDoc()->ForgetLink(this); |
|
237 |
// If this link is ever reinserted into a document, it might
|
|
238 |
// be under a different xml:base, so forget the cached state now
|
|
239 |
mLinkState = eLinkState_Unknown; |
|
240 |
}
|
|
241 |
||
1.1.7
by Alexander Sack
Import upstream version 1.9~rc1+nobinonly |
242 |
// Once we have XPCOMGC we shouldn't need to call UnbindFromTree during Unlink
|
243 |
// and so this messy event dispatch can go away.
|
|
1.1.5
by Alexander Sack
Import upstream version 1.9~b4+nobinonly |
244 |
CreateAndDispatchEvent(oldDoc, NS_LITERAL_STRING("DOMLinkRemoved")); |
245 |
nsGenericHTMLElement::UnbindFromTree(aDeep, aNullParent); |
|
246 |
UpdateStyleSheetInternal(oldDoc); |
|
247 |
}
|
|
248 |
||
249 |
void
|
|
250 |
nsHTMLLinkElement::CreateAndDispatchEvent(nsIDocument* aDoc, |
|
251 |
const nsAString& aEventName) |
|
252 |
{
|
|
253 |
if (!aDoc) |
|
254 |
return; |
|
255 |
||
256 |
// In the unlikely case that both rev is specified *and* rel=stylesheet,
|
|
257 |
// this code will cause the event to fire, on the principle that maybe the
|
|
258 |
// page really does want to specify that it's author is a stylesheet. Since
|
|
259 |
// this should never actually happen and the performance hit is minimal,
|
|
260 |
// doing the "right" thing costs virtually nothing here, even if it doesn't
|
|
261 |
// make much sense.
|
|
262 |
static nsIContent::AttrValuesArray strings[] = |
|
263 |
{&nsGkAtoms::_empty, &nsGkAtoms::stylesheet, nsnull}; |
|
264 |
||
265 |
if (!nsContentUtils::HasNonEmptyAttr(this, kNameSpaceID_None, |
|
266 |
nsGkAtoms::rev) && |
|
267 |
FindAttrValueIn(kNameSpaceID_None, nsGkAtoms::rel, |
|
268 |
strings, eIgnoreCase) != ATTR_VALUE_NO_MATCH) |
|
269 |
return; |
|
270 |
||
271 |
nsRefPtr<nsPLDOMEvent> event = new nsPLDOMEvent(this, aEventName); |
|
272 |
if (event) { |
|
1.1.7
by Alexander Sack
Import upstream version 1.9~rc1+nobinonly |
273 |
// If we have script blockers on the stack then we want to run as soon as
|
274 |
// they are removed. Otherwise punt the runable to the event loop as we
|
|
275 |
// don't know when it will be safe to run script.
|
|
276 |
if (nsContentUtils::IsSafeToRunScript()) |
|
277 |
event->PostDOMEvent(); |
|
278 |
else
|
|
279 |
event->RunDOMEventWhenSafe(); |
|
1.1.5
by Alexander Sack
Import upstream version 1.9~b4+nobinonly |
280 |
}
|
281 |
}
|
|
282 |
||
283 |
nsresult
|
|
284 |
nsHTMLLinkElement::SetAttr(PRInt32 aNameSpaceID, nsIAtom* aName, |
|
285 |
nsIAtom* aPrefix, const nsAString& aValue, |
|
286 |
PRBool aNotify) |
|
287 |
{
|
|
288 |
if (aName == nsGkAtoms::href && kNameSpaceID_None == aNameSpaceID) { |
|
289 |
nsIDocument* doc = GetCurrentDoc(); |
|
290 |
if (doc) { |
|
291 |
doc->ForgetLink(this); |
|
292 |
// The change to 'href' will cause style reresolution which will
|
|
293 |
// eventually recompute the link state and re-add this element
|
|
294 |
// to the link map if necessary.
|
|
295 |
}
|
|
296 |
SetLinkState(eLinkState_Unknown); |
|
297 |
}
|
|
298 |
||
299 |
nsresult rv = nsGenericHTMLElement::SetAttr(aNameSpaceID, aName, aPrefix, |
|
300 |
aValue, aNotify); |
|
301 |
if (NS_SUCCEEDED(rv)) { |
|
302 |
PRBool dropSheet = PR_FALSE; |
|
303 |
if (aNameSpaceID == kNameSpaceID_None && aName == nsGkAtoms::rel && |
|
1.1.17
by Alexander Sack
Import upstream version 1.9.0.7+nobinonly |
304 |
GetStyleSheet()) { |
1.1.5
by Alexander Sack
Import upstream version 1.9~b4+nobinonly |
305 |
nsStringArray linkTypes(4); |
306 |
nsStyleLinkElement::ParseLinkTypes(aValue, linkTypes); |
|
307 |
dropSheet = linkTypes.IndexOf(NS_LITERAL_STRING("stylesheet")) < 0; |
|
308 |
}
|
|
309 |
||
310 |
UpdateStyleSheetInternal(nsnull, |
|
311 |
dropSheet || |
|
312 |
(aNameSpaceID == kNameSpaceID_None && |
|
313 |
(aName == nsGkAtoms::title || |
|
314 |
aName == nsGkAtoms::media || |
|
315 |
aName == nsGkAtoms::type))); |
|
316 |
}
|
|
317 |
||
318 |
return rv; |
|
319 |
}
|
|
320 |
||
321 |
nsresult
|
|
322 |
nsHTMLLinkElement::UnsetAttr(PRInt32 aNameSpaceID, nsIAtom* aAttribute, |
|
323 |
PRBool aNotify) |
|
324 |
{
|
|
325 |
nsresult rv = nsGenericHTMLElement::UnsetAttr(aNameSpaceID, aAttribute, |
|
326 |
aNotify); |
|
327 |
if (NS_SUCCEEDED(rv)) { |
|
328 |
UpdateStyleSheetInternal(nsnull, |
|
329 |
aNameSpaceID == kNameSpaceID_None && |
|
330 |
(aAttribute == nsGkAtoms::rel || |
|
331 |
aAttribute == nsGkAtoms::title || |
|
332 |
aAttribute == nsGkAtoms::media || |
|
333 |
aAttribute == nsGkAtoms::type)); |
|
334 |
}
|
|
335 |
||
336 |
return rv; |
|
337 |
}
|
|
338 |
||
339 |
nsresult
|
|
340 |
nsHTMLLinkElement::PreHandleEvent(nsEventChainPreVisitor& aVisitor) |
|
341 |
{
|
|
342 |
return PreHandleEventForAnchors(aVisitor); |
|
343 |
}
|
|
344 |
||
345 |
nsresult
|
|
346 |
nsHTMLLinkElement::PostHandleEvent(nsEventChainPostVisitor& aVisitor) |
|
347 |
{
|
|
348 |
return PostHandleEventForAnchors(aVisitor); |
|
349 |
}
|
|
350 |
||
351 |
PRBool
|
|
352 |
nsHTMLLinkElement::IsLink(nsIURI** aURI) const |
|
353 |
{
|
|
354 |
return IsHTMLLink(aURI); |
|
355 |
}
|
|
356 |
||
357 |
void
|
|
358 |
nsHTMLLinkElement::GetLinkTarget(nsAString& aTarget) |
|
359 |
{
|
|
360 |
GetAttr(kNameSpaceID_None, nsGkAtoms::target, aTarget); |
|
361 |
if (aTarget.IsEmpty()) { |
|
362 |
GetBaseTarget(aTarget); |
|
363 |
}
|
|
364 |
}
|
|
365 |
||
366 |
NS_IMETHODIMP
|
|
367 |
nsHTMLLinkElement::GetLinkState(nsLinkState &aState) |
|
368 |
{
|
|
369 |
aState = mLinkState; |
|
370 |
return NS_OK; |
|
371 |
}
|
|
372 |
||
373 |
NS_IMETHODIMP
|
|
374 |
nsHTMLLinkElement::SetLinkState(nsLinkState aState) |
|
375 |
{
|
|
376 |
mLinkState = aState; |
|
377 |
return NS_OK; |
|
378 |
}
|
|
379 |
||
380 |
NS_IMETHODIMP
|
|
381 |
nsHTMLLinkElement::GetHrefURI(nsIURI** aURI) |
|
382 |
{
|
|
383 |
return GetHrefURIForAnchors(aURI); |
|
384 |
}
|
|
385 |
||
386 |
void
|
|
387 |
nsHTMLLinkElement::GetStyleSheetURL(PRBool* aIsInline, |
|
388 |
nsIURI** aURI) |
|
389 |
{
|
|
390 |
*aIsInline = PR_FALSE; |
|
391 |
GetHrefURIForAnchors(aURI); |
|
392 |
return; |
|
393 |
}
|
|
394 |
||
395 |
void
|
|
396 |
nsHTMLLinkElement::GetStyleSheetInfo(nsAString& aTitle, |
|
397 |
nsAString& aType, |
|
398 |
nsAString& aMedia, |
|
399 |
PRBool* aIsAlternate) |
|
400 |
{
|
|
401 |
aTitle.Truncate(); |
|
402 |
aType.Truncate(); |
|
403 |
aMedia.Truncate(); |
|
404 |
*aIsAlternate = PR_FALSE; |
|
405 |
||
406 |
nsAutoString rel; |
|
407 |
nsStringArray linkTypes(4); |
|
408 |
GetAttr(kNameSpaceID_None, nsGkAtoms::rel, rel); |
|
409 |
nsStyleLinkElement::ParseLinkTypes(rel, linkTypes); |
|
410 |
// Is it a stylesheet link?
|
|
411 |
if (linkTypes.IndexOf(NS_LITERAL_STRING("stylesheet")) < 0) { |
|
412 |
return; |
|
413 |
}
|
|
414 |
||
415 |
nsAutoString title; |
|
416 |
GetAttr(kNameSpaceID_None, nsGkAtoms::title, title); |
|
417 |
title.CompressWhitespace(); |
|
418 |
aTitle.Assign(title); |
|
419 |
||
420 |
// If alternate, does it have title?
|
|
421 |
if (-1 != linkTypes.IndexOf(NS_LITERAL_STRING("alternate"))) { |
|
422 |
if (aTitle.IsEmpty()) { // alternates must have title |
|
423 |
return; |
|
424 |
} else { |
|
425 |
*aIsAlternate = PR_TRUE; |
|
426 |
}
|
|
427 |
}
|
|
428 |
||
429 |
GetAttr(kNameSpaceID_None, nsGkAtoms::media, aMedia); |
|
430 |
ToLowerCase(aMedia); // HTML4.0 spec is inconsistent, make it case INSENSITIVE |
|
431 |
||
432 |
nsAutoString mimeType; |
|
433 |
nsAutoString notUsed; |
|
434 |
GetAttr(kNameSpaceID_None, nsGkAtoms::type, aType); |
|
435 |
nsParserUtils::SplitMimeType(aType, mimeType, notUsed); |
|
436 |
if (!mimeType.IsEmpty() && !mimeType.LowerCaseEqualsLiteral("text/css")) { |
|
437 |
return; |
|
438 |
}
|
|
439 |
||
440 |
// If we get here we assume that we're loading a css file, so set the
|
|
441 |
// type to 'text/css'
|
|
442 |
aType.AssignLiteral("text/css"); |
|
443 |
||
444 |
return; |
|
445 |
}
|