Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/base/Link.cpp
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
3
/* This Source Code Form is subject to the terms of the Mozilla Public
4
 * License, v. 2.0. If a copy of the MPL was not distributed with this
5
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7
#include "Link.h"
8
9
#include "mozilla/EventStates.h"
10
#include "mozilla/MemoryReporting.h"
11
#include "mozilla/dom/Element.h"
12
#if defined(MOZ_PLACES)
13
#include "mozilla/places/History.h"
14
#else
15
#include "mozilla/IHistory.h"
16
#endif
17
#include "nsIURL.h"
18
#include "nsIURIMutator.h"
19
#include "nsISizeOf.h"
20
#include "nsIDocShell.h"
21
#include "nsIPrefetchService.h"
22
#include "nsCPrefetchService.h"
23
#include "nsStyleLinkElement.h"
24
25
#include "nsEscape.h"
26
#include "nsGkAtoms.h"
27
#include "nsHTMLDNSPrefetch.h"
28
#include "nsString.h"
29
#include "mozAutoDocUpdate.h"
30
31
#include "mozilla/Services.h"
32
#include "nsAttrValueInlines.h"
33
#include "HTMLLinkElement.h"
34
35
namespace mozilla {
36
namespace dom {
37
38
#if defined(MOZ_PLACES)
39
using places::History;
40
#endif
41
42
Link::Link(Element *aElement)
43
  : mElement(aElement)
44
  , mLinkState(eLinkState_NotLink)
45
  , mNeedsRegistration(false)
46
  , mRegistered(false)
47
  , mHasPendingLinkUpdate(false)
48
  , mInDNSPrefetch(false)
49
  , mHistory(true)
50
0
{
51
0
  MOZ_ASSERT(mElement, "Must have an element");
52
0
}
53
54
Link::Link()
55
  : mElement(nullptr)
56
  , mLinkState(eLinkState_NotLink)
57
  , mNeedsRegistration(false)
58
  , mRegistered(false)
59
  , mHasPendingLinkUpdate(false)
60
  , mInDNSPrefetch(false)
61
  , mHistory(false)
62
0
{
63
0
}
64
65
Link::~Link()
66
0
{
67
0
  // !mElement is for mock_Link.
68
0
  MOZ_ASSERT(!mElement || !mElement->IsInComposedDoc());
69
0
  if (IsInDNSPrefetch()) {
70
0
    nsHTMLDNSPrefetch::LinkDestroyed(this);
71
0
  }
72
0
  UnregisterFromHistory();
73
0
}
74
75
bool
76
Link::ElementHasHref() const
77
0
{
78
0
  return mElement->HasAttr(kNameSpaceID_None, nsGkAtoms::href) ||
79
0
         (!mElement->IsHTMLElement() &&
80
0
          mElement->HasAttr(kNameSpaceID_XLink, nsGkAtoms::href));
81
0
}
82
83
void
84
Link::TryDNSPrefetch()
85
0
{
86
0
  MOZ_ASSERT(mElement->IsInComposedDoc());
87
0
  if (ElementHasHref() && nsHTMLDNSPrefetch::IsAllowed(mElement->OwnerDoc())) {
88
0
    nsHTMLDNSPrefetch::PrefetchLow(this);
89
0
  }
90
0
}
91
92
void
93
Link::CancelDNSPrefetch(nsWrapperCache::FlagsType aDeferredFlag,
94
                        nsWrapperCache::FlagsType aRequestedFlag)
95
0
{
96
0
  // If prefetch was deferred, clear flag and move on
97
0
  if (mElement->HasFlag(aDeferredFlag)) {
98
0
    mElement->UnsetFlags(aDeferredFlag);
99
0
    // Else if prefetch was requested, clear flag and send cancellation
100
0
  } else if (mElement->HasFlag(aRequestedFlag)) {
101
0
    mElement->UnsetFlags(aRequestedFlag);
102
0
    // Possible that hostname could have changed since binding, but since this
103
0
    // covers common cases, most DNS prefetch requests will be canceled
104
0
    nsHTMLDNSPrefetch::CancelPrefetchLow(this, NS_ERROR_ABORT);
105
0
  }
106
0
}
107
108
void
109
Link::GetContentPolicyMimeTypeMedia(nsAttrValue& aAsAttr,
110
                                    nsContentPolicyType& aPolicyType,
111
                                    nsString& aMimeType,
112
                                    nsAString& aMedia)
113
0
{
114
0
  nsAutoString as;
115
0
  mElement->GetAttr(kNameSpaceID_None, nsGkAtoms::as, as);
116
0
  Link::ParseAsValue(as, aAsAttr);
117
0
  aPolicyType = AsValueToContentPolicy(aAsAttr);
118
0
119
0
  nsAutoString type;
120
0
  mElement->GetAttr(kNameSpaceID_None, nsGkAtoms::type, type);
121
0
  nsAutoString notUsed;
122
0
  nsContentUtils::SplitMimeType(type, aMimeType, notUsed);
123
0
124
0
  mElement->GetAttr(kNameSpaceID_None, nsGkAtoms::media, aMedia);
125
0
}
126
127
void
128
Link::TryDNSPrefetchOrPreconnectOrPrefetchOrPreloadOrPrerender()
129
0
{
130
0
  MOZ_ASSERT(mElement->IsInComposedDoc());
131
0
  if (!ElementHasHref()) {
132
0
    return;
133
0
  }
134
0
135
0
  nsAutoString rel;
136
0
  if (!mElement->GetAttr(kNameSpaceID_None, nsGkAtoms::rel, rel)) {
137
0
    return;
138
0
  }
139
0
140
0
  if (!nsContentUtils::PrefetchPreloadEnabled(mElement->OwnerDoc()->GetDocShell())) {
141
0
    return;
142
0
  }
143
0
144
0
  uint32_t linkTypes = nsStyleLinkElement::ParseLinkTypes(rel);
145
0
146
0
  if ((linkTypes & nsStyleLinkElement::ePREFETCH) ||
147
0
      (linkTypes & nsStyleLinkElement::eNEXT) ||
148
0
      (linkTypes & nsStyleLinkElement::ePRELOAD)){
149
0
    nsCOMPtr<nsIPrefetchService> prefetchService(do_GetService(NS_PREFETCHSERVICE_CONTRACTID));
150
0
    if (prefetchService) {
151
0
      nsCOMPtr<nsIURI> uri(GetURI());
152
0
      if (uri) {
153
0
        if (linkTypes & nsStyleLinkElement::ePRELOAD) {
154
0
          nsAttrValue asAttr;
155
0
          nsContentPolicyType policyType;
156
0
          nsAutoString mimeType;
157
0
          nsAutoString media;
158
0
          GetContentPolicyMimeTypeMedia(asAttr, policyType, mimeType, media);
159
0
160
0
          if (policyType == nsIContentPolicy::TYPE_INVALID) {
161
0
            // Ignore preload with a wrong or empty as attribute.
162
0
            return;
163
0
          }
164
0
165
0
          if (!HTMLLinkElement::CheckPreloadAttrs(asAttr, mimeType, media,
166
0
                                                  mElement->OwnerDoc())) {
167
0
            policyType = nsIContentPolicy::TYPE_INVALID;
168
0
          }
169
0
170
0
          prefetchService->PreloadURI(uri,
171
0
                                      mElement->OwnerDoc()->GetDocumentURI(),
172
0
                                      mElement, policyType);
173
0
        } else {
174
0
          prefetchService->PrefetchURI(uri,
175
0
                                       mElement->OwnerDoc()->GetDocumentURI(),
176
0
                                       mElement, linkTypes & nsStyleLinkElement::ePREFETCH);
177
0
        }
178
0
        return;
179
0
      }
180
0
    }
181
0
  }
182
0
183
0
  if (linkTypes & nsStyleLinkElement::ePRECONNECT) {
184
0
    nsCOMPtr<nsIURI> uri(GetURI());
185
0
    if (uri && mElement->OwnerDoc()) {
186
0
      mElement->OwnerDoc()->MaybePreconnect(uri,
187
0
        Element::AttrValueToCORSMode(mElement->GetParsedAttr(nsGkAtoms::crossorigin)));
188
0
      return;
189
0
    }
190
0
  }
191
0
192
0
  if (linkTypes & nsStyleLinkElement::eDNS_PREFETCH) {
193
0
    if (nsHTMLDNSPrefetch::IsAllowed(mElement->OwnerDoc())) {
194
0
      nsHTMLDNSPrefetch::PrefetchLow(this);
195
0
    }
196
0
  }
197
0
}
198
199
void
200
Link::UpdatePreload(nsAtom* aName, const nsAttrValue* aValue,
201
                    const nsAttrValue* aOldValue)
202
0
{
203
0
  MOZ_ASSERT(mElement->IsInComposedDoc());
204
0
205
0
  if (!ElementHasHref()) {
206
0
     return;
207
0
  }
208
0
209
0
  nsAutoString rel;
210
0
  if (!mElement->GetAttr(kNameSpaceID_None, nsGkAtoms::rel, rel)) {
211
0
    return;
212
0
  }
213
0
214
0
  if (!nsContentUtils::PrefetchPreloadEnabled(mElement->OwnerDoc()->GetDocShell())) {
215
0
    return;
216
0
  }
217
0
218
0
  uint32_t linkTypes = nsStyleLinkElement::ParseLinkTypes(rel);
219
0
220
0
  if (!(linkTypes & nsStyleLinkElement::ePRELOAD)) {
221
0
    return;
222
0
  }
223
0
224
0
  nsCOMPtr<nsIPrefetchService> prefetchService(do_GetService(NS_PREFETCHSERVICE_CONTRACTID));
225
0
  if (!prefetchService) {
226
0
    return;
227
0
  }
228
0
229
0
  nsCOMPtr<nsIURI> uri(GetURI());
230
0
  if (!uri) {
231
0
    return;
232
0
  }
233
0
234
0
  nsAttrValue asAttr;
235
0
  nsContentPolicyType asPolicyType;
236
0
  nsAutoString mimeType;
237
0
  nsAutoString media;
238
0
  GetContentPolicyMimeTypeMedia(asAttr, asPolicyType, mimeType, media);
239
0
240
0
  if (asPolicyType == nsIContentPolicy::TYPE_INVALID) {
241
0
    // Ignore preload with a wrong or empty as attribute, but be sure to cancel
242
0
    // the old one.
243
0
    prefetchService->CancelPrefetchPreloadURI(uri, mElement);
244
0
    return;
245
0
  }
246
0
247
0
  nsContentPolicyType policyType = asPolicyType;
248
0
  if (!HTMLLinkElement::CheckPreloadAttrs(asAttr, mimeType, media,
249
0
                                          mElement->OwnerDoc())) {
250
0
    policyType = nsIContentPolicy::TYPE_INVALID;
251
0
  }
252
0
253
0
  if (aName == nsGkAtoms::crossorigin) {
254
0
    CORSMode corsMode = Element::AttrValueToCORSMode(aValue);
255
0
    CORSMode oldCorsMode = Element::AttrValueToCORSMode(aOldValue);
256
0
    if (corsMode != oldCorsMode) {
257
0
      prefetchService->CancelPrefetchPreloadURI(uri, mElement);
258
0
      prefetchService->PreloadURI(uri, mElement->OwnerDoc()->GetDocumentURI(),
259
0
                                  mElement, policyType);
260
0
    }
261
0
    return;
262
0
  }
263
0
264
0
  nsContentPolicyType oldPolicyType;
265
0
266
0
  if (aName == nsGkAtoms::as) {
267
0
    if (aOldValue) {
268
0
      oldPolicyType = AsValueToContentPolicy(*aOldValue);
269
0
      if (!HTMLLinkElement::CheckPreloadAttrs(*aOldValue, mimeType, media,
270
0
                                              mElement->OwnerDoc())) {
271
0
        oldPolicyType = nsIContentPolicy::TYPE_INVALID;
272
0
      }
273
0
    } else {
274
0
      oldPolicyType = nsIContentPolicy::TYPE_INVALID;
275
0
    }
276
0
  } else if (aName == nsGkAtoms::type) {
277
0
    nsAutoString oldType;
278
0
    nsAutoString notUsed;
279
0
    if (aOldValue) {
280
0
      aOldValue->ToString(oldType);
281
0
    } else {
282
0
      oldType = EmptyString();
283
0
    }
284
0
    nsAutoString oldMimeType;
285
0
    nsContentUtils::SplitMimeType(oldType, oldMimeType, notUsed);
286
0
    if (HTMLLinkElement::CheckPreloadAttrs(asAttr, oldMimeType, media,
287
0
                                           mElement->OwnerDoc())) {
288
0
      oldPolicyType = asPolicyType;
289
0
    } else {
290
0
      oldPolicyType = nsIContentPolicy::TYPE_INVALID;
291
0
    }
292
0
  } else {
293
0
    MOZ_ASSERT(aName == nsGkAtoms::media);
294
0
    nsAutoString oldMedia;
295
0
    if (aOldValue) {
296
0
      aOldValue->ToString(oldMedia);
297
0
    } else {
298
0
      oldMedia = EmptyString();
299
0
    }
300
0
    if (HTMLLinkElement::CheckPreloadAttrs(asAttr, mimeType, oldMedia,
301
0
                                           mElement->OwnerDoc())) {
302
0
      oldPolicyType = asPolicyType;
303
0
    } else {
304
0
      oldPolicyType = nsIContentPolicy::TYPE_INVALID;
305
0
    }
306
0
  }
307
0
308
0
  if ((policyType != oldPolicyType) &&
309
0
      (oldPolicyType != nsIContentPolicy::TYPE_INVALID)) {
310
0
    prefetchService->CancelPrefetchPreloadURI(uri, mElement);
311
0
312
0
  }
313
0
314
0
  // Trigger a new preload if the policy type has changed.
315
0
  // Also trigger load if the new policy type is invalid, this will only
316
0
  // trigger an error event.
317
0
  if ((policyType != oldPolicyType) ||
318
0
      (policyType == nsIContentPolicy::TYPE_INVALID)) {
319
0
    prefetchService->PreloadURI(uri, mElement->OwnerDoc()->GetDocumentURI(),
320
0
                                mElement, policyType);
321
0
  }
322
0
}
323
324
void
325
Link::CancelPrefetchOrPreload()
326
0
{
327
0
  nsCOMPtr<nsIPrefetchService> prefetchService(do_GetService(NS_PREFETCHSERVICE_CONTRACTID));
328
0
  if (prefetchService) {
329
0
    nsCOMPtr<nsIURI> uri(GetURI());
330
0
    if (uri) {
331
0
      prefetchService->CancelPrefetchPreloadURI(uri, mElement);
332
0
    }
333
0
  }
334
0
}
335
336
void
337
Link::SetLinkState(nsLinkState aState)
338
0
{
339
0
  NS_ASSERTION(mRegistered,
340
0
               "Setting the link state of an unregistered Link!");
341
0
  NS_ASSERTION(mLinkState != aState,
342
0
               "Setting state to the currently set state!");
343
0
344
0
  // Set our current state as appropriate.
345
0
  mLinkState = aState;
346
0
347
0
  // Per IHistory interface documentation, we are no longer registered.
348
0
  mRegistered = false;
349
0
350
0
  MOZ_ASSERT(LinkState() == NS_EVENT_STATE_VISITED ||
351
0
             LinkState() == NS_EVENT_STATE_UNVISITED,
352
0
             "Unexpected state obtained from LinkState()!");
353
0
354
0
  // Tell the element to update its visited state
355
0
  mElement->UpdateState(true);
356
0
}
357
358
EventStates
359
Link::LinkState() const
360
0
{
361
0
  // We are a constant method, but we are just lazily doing things and have to
362
0
  // track that state.  Cast away that constness!
363
0
  Link *self = const_cast<Link *>(this);
364
0
365
0
  Element *element = self->mElement;
366
0
367
0
  // If we have not yet registered for notifications and need to,
368
0
  // due to our href changing, register now!
369
0
  if (!mRegistered && mNeedsRegistration && element->IsInComposedDoc() &&
370
0
      !HasPendingLinkUpdate()) {
371
0
    // Only try and register once.
372
0
    self->mNeedsRegistration = false;
373
0
374
0
    nsCOMPtr<nsIURI> hrefURI(GetURI());
375
0
376
0
    // Assume that we are not visited until we are told otherwise.
377
0
    self->mLinkState = eLinkState_Unvisited;
378
0
379
0
    // Make sure the href attribute has a valid link (bug 23209).
380
0
    // If we have a good href, register with History if available.
381
0
    if (mHistory && hrefURI) {
382
#ifdef ANDROID
383
      nsCOMPtr<IHistory> history = services::GetHistoryService();
384
#elif defined(MOZ_PLACES)
385
      History* history = History::GetService();
386
#else
387
      nsCOMPtr<IHistory> history;
388
#endif
389
0
      if (history) {
390
0
        nsresult rv = history->RegisterVisitedCallback(hrefURI, self);
391
0
        if (NS_SUCCEEDED(rv)) {
392
0
          self->mRegistered = true;
393
0
394
0
          // And make sure we are in the document's link map.
395
0
          element->GetComposedDoc()->AddStyleRelevantLink(self);
396
0
        }
397
0
      }
398
0
    }
399
0
  }
400
0
401
0
  // Otherwise, return our known state.
402
0
  if (mLinkState == eLinkState_Visited) {
403
0
    return NS_EVENT_STATE_VISITED;
404
0
  }
405
0
406
0
  if (mLinkState == eLinkState_Unvisited) {
407
0
    return NS_EVENT_STATE_UNVISITED;
408
0
  }
409
0
410
0
  return EventStates();
411
0
}
412
413
nsIURI*
414
Link::GetURI() const
415
0
{
416
0
  // If we have this URI cached, use it.
417
0
  if (mCachedURI) {
418
0
    return mCachedURI;
419
0
  }
420
0
421
0
  // Otherwise obtain it.
422
0
  Link *self = const_cast<Link *>(this);
423
0
  Element *element = self->mElement;
424
0
  mCachedURI = element->GetHrefURI();
425
0
426
0
  return mCachedURI;
427
0
}
428
429
void
430
Link::SetProtocol(const nsAString &aProtocol)
431
0
{
432
0
  nsCOMPtr<nsIURI> uri(GetURI());
433
0
  if (!uri) {
434
0
    // Ignore failures to be compatible with NS4.
435
0
    return;
436
0
  }
437
0
438
0
  nsAString::const_iterator start, end;
439
0
  aProtocol.BeginReading(start);
440
0
  aProtocol.EndReading(end);
441
0
  nsAString::const_iterator iter(start);
442
0
  (void)FindCharInReadable(':', iter, end);
443
0
  nsresult rv = NS_MutateURI(uri)
444
0
                  .SetScheme(NS_ConvertUTF16toUTF8(Substring(start, iter)))
445
0
                  .Finalize(uri);
446
0
  if (NS_FAILED(rv)) {
447
0
    return;
448
0
  }
449
0
450
0
  SetHrefAttribute(uri);
451
0
}
452
453
void
454
Link::SetPassword(const nsAString &aPassword)
455
0
{
456
0
  nsCOMPtr<nsIURI> uri(GetURI());
457
0
  if (!uri) {
458
0
    // Ignore failures to be compatible with NS4.
459
0
    return;
460
0
  }
461
0
462
0
  nsresult rv = NS_MutateURI(uri)
463
0
                  .SetPassword(NS_ConvertUTF16toUTF8(aPassword))
464
0
                  .Finalize(uri);
465
0
  if (NS_SUCCEEDED(rv)) {
466
0
    SetHrefAttribute(uri);
467
0
  }
468
0
}
469
470
void
471
Link::SetUsername(const nsAString &aUsername)
472
0
{
473
0
  nsCOMPtr<nsIURI> uri(GetURI());
474
0
  if (!uri) {
475
0
    // Ignore failures to be compatible with NS4.
476
0
    return;
477
0
  }
478
0
479
0
  nsresult rv = NS_MutateURI(uri)
480
0
                  .SetUsername(NS_ConvertUTF16toUTF8(aUsername))
481
0
                  .Finalize(uri);
482
0
  if (NS_SUCCEEDED(rv)) {
483
0
    SetHrefAttribute(uri);
484
0
  }
485
0
}
486
487
void
488
Link::SetHost(const nsAString &aHost)
489
0
{
490
0
  nsCOMPtr<nsIURI> uri(GetURI());
491
0
  if (!uri) {
492
0
    // Ignore failures to be compatible with NS4.
493
0
    return;
494
0
  }
495
0
496
0
  nsresult rv = NS_MutateURI(uri)
497
0
                  .SetHostPort(NS_ConvertUTF16toUTF8(aHost))
498
0
                  .Finalize(uri);
499
0
  if (NS_FAILED(rv)) {
500
0
    return;
501
0
  }
502
0
  SetHrefAttribute(uri);
503
0
}
504
505
void
506
Link::SetHostname(const nsAString &aHostname)
507
0
{
508
0
  nsCOMPtr<nsIURI> uri(GetURI());
509
0
  if (!uri) {
510
0
    // Ignore failures to be compatible with NS4.
511
0
    return;
512
0
  }
513
0
514
0
  nsresult rv = NS_MutateURI(uri)
515
0
                  .SetHost(NS_ConvertUTF16toUTF8(aHostname))
516
0
                  .Finalize(uri);
517
0
  if (NS_FAILED(rv)) {
518
0
    return;
519
0
  }
520
0
  SetHrefAttribute(uri);
521
0
}
522
523
void
524
Link::SetPathname(const nsAString &aPathname)
525
0
{
526
0
  nsCOMPtr<nsIURI> uri(GetURI());
527
0
  nsCOMPtr<nsIURL> url(do_QueryInterface(uri));
528
0
  if (!url) {
529
0
    // Ignore failures to be compatible with NS4.
530
0
    return;
531
0
  }
532
0
533
0
  nsresult rv = NS_MutateURI(uri)
534
0
                  .SetFilePath(NS_ConvertUTF16toUTF8(aPathname))
535
0
                  .Finalize(uri);
536
0
  if (NS_FAILED(rv)) {
537
0
    return;
538
0
  }
539
0
  SetHrefAttribute(uri);
540
0
}
541
542
void
543
Link::SetSearch(const nsAString& aSearch)
544
0
{
545
0
  nsCOMPtr<nsIURI> uri(GetURI());
546
0
  nsCOMPtr<nsIURL> url(do_QueryInterface(uri));
547
0
  if (!url) {
548
0
    // Ignore failures to be compatible with NS4.
549
0
    return;
550
0
  }
551
0
552
0
  auto encoding = mElement->OwnerDoc()->GetDocumentCharacterSet();
553
0
  nsresult rv = NS_MutateURI(uri)
554
0
                  .SetQueryWithEncoding(NS_ConvertUTF16toUTF8(aSearch), encoding)
555
0
                  .Finalize(uri);
556
0
  if (NS_FAILED(rv)) {
557
0
    return;
558
0
  }
559
0
  SetHrefAttribute(uri);
560
0
}
561
562
void
563
Link::SetPort(const nsAString &aPort)
564
0
{
565
0
  nsCOMPtr<nsIURI> uri(GetURI());
566
0
  if (!uri) {
567
0
    // Ignore failures to be compatible with NS4.
568
0
    return;
569
0
  }
570
0
571
0
  nsresult rv;
572
0
  nsAutoString portStr(aPort);
573
0
574
0
  // nsIURI uses -1 as default value.
575
0
  int32_t port = -1;
576
0
  if (!aPort.IsEmpty()) {
577
0
    port = portStr.ToInteger(&rv);
578
0
    if (NS_FAILED(rv)) {
579
0
      return;
580
0
    }
581
0
  }
582
0
583
0
  rv = NS_MutateURI(uri)
584
0
         .SetPort(port)
585
0
         .Finalize(uri);
586
0
  if (NS_FAILED(rv)) {
587
0
    return;
588
0
  }
589
0
  SetHrefAttribute(uri);
590
0
}
591
592
void
593
Link::SetHash(const nsAString &aHash)
594
0
{
595
0
  nsCOMPtr<nsIURI> uri(GetURI());
596
0
  if (!uri) {
597
0
    // Ignore failures to be compatible with NS4.
598
0
    return;
599
0
  }
600
0
601
0
  nsresult rv = NS_MutateURI(uri)
602
0
                  .SetRef(NS_ConvertUTF16toUTF8(aHash))
603
0
                  .Finalize(uri);
604
0
  if (NS_FAILED(rv)) {
605
0
    return;
606
0
  }
607
0
608
0
  SetHrefAttribute(uri);
609
0
}
610
611
void
612
Link::GetOrigin(nsAString &aOrigin)
613
0
{
614
0
  aOrigin.Truncate();
615
0
616
0
  nsCOMPtr<nsIURI> uri(GetURI());
617
0
  if (!uri) {
618
0
    return;
619
0
  }
620
0
621
0
  nsString origin;
622
0
  nsContentUtils::GetUTFOrigin(uri, origin);
623
0
  aOrigin.Assign(origin);
624
0
}
625
626
void
627
Link::GetProtocol(nsAString &_protocol)
628
0
{
629
0
  nsCOMPtr<nsIURI> uri(GetURI());
630
0
  if (!uri) {
631
0
    _protocol.AssignLiteral("http");
632
0
  }
633
0
  else {
634
0
    nsAutoCString scheme;
635
0
    (void)uri->GetScheme(scheme);
636
0
    CopyASCIItoUTF16(scheme, _protocol);
637
0
  }
638
0
  _protocol.Append(char16_t(':'));
639
0
}
640
641
void
642
Link::GetUsername(nsAString& aUsername)
643
0
{
644
0
  aUsername.Truncate();
645
0
646
0
  nsCOMPtr<nsIURI> uri(GetURI());
647
0
  if (!uri) {
648
0
    return;
649
0
  }
650
0
651
0
  nsAutoCString username;
652
0
  uri->GetUsername(username);
653
0
  CopyASCIItoUTF16(username, aUsername);
654
0
}
655
656
void
657
Link::GetPassword(nsAString &aPassword)
658
0
{
659
0
  aPassword.Truncate();
660
0
661
0
  nsCOMPtr<nsIURI> uri(GetURI());
662
0
  if (!uri) {
663
0
    return;
664
0
  }
665
0
666
0
  nsAutoCString password;
667
0
  uri->GetPassword(password);
668
0
  CopyASCIItoUTF16(password, aPassword);
669
0
}
670
671
void
672
Link::GetHost(nsAString &_host)
673
0
{
674
0
  _host.Truncate();
675
0
676
0
  nsCOMPtr<nsIURI> uri(GetURI());
677
0
  if (!uri) {
678
0
    // Do not throw!  Not having a valid URI should result in an empty string.
679
0
    return;
680
0
  }
681
0
682
0
  nsAutoCString hostport;
683
0
  nsresult rv = uri->GetHostPort(hostport);
684
0
  if (NS_SUCCEEDED(rv)) {
685
0
    CopyUTF8toUTF16(hostport, _host);
686
0
  }
687
0
}
688
689
void
690
Link::GetHostname(nsAString &_hostname)
691
0
{
692
0
  _hostname.Truncate();
693
0
694
0
  nsCOMPtr<nsIURI> uri(GetURI());
695
0
  if (!uri) {
696
0
    // Do not throw!  Not having a valid URI should result in an empty string.
697
0
    return;
698
0
  }
699
0
700
0
  nsContentUtils::GetHostOrIPv6WithBrackets(uri, _hostname);
701
0
}
702
703
void
704
Link::GetPathname(nsAString &_pathname)
705
0
{
706
0
  _pathname.Truncate();
707
0
708
0
  nsCOMPtr<nsIURI> uri(GetURI());
709
0
  nsCOMPtr<nsIURL> url(do_QueryInterface(uri));
710
0
  if (!url) {
711
0
    // Do not throw!  Not having a valid URI or URL should result in an empty
712
0
    // string.
713
0
    return;
714
0
  }
715
0
716
0
  nsAutoCString file;
717
0
  nsresult rv = url->GetFilePath(file);
718
0
  if (NS_SUCCEEDED(rv)) {
719
0
    CopyUTF8toUTF16(file, _pathname);
720
0
  }
721
0
}
722
723
void
724
Link::GetSearch(nsAString &_search)
725
0
{
726
0
  _search.Truncate();
727
0
728
0
  nsCOMPtr<nsIURI> uri(GetURI());
729
0
  nsCOMPtr<nsIURL> url(do_QueryInterface(uri));
730
0
  if (!url) {
731
0
    // Do not throw!  Not having a valid URI or URL should result in an empty
732
0
    // string.
733
0
    return;
734
0
  }
735
0
736
0
  nsAutoCString search;
737
0
  nsresult rv = url->GetQuery(search);
738
0
  if (NS_SUCCEEDED(rv) && !search.IsEmpty()) {
739
0
    _search.Assign(u'?');
740
0
    AppendUTF8toUTF16(search, _search);
741
0
  }
742
0
}
743
744
void
745
Link::GetPort(nsAString &_port)
746
0
{
747
0
  _port.Truncate();
748
0
749
0
  nsCOMPtr<nsIURI> uri(GetURI());
750
0
  if (!uri) {
751
0
    // Do not throw!  Not having a valid URI should result in an empty string.
752
0
    return;
753
0
  }
754
0
755
0
  int32_t port;
756
0
  nsresult rv = uri->GetPort(&port);
757
0
  // Note that failure to get the port from the URI is not necessarily a bad
758
0
  // thing.  Some URIs do not have a port.
759
0
  if (NS_SUCCEEDED(rv) && port != -1) {
760
0
    nsAutoString portStr;
761
0
    portStr.AppendInt(port, 10);
762
0
    _port.Assign(portStr);
763
0
  }
764
0
}
765
766
void
767
Link::GetHash(nsAString &_hash)
768
0
{
769
0
  _hash.Truncate();
770
0
771
0
  nsCOMPtr<nsIURI> uri(GetURI());
772
0
  if (!uri) {
773
0
    // Do not throw!  Not having a valid URI should result in an empty
774
0
    // string.
775
0
    return;
776
0
  }
777
0
778
0
  nsAutoCString ref;
779
0
  nsresult rv = uri->GetRef(ref);
780
0
  if (NS_SUCCEEDED(rv) && !ref.IsEmpty()) {
781
0
    _hash.Assign(char16_t('#'));
782
0
    AppendUTF8toUTF16(ref, _hash);
783
0
  }
784
0
}
785
786
void
787
Link::ResetLinkState(bool aNotify, bool aHasHref)
788
0
{
789
0
  nsLinkState defaultState;
790
0
791
0
  // The default state for links with an href is unvisited.
792
0
  if (aHasHref) {
793
0
    defaultState = eLinkState_Unvisited;
794
0
  } else {
795
0
    defaultState = eLinkState_NotLink;
796
0
  }
797
0
798
0
  // If !mNeedsRegstration, then either we've never registered, or we're
799
0
  // currently registered; in either case, we should remove ourself
800
0
  // from the doc and the history.
801
0
  if (!mNeedsRegistration && mLinkState != eLinkState_NotLink) {
802
0
    nsIDocument *doc = mElement->GetComposedDoc();
803
0
    if (doc && (mRegistered || mLinkState == eLinkState_Visited)) {
804
0
      // Tell the document to forget about this link if we've registered
805
0
      // with it before.
806
0
      doc->ForgetLink(this);
807
0
    }
808
0
  }
809
0
810
0
  // If we have an href, we should register with the history.
811
0
  mNeedsRegistration = aHasHref;
812
0
813
0
  // If we've cached the URI, reset always invalidates it.
814
0
  UnregisterFromHistory();
815
0
  mCachedURI = nullptr;
816
0
817
0
  // Update our state back to the default.
818
0
  mLinkState = defaultState;
819
0
820
0
  // We have to be very careful here: if aNotify is false we do NOT
821
0
  // want to call UpdateState, because that will call into LinkState()
822
0
  // and try to start off loads, etc.  But ResetLinkState is called
823
0
  // with aNotify false when things are in inconsistent states, so
824
0
  // we'll get confused in that situation.  Instead, just silently
825
0
  // update the link state on mElement. Since we might have set the
826
0
  // link state to unvisited, make sure to update with that state if
827
0
  // required.
828
0
  if (aNotify) {
829
0
    mElement->UpdateState(aNotify);
830
0
  } else {
831
0
    if (mLinkState == eLinkState_Unvisited) {
832
0
      mElement->UpdateLinkState(NS_EVENT_STATE_UNVISITED);
833
0
    } else {
834
0
      mElement->UpdateLinkState(EventStates());
835
0
    }
836
0
  }
837
0
}
838
839
void
840
Link::UnregisterFromHistory()
841
0
{
842
0
  // If we are not registered, we have nothing to do.
843
0
  if (!mRegistered) {
844
0
    return;
845
0
  }
846
0
847
0
  // And tell History to stop tracking us.
848
0
  if (mHistory && mCachedURI) {
849
#ifdef ANDROID
850
    nsCOMPtr<IHistory> history = services::GetHistoryService();
851
#elif defined(MOZ_PLACES)
852
    History* history = History::GetService();
853
#else
854
    nsCOMPtr<IHistory> history;
855
#endif
856
0
    if (history) {
857
0
      nsresult rv = history->UnregisterVisitedCallback(mCachedURI, this);
858
0
      NS_ASSERTION(NS_SUCCEEDED(rv), "This should only fail if we misuse the API!");
859
0
      if (NS_SUCCEEDED(rv)) {
860
0
        mRegistered = false;
861
0
      }
862
0
    }
863
0
  }
864
0
}
865
866
void
867
Link::SetHrefAttribute(nsIURI *aURI)
868
0
{
869
0
  NS_ASSERTION(aURI, "Null URI is illegal!");
870
0
871
0
  // if we change this code to not reserialize we need to do something smarter
872
0
  // in SetProtocol because changing the protocol of an URI can change the
873
0
  // "nature" of the nsIURL/nsIURI implementation.
874
0
  nsAutoCString href;
875
0
  (void)aURI->GetSpec(href);
876
0
  (void)mElement->SetAttr(kNameSpaceID_None, nsGkAtoms::href,
877
0
                          NS_ConvertUTF8toUTF16(href), true);
878
0
}
879
880
size_t
881
Link::SizeOfExcludingThis(mozilla::SizeOfState& aState) const
882
0
{
883
0
  size_t n = 0;
884
0
885
0
  if (mCachedURI) {
886
0
    nsCOMPtr<nsISizeOf> iface = do_QueryInterface(mCachedURI);
887
0
    if (iface) {
888
0
      n += iface->SizeOfIncludingThis(aState.mMallocSizeOf);
889
0
    }
890
0
  }
891
0
892
0
  // The following members don't need to be measured:
893
0
  // - mElement, because it is a pointer-to-self used to avoid QIs
894
0
895
0
  return n;
896
0
}
897
898
static const nsAttrValue::EnumTable kAsAttributeTable[] = {
899
  { "",              DESTINATION_INVALID       },
900
  { "audio",         DESTINATION_AUDIO         },
901
  { "font",          DESTINATION_FONT          },
902
  { "image",         DESTINATION_IMAGE         },
903
  { "script",        DESTINATION_SCRIPT        },
904
  { "style",         DESTINATION_STYLE         },
905
  { "track",         DESTINATION_TRACK         },
906
  { "video",         DESTINATION_VIDEO         },
907
  { "fetch",         DESTINATION_FETCH         },
908
  { nullptr,         0 }
909
};
910
911
912
/* static */ void
913
Link::ParseAsValue(const nsAString& aValue,
914
                   nsAttrValue& aResult)
915
0
{
916
0
  DebugOnly<bool> success =
917
0
  aResult.ParseEnumValue(aValue, kAsAttributeTable, false,
918
0
                         // default value is a empty string
919
0
                         // if aValue is not a value we
920
0
                         // understand
921
0
                         &kAsAttributeTable[0]);
922
0
  MOZ_ASSERT(success);
923
0
}
924
925
/* static */ nsContentPolicyType
926
Link::AsValueToContentPolicy(const nsAttrValue& aValue)
927
0
{
928
0
  switch(aValue.GetEnumValue()) {
929
0
  case DESTINATION_INVALID:
930
0
    return nsIContentPolicy::TYPE_INVALID;
931
0
  case DESTINATION_AUDIO:
932
0
    return nsIContentPolicy::TYPE_INTERNAL_AUDIO;
933
0
  case DESTINATION_TRACK:
934
0
    return nsIContentPolicy::TYPE_INTERNAL_TRACK;
935
0
  case DESTINATION_VIDEO:
936
0
    return nsIContentPolicy::TYPE_INTERNAL_VIDEO;
937
0
  case DESTINATION_FONT:
938
0
    return nsIContentPolicy::TYPE_FONT;
939
0
  case DESTINATION_IMAGE:
940
0
    return nsIContentPolicy::TYPE_IMAGE;
941
0
  case DESTINATION_SCRIPT:
942
0
    return nsIContentPolicy::TYPE_SCRIPT;
943
0
  case DESTINATION_STYLE:
944
0
    return nsIContentPolicy::TYPE_STYLESHEET;
945
0
  case DESTINATION_FETCH:
946
0
    return nsIContentPolicy::TYPE_OTHER;
947
0
  }
948
0
  return nsIContentPolicy::TYPE_INVALID;
949
0
}
950
951
} // namespace dom
952
} // namespace mozilla