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