~ubuntu-branches/ubuntu/karmic/xulrunner-1.9/karmic

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
}