Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * vim: sw=2 ts=2 et :
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/. */
7 #include "Link.h"
9 #include "mozilla/EventStates.h"
10 #include "mozilla/MemoryReporting.h"
11 #include "mozilla/dom/Element.h"
12 #include "nsIURL.h"
13 #include "nsISizeOf.h"
15 #include "nsEscape.h"
16 #include "nsGkAtoms.h"
17 #include "nsString.h"
18 #include "mozAutoDocUpdate.h"
20 #include "mozilla/Services.h"
22 namespace mozilla {
23 namespace dom {
25 Link::Link(Element *aElement)
26 : mElement(aElement)
27 , mHistory(services::GetHistoryService())
28 , mLinkState(eLinkState_NotLink)
29 , mNeedsRegistration(false)
30 , mRegistered(false)
31 {
32 NS_ABORT_IF_FALSE(mElement, "Must have an element");
33 }
35 Link::~Link()
36 {
37 UnregisterFromHistory();
38 }
40 bool
41 Link::ElementHasHref() const
42 {
43 return ((!mElement->IsSVG() && mElement->HasAttr(kNameSpaceID_None, nsGkAtoms::href))
44 || (!mElement->IsHTML() && mElement->HasAttr(kNameSpaceID_XLink, nsGkAtoms::href)));
45 }
47 void
48 Link::SetLinkState(nsLinkState aState)
49 {
50 NS_ASSERTION(mRegistered,
51 "Setting the link state of an unregistered Link!");
52 NS_ASSERTION(mLinkState != aState,
53 "Setting state to the currently set state!");
55 // Set our current state as appropriate.
56 mLinkState = aState;
58 // Per IHistory interface documentation, we are no longer registered.
59 mRegistered = false;
61 NS_ABORT_IF_FALSE(LinkState() == NS_EVENT_STATE_VISITED ||
62 LinkState() == NS_EVENT_STATE_UNVISITED,
63 "Unexpected state obtained from LinkState()!");
65 // Tell the element to update its visited state
66 mElement->UpdateState(true);
67 }
69 EventStates
70 Link::LinkState() const
71 {
72 // We are a constant method, but we are just lazily doing things and have to
73 // track that state. Cast away that constness!
74 Link *self = const_cast<Link *>(this);
76 Element *element = self->mElement;
78 // If we have not yet registered for notifications and need to,
79 // due to our href changing, register now!
80 if (!mRegistered && mNeedsRegistration && element->IsInDoc()) {
81 // Only try and register once.
82 self->mNeedsRegistration = false;
84 nsCOMPtr<nsIURI> hrefURI(GetURI());
86 // Assume that we are not visited until we are told otherwise.
87 self->mLinkState = eLinkState_Unvisited;
89 // Make sure the href attribute has a valid link (bug 23209).
90 // If we have a good href, register with History if available.
91 if (mHistory && hrefURI) {
92 nsresult rv = mHistory->RegisterVisitedCallback(hrefURI, self);
93 if (NS_SUCCEEDED(rv)) {
94 self->mRegistered = true;
96 // And make sure we are in the document's link map.
97 element->GetCurrentDoc()->AddStyleRelevantLink(self);
98 }
99 }
100 }
102 // Otherwise, return our known state.
103 if (mLinkState == eLinkState_Visited) {
104 return NS_EVENT_STATE_VISITED;
105 }
107 if (mLinkState == eLinkState_Unvisited) {
108 return NS_EVENT_STATE_UNVISITED;
109 }
111 return EventStates();
112 }
114 nsIURI*
115 Link::GetURI() const
116 {
117 // If we have this URI cached, use it.
118 if (mCachedURI) {
119 return mCachedURI;
120 }
122 // Otherwise obtain it.
123 Link *self = const_cast<Link *>(this);
124 Element *element = self->mElement;
125 mCachedURI = element->GetHrefURI();
127 return mCachedURI;
128 }
130 void
131 Link::SetProtocol(const nsAString &aProtocol)
132 {
133 nsCOMPtr<nsIURI> uri(GetURIToMutate());
134 if (!uri) {
135 // Ignore failures to be compatible with NS4.
136 return;
137 }
139 nsAString::const_iterator start, end;
140 aProtocol.BeginReading(start);
141 aProtocol.EndReading(end);
142 nsAString::const_iterator iter(start);
143 (void)FindCharInReadable(':', iter, end);
144 (void)uri->SetScheme(NS_ConvertUTF16toUTF8(Substring(start, iter)));
146 SetHrefAttribute(uri);
147 }
149 void
150 Link::SetPassword(const nsAString &aPassword)
151 {
152 nsCOMPtr<nsIURI> uri(GetURIToMutate());
153 if (!uri) {
154 // Ignore failures to be compatible with NS4.
155 return;
156 }
158 uri->SetPassword(NS_ConvertUTF16toUTF8(aPassword));
159 SetHrefAttribute(uri);
160 }
162 void
163 Link::SetUsername(const nsAString &aUsername)
164 {
165 nsCOMPtr<nsIURI> uri(GetURIToMutate());
166 if (!uri) {
167 // Ignore failures to be compatible with NS4.
168 return;
169 }
171 uri->SetUsername(NS_ConvertUTF16toUTF8(aUsername));
172 SetHrefAttribute(uri);
173 }
175 void
176 Link::SetHost(const nsAString &aHost)
177 {
178 nsCOMPtr<nsIURI> uri(GetURIToMutate());
179 if (!uri) {
180 // Ignore failures to be compatible with NS4.
181 return;
182 }
184 (void)uri->SetHostPort(NS_ConvertUTF16toUTF8(aHost));
185 SetHrefAttribute(uri);
186 }
188 void
189 Link::SetHostname(const nsAString &aHostname)
190 {
191 nsCOMPtr<nsIURI> uri(GetURIToMutate());
192 if (!uri) {
193 // Ignore failures to be compatible with NS4.
194 return;
195 }
197 (void)uri->SetHost(NS_ConvertUTF16toUTF8(aHostname));
198 SetHrefAttribute(uri);
199 }
201 void
202 Link::SetPathname(const nsAString &aPathname)
203 {
204 nsCOMPtr<nsIURI> uri(GetURIToMutate());
205 nsCOMPtr<nsIURL> url(do_QueryInterface(uri));
206 if (!url) {
207 // Ignore failures to be compatible with NS4.
208 return;
209 }
211 (void)url->SetFilePath(NS_ConvertUTF16toUTF8(aPathname));
212 SetHrefAttribute(uri);
213 }
215 void
216 Link::SetSearch(const nsAString& aSearch)
217 {
218 SetSearchInternal(aSearch);
219 UpdateURLSearchParams();
220 }
222 void
223 Link::SetSearchInternal(const nsAString& aSearch)
224 {
225 nsCOMPtr<nsIURI> uri(GetURIToMutate());
226 nsCOMPtr<nsIURL> url(do_QueryInterface(uri));
227 if (!url) {
228 // Ignore failures to be compatible with NS4.
229 return;
230 }
232 (void)url->SetQuery(NS_ConvertUTF16toUTF8(aSearch));
233 SetHrefAttribute(uri);
234 }
236 void
237 Link::SetPort(const nsAString &aPort)
238 {
239 nsCOMPtr<nsIURI> uri(GetURIToMutate());
240 if (!uri) {
241 // Ignore failures to be compatible with NS4.
242 return;
243 }
245 nsresult rv;
246 nsAutoString portStr(aPort);
248 // nsIURI uses -1 as default value.
249 int32_t port = -1;
250 if (!aPort.IsEmpty()) {
251 port = portStr.ToInteger(&rv);
252 if (NS_FAILED(rv)) {
253 return;
254 }
255 }
257 (void)uri->SetPort(port);
258 SetHrefAttribute(uri);
259 }
261 void
262 Link::SetHash(const nsAString &aHash)
263 {
264 nsCOMPtr<nsIURI> uri(GetURIToMutate());
265 if (!uri) {
266 // Ignore failures to be compatible with NS4.
267 return;
268 }
270 (void)uri->SetRef(NS_ConvertUTF16toUTF8(aHash));
271 SetHrefAttribute(uri);
272 }
274 void
275 Link::GetOrigin(nsAString &aOrigin)
276 {
277 aOrigin.Truncate();
279 nsCOMPtr<nsIURI> uri(GetURI());
280 if (!uri) {
281 return;
282 }
284 nsString origin;
285 nsContentUtils::GetUTFNonNullOrigin(uri, origin);
286 aOrigin.Assign(origin);
287 }
289 void
290 Link::GetProtocol(nsAString &_protocol)
291 {
292 nsCOMPtr<nsIURI> uri(GetURI());
293 if (!uri) {
294 _protocol.AssignLiteral("http");
295 }
296 else {
297 nsAutoCString scheme;
298 (void)uri->GetScheme(scheme);
299 CopyASCIItoUTF16(scheme, _protocol);
300 }
301 _protocol.Append(char16_t(':'));
302 return;
303 }
305 void
306 Link::GetUsername(nsAString& aUsername)
307 {
308 aUsername.Truncate();
310 nsCOMPtr<nsIURI> uri(GetURI());
311 if (!uri) {
312 return;
313 }
315 nsAutoCString username;
316 uri->GetUsername(username);
317 CopyASCIItoUTF16(username, aUsername);
318 }
320 void
321 Link::GetPassword(nsAString &aPassword)
322 {
323 aPassword.Truncate();
325 nsCOMPtr<nsIURI> uri(GetURI());
326 if (!uri) {
327 return;
328 }
330 nsAutoCString password;
331 uri->GetPassword(password);
332 CopyASCIItoUTF16(password, aPassword);
333 }
335 void
336 Link::GetHost(nsAString &_host)
337 {
338 _host.Truncate();
340 nsCOMPtr<nsIURI> uri(GetURI());
341 if (!uri) {
342 // Do not throw! Not having a valid URI should result in an empty string.
343 return;
344 }
346 nsAutoCString hostport;
347 nsresult rv = uri->GetHostPort(hostport);
348 if (NS_SUCCEEDED(rv)) {
349 CopyUTF8toUTF16(hostport, _host);
350 }
351 }
353 void
354 Link::GetHostname(nsAString &_hostname)
355 {
356 _hostname.Truncate();
358 nsCOMPtr<nsIURI> uri(GetURI());
359 if (!uri) {
360 // Do not throw! Not having a valid URI should result in an empty string.
361 return;
362 }
364 nsAutoCString host;
365 nsresult rv = uri->GetHost(host);
366 // Note that failure to get the host from the URI is not necessarily a bad
367 // thing. Some URIs do not have a host.
368 if (NS_SUCCEEDED(rv)) {
369 CopyUTF8toUTF16(host, _hostname);
370 }
371 }
373 void
374 Link::GetPathname(nsAString &_pathname)
375 {
376 _pathname.Truncate();
378 nsCOMPtr<nsIURI> uri(GetURI());
379 nsCOMPtr<nsIURL> url(do_QueryInterface(uri));
380 if (!url) {
381 // Do not throw! Not having a valid URI or URL should result in an empty
382 // string.
383 return;
384 }
386 nsAutoCString file;
387 nsresult rv = url->GetFilePath(file);
388 if (NS_SUCCEEDED(rv)) {
389 CopyUTF8toUTF16(file, _pathname);
390 }
391 }
393 void
394 Link::GetSearch(nsAString &_search)
395 {
396 _search.Truncate();
398 nsCOMPtr<nsIURI> uri(GetURI());
399 nsCOMPtr<nsIURL> url(do_QueryInterface(uri));
400 if (!url) {
401 // Do not throw! Not having a valid URI or URL should result in an empty
402 // string.
403 return;
404 }
406 nsAutoCString search;
407 nsresult rv = url->GetQuery(search);
408 if (NS_SUCCEEDED(rv) && !search.IsEmpty()) {
409 CopyUTF8toUTF16(NS_LITERAL_CSTRING("?") + search, _search);
410 }
411 }
413 void
414 Link::GetPort(nsAString &_port)
415 {
416 _port.Truncate();
418 nsCOMPtr<nsIURI> uri(GetURI());
419 if (!uri) {
420 // Do not throw! Not having a valid URI should result in an empty string.
421 return;
422 }
424 int32_t port;
425 nsresult rv = uri->GetPort(&port);
426 // Note that failure to get the port from the URI is not necessarily a bad
427 // thing. Some URIs do not have a port.
428 if (NS_SUCCEEDED(rv) && port != -1) {
429 nsAutoString portStr;
430 portStr.AppendInt(port, 10);
431 _port.Assign(portStr);
432 }
433 }
435 void
436 Link::GetHash(nsAString &_hash)
437 {
438 _hash.Truncate();
440 nsCOMPtr<nsIURI> uri(GetURI());
441 if (!uri) {
442 // Do not throw! Not having a valid URI should result in an empty
443 // string.
444 return;
445 }
447 nsAutoCString ref;
448 nsresult rv = uri->GetRef(ref);
449 if (NS_SUCCEEDED(rv) && !ref.IsEmpty()) {
450 NS_UnescapeURL(ref); // XXX may result in random non-ASCII bytes!
451 _hash.Assign(char16_t('#'));
452 AppendUTF8toUTF16(ref, _hash);
453 }
454 }
456 void
457 Link::ResetLinkState(bool aNotify, bool aHasHref)
458 {
459 nsLinkState defaultState;
461 // The default state for links with an href is unvisited.
462 if (aHasHref) {
463 defaultState = eLinkState_Unvisited;
464 } else {
465 defaultState = eLinkState_NotLink;
466 }
468 // If !mNeedsRegstration, then either we've never registered, or we're
469 // currently registered; in either case, we should remove ourself
470 // from the doc and the history.
471 if (!mNeedsRegistration && mLinkState != eLinkState_NotLink) {
472 nsIDocument *doc = mElement->GetCurrentDoc();
473 if (doc && (mRegistered || mLinkState == eLinkState_Visited)) {
474 // Tell the document to forget about this link if we've registered
475 // with it before.
476 doc->ForgetLink(this);
477 }
479 UnregisterFromHistory();
480 }
482 // If we have an href, we should register with the history.
483 mNeedsRegistration = aHasHref;
485 // If we've cached the URI, reset always invalidates it.
486 mCachedURI = nullptr;
487 UpdateURLSearchParams();
489 // Update our state back to the default.
490 mLinkState = defaultState;
492 // We have to be very careful here: if aNotify is false we do NOT
493 // want to call UpdateState, because that will call into LinkState()
494 // and try to start off loads, etc. But ResetLinkState is called
495 // with aNotify false when things are in inconsistent states, so
496 // we'll get confused in that situation. Instead, just silently
497 // update the link state on mElement. Since we might have set the
498 // link state to unvisited, make sure to update with that state if
499 // required.
500 if (aNotify) {
501 mElement->UpdateState(aNotify);
502 } else {
503 if (mLinkState == eLinkState_Unvisited) {
504 mElement->UpdateLinkState(NS_EVENT_STATE_UNVISITED);
505 } else {
506 mElement->UpdateLinkState(EventStates());
507 }
508 }
509 }
511 void
512 Link::UnregisterFromHistory()
513 {
514 // If we are not registered, we have nothing to do.
515 if (!mRegistered) {
516 return;
517 }
519 NS_ASSERTION(mCachedURI, "mRegistered is true, but we have no cached URI?!");
521 // And tell History to stop tracking us.
522 if (mHistory) {
523 nsresult rv = mHistory->UnregisterVisitedCallback(mCachedURI, this);
524 NS_ASSERTION(NS_SUCCEEDED(rv), "This should only fail if we misuse the API!");
525 if (NS_SUCCEEDED(rv)) {
526 mRegistered = false;
527 }
528 }
529 }
531 already_AddRefed<nsIURI>
532 Link::GetURIToMutate()
533 {
534 nsCOMPtr<nsIURI> uri(GetURI());
535 if (!uri) {
536 return nullptr;
537 }
538 nsCOMPtr<nsIURI> clone;
539 (void)uri->Clone(getter_AddRefs(clone));
540 return clone.forget();
541 }
543 void
544 Link::SetHrefAttribute(nsIURI *aURI)
545 {
546 NS_ASSERTION(aURI, "Null URI is illegal!");
548 nsAutoCString href;
549 (void)aURI->GetSpec(href);
550 (void)mElement->SetAttr(kNameSpaceID_None, nsGkAtoms::href,
551 NS_ConvertUTF8toUTF16(href), true);
552 }
554 size_t
555 Link::SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
556 {
557 size_t n = 0;
559 if (mCachedURI) {
560 nsCOMPtr<nsISizeOf> iface = do_QueryInterface(mCachedURI);
561 if (iface) {
562 n += iface->SizeOfIncludingThis(aMallocSizeOf);
563 }
564 }
566 // The following members don't need to be measured:
567 // - mElement, because it is a pointer-to-self used to avoid QIs
568 // - mHistory, because it is non-owning
570 return n;
571 }
573 URLSearchParams*
574 Link::SearchParams()
575 {
576 CreateSearchParamsIfNeeded();
577 return mSearchParams;
578 }
580 void
581 Link::SetSearchParams(URLSearchParams& aSearchParams)
582 {
583 if (mSearchParams) {
584 mSearchParams->RemoveObserver(this);
585 }
587 mSearchParams = &aSearchParams;
588 mSearchParams->AddObserver(this);
590 nsAutoString search;
591 mSearchParams->Serialize(search);
592 SetSearchInternal(search);
593 }
595 void
596 Link::URLSearchParamsUpdated()
597 {
598 MOZ_ASSERT(mSearchParams);
600 nsString search;
601 mSearchParams->Serialize(search);
602 SetSearchInternal(search);
603 }
605 void
606 Link::UpdateURLSearchParams()
607 {
608 if (!mSearchParams) {
609 return;
610 }
612 nsAutoCString search;
613 nsCOMPtr<nsIURI> uri(GetURI());
614 nsCOMPtr<nsIURL> url(do_QueryInterface(uri));
615 if (url) {
616 nsresult rv = url->GetQuery(search);
617 if (NS_FAILED(rv)) {
618 NS_WARNING("Failed to get the query from a nsIURL.");
619 }
620 }
622 mSearchParams->ParseInput(search, this);
623 }
625 void
626 Link::CreateSearchParamsIfNeeded()
627 {
628 if (!mSearchParams) {
629 mSearchParams = new URLSearchParams();
630 mSearchParams->AddObserver(this);
631 UpdateURLSearchParams();
632 }
633 }
635 void
636 Link::Unlink()
637 {
638 if (mSearchParams) {
639 mSearchParams->RemoveObserver(this);
640 mSearchParams = nullptr;
641 }
642 }
644 void
645 Link::Traverse(nsCycleCollectionTraversalCallback &cb)
646 {
647 Link* tmp = this;
648 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSearchParams);
649 }
651 } // namespace dom
652 } // namespace mozilla