LCOV - code coverage report
Current view: directory - content/base/src - Link.cpp (source / functions) Found Hit Coverage
Test: app.info Lines: 239 0 0.0 %
Date: 2012-06-02 Functions: 26 0 0.0 %

       1                 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
       2                 :  * vim: sw=2 ts=2 et :
       3                 :  * ***** BEGIN LICENSE BLOCK *****
       4                 :  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
       5                 :  *
       6                 :  * The contents of this file are subject to the Mozilla Public License Version
       7                 :  * 1.1 (the "License"); you may not use this file except in compliance with
       8                 :  * the License. You may obtain a copy of the License at
       9                 :  * http://www.mozilla.org/MPL/
      10                 :  *
      11                 :  * Software distributed under the License is distributed on an "AS IS" basis,
      12                 :  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
      13                 :  * for the specific language governing rights and limitations under the
      14                 :  * License.
      15                 :  *
      16                 :  * The Original Code is mozilla.org code.
      17                 :  *
      18                 :  * The Initial Developer of the Original Code is
      19                 :  * Mozilla Foundation.
      20                 :  * Portions created by the Initial Developer are Copyright (C) 2009
      21                 :  * the Initial Developer. All Rights Reserved.
      22                 :  *
      23                 :  * Contributor(s):
      24                 :  *   Shawn Wilsher <me@shawnwilsher.com> (Original Author)
      25                 :  *
      26                 :  * Alternatively, the contents of this file may be used under the terms of
      27                 :  * either of the GNU General Public License Version 2 or later (the "GPL"),
      28                 :  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
      29                 :  * in which case the provisions of the GPL or the LGPL are applicable instead
      30                 :  * of those above. If you wish to allow use of your version of this file only
      31                 :  * under the terms of either the GPL or the LGPL, and not to allow others to
      32                 :  * use your version of this file under the terms of the MPL, indicate your
      33                 :  * decision by deleting the provisions above and replace them with the notice
      34                 :  * and other provisions required by the GPL or the LGPL. If you do not delete
      35                 :  * the provisions above, a recipient may use your version of this file under
      36                 :  * the terms of any one of the MPL, the GPL or the LGPL.
      37                 :  *
      38                 :  * ***** END LICENSE BLOCK ***** */
      39                 : 
      40                 : #include "Link.h"
      41                 : 
      42                 : #include "nsEventStates.h"
      43                 : #include "nsIURL.h"
      44                 : #include "nsISizeOf.h"
      45                 : 
      46                 : #include "nsContentUtils.h"
      47                 : #include "nsEscape.h"
      48                 : #include "nsGkAtoms.h"
      49                 : #include "nsString.h"
      50                 : #include "mozAutoDocUpdate.h"
      51                 : 
      52                 : #include "mozilla/Services.h"
      53                 : 
      54                 : namespace mozilla {
      55                 : namespace dom {
      56                 : 
      57               0 : Link::Link(Element *aElement)
      58                 :   : mLinkState(defaultState)
      59                 :   , mRegistered(false)
      60                 :   , mElement(aElement)
      61               0 :   , mHistory(services::GetHistoryService())
      62                 : {
      63               0 :   NS_ABORT_IF_FALSE(mElement, "Must have an element");
      64               0 : }
      65                 : 
      66               0 : Link::~Link()
      67                 : {
      68               0 :   UnregisterFromHistory();
      69               0 : }
      70                 : 
      71                 : nsLinkState
      72               0 : Link::GetLinkState() const
      73                 : {
      74               0 :   NS_ASSERTION(mRegistered,
      75                 :                "Getting the link state of an unregistered Link!");
      76               0 :   NS_ASSERTION(mLinkState != eLinkState_Unknown,
      77                 :                "Getting the link state with an unknown value!");
      78               0 :   return mLinkState;
      79                 : }
      80                 : 
      81                 : void
      82               0 : Link::SetLinkState(nsLinkState aState)
      83                 : {
      84               0 :   NS_ASSERTION(mRegistered,
      85                 :                "Setting the link state of an unregistered Link!");
      86               0 :   NS_ASSERTION(mLinkState != aState,
      87                 :                "Setting state to the currently set state!");
      88                 : 
      89                 :   // Set our current state as appropriate.
      90               0 :   mLinkState = aState;
      91                 : 
      92                 :   // Per IHistory interface documentation, we are no longer registered.
      93               0 :   mRegistered = false;
      94                 : 
      95               0 :   NS_ABORT_IF_FALSE(LinkState() == NS_EVENT_STATE_VISITED ||
      96                 :                     LinkState() == NS_EVENT_STATE_UNVISITED,
      97                 :                     "Unexpected state obtained from LinkState()!");
      98                 : 
      99                 :   // Tell the element to update its visited state
     100               0 :   mElement->UpdateState(true);
     101               0 : }
     102                 : 
     103                 : nsEventStates
     104               0 : Link::LinkState() const
     105                 : {
     106                 :   // We are a constant method, but we are just lazily doing things and have to
     107                 :   // track that state.  Cast away that constness!
     108               0 :   Link *self = const_cast<Link *>(this);
     109                 : 
     110                 :   // If we are not in the document, default to not visited.
     111               0 :   Element *element = self->mElement;
     112               0 :   if (!element->IsInDoc()) {
     113               0 :     self->mLinkState = eLinkState_Unvisited;
     114                 :   }
     115                 : 
     116                 :   // If we have not yet registered for notifications and are in an unknown
     117                 :   // state, register now!
     118               0 :   if (!mRegistered && mLinkState == eLinkState_Unknown) {
     119                 :     // First, make sure the href attribute has a valid link (bug 23209).
     120               0 :     nsCOMPtr<nsIURI> hrefURI(GetURI());
     121               0 :     if (!hrefURI) {
     122               0 :       self->mLinkState = eLinkState_NotLink;
     123               0 :       return nsEventStates();
     124                 :     }
     125                 : 
     126                 :     // We have a good href, so register with History.
     127               0 :     nsresult rv = mHistory->RegisterVisitedCallback(hrefURI, self);
     128               0 :     if (NS_SUCCEEDED(rv)) {
     129               0 :       self->mRegistered = true;
     130                 : 
     131                 :       // Assume that we are not visited until we are told otherwise.
     132               0 :       self->mLinkState = eLinkState_Unvisited;
     133                 : 
     134                 :       // And make sure we are in the document's link map.
     135               0 :       nsIDocument *doc = element->GetCurrentDoc();
     136               0 :       if (doc) {
     137               0 :         doc->AddStyleRelevantLink(self);
     138                 :       }
     139                 :     }
     140                 :   }
     141                 : 
     142                 :   // Otherwise, return our known state.
     143               0 :   if (mLinkState == eLinkState_Visited) {
     144               0 :     return NS_EVENT_STATE_VISITED;
     145                 :   }
     146                 : 
     147               0 :   if (mLinkState == eLinkState_Unvisited) {
     148               0 :     return NS_EVENT_STATE_UNVISITED;
     149                 :   }
     150                 : 
     151               0 :   return nsEventStates();
     152                 : }
     153                 : 
     154                 : already_AddRefed<nsIURI>
     155               0 : Link::GetURI() const
     156                 : {
     157               0 :   nsCOMPtr<nsIURI> uri(mCachedURI);
     158                 : 
     159                 :   // If we have this URI cached, use it.
     160               0 :   if (uri) {
     161               0 :     return uri.forget();
     162                 :   }
     163                 : 
     164                 :   // Otherwise obtain it.
     165               0 :   Link *self = const_cast<Link *>(this);
     166               0 :   Element *element = self->mElement;
     167               0 :   uri = element->GetHrefURI();
     168                 : 
     169                 :   // We want to cache the URI if the node is in the document.
     170               0 :   if (uri && element->IsInDoc()) {
     171               0 :     mCachedURI = uri;
     172                 :   }
     173                 : 
     174               0 :   return uri.forget();
     175                 : }
     176                 : 
     177                 : nsresult
     178               0 : Link::SetProtocol(const nsAString &aProtocol)
     179                 : {
     180               0 :   nsCOMPtr<nsIURI> uri(GetURIToMutate());
     181               0 :   if (!uri) {
     182                 :     // Ignore failures to be compatible with NS4.
     183               0 :     return NS_OK;
     184                 :   }
     185                 : 
     186               0 :   nsAString::const_iterator start, end;
     187               0 :   aProtocol.BeginReading(start);
     188               0 :   aProtocol.EndReading(end);
     189               0 :   nsAString::const_iterator iter(start);
     190               0 :   (void)FindCharInReadable(':', iter, end);
     191               0 :   (void)uri->SetScheme(NS_ConvertUTF16toUTF8(Substring(start, iter)));
     192                 : 
     193               0 :   SetHrefAttribute(uri);
     194               0 :   return NS_OK;
     195                 : }
     196                 : 
     197                 : nsresult
     198               0 : Link::SetHost(const nsAString &aHost)
     199                 : {
     200               0 :   nsCOMPtr<nsIURI> uri(GetURIToMutate());
     201               0 :   if (!uri) {
     202                 :     // Ignore failures to be compatible with NS4.
     203               0 :     return NS_OK;
     204                 :   }
     205                 : 
     206                 :   // We cannot simply call nsIURI::SetHost because that would treat the name as
     207                 :   // an IPv6 address (like http:://[server:443]/).  We also cannot call
     208                 :   // nsIURI::SetHostPort because that isn't implemented.  Sadfaces.
     209                 : 
     210                 :   // First set the hostname.
     211               0 :   nsAString::const_iterator start, end;
     212               0 :   aHost.BeginReading(start);
     213               0 :   aHost.EndReading(end);
     214               0 :   nsAString::const_iterator iter(start);
     215               0 :   (void)FindCharInReadable(':', iter, end);
     216               0 :   NS_ConvertUTF16toUTF8 host(Substring(start, iter));
     217               0 :   (void)uri->SetHost(host);
     218                 : 
     219                 :   // Also set the port if needed.
     220               0 :   if (iter != end) {
     221               0 :     iter++;
     222               0 :     if (iter != end) {
     223               0 :       nsAutoString portStr(Substring(iter, end));
     224                 :       nsresult rv;
     225               0 :       PRInt32 port = portStr.ToInteger((PRInt32 *)&rv);
     226               0 :       if (NS_SUCCEEDED(rv)) {
     227               0 :         (void)uri->SetPort(port);
     228                 :       }
     229                 :     }
     230                 :   };
     231                 : 
     232               0 :   SetHrefAttribute(uri);
     233               0 :   return NS_OK;
     234                 : }
     235                 : 
     236                 : nsresult
     237               0 : Link::SetHostname(const nsAString &aHostname)
     238                 : {
     239               0 :   nsCOMPtr<nsIURI> uri(GetURIToMutate());
     240               0 :   if (!uri) {
     241                 :     // Ignore failures to be compatible with NS4.
     242               0 :     return NS_OK;
     243                 :   }
     244                 : 
     245               0 :   (void)uri->SetHost(NS_ConvertUTF16toUTF8(aHostname));
     246               0 :   SetHrefAttribute(uri);
     247               0 :   return NS_OK;
     248                 : }
     249                 : 
     250                 : nsresult
     251               0 : Link::SetPathname(const nsAString &aPathname)
     252                 : {
     253               0 :   nsCOMPtr<nsIURI> uri(GetURIToMutate());
     254               0 :   nsCOMPtr<nsIURL> url(do_QueryInterface(uri));
     255               0 :   if (!url) {
     256                 :     // Ignore failures to be compatible with NS4.
     257               0 :     return NS_OK;
     258                 :   }
     259                 : 
     260               0 :   (void)url->SetFilePath(NS_ConvertUTF16toUTF8(aPathname));
     261               0 :   SetHrefAttribute(uri);
     262               0 :   return NS_OK;
     263                 : }
     264                 : 
     265                 : nsresult
     266               0 : Link::SetSearch(const nsAString &aSearch)
     267                 : {
     268               0 :   nsCOMPtr<nsIURI> uri(GetURIToMutate());
     269               0 :   nsCOMPtr<nsIURL> url(do_QueryInterface(uri));
     270               0 :   if (!url) {
     271                 :     // Ignore failures to be compatible with NS4.
     272               0 :     return NS_OK;
     273                 :   }
     274                 : 
     275               0 :   (void)url->SetQuery(NS_ConvertUTF16toUTF8(aSearch));
     276               0 :   SetHrefAttribute(uri);
     277               0 :   return NS_OK;
     278                 : }
     279                 : 
     280                 : nsresult
     281               0 : Link::SetPort(const nsAString &aPort)
     282                 : {
     283               0 :   nsCOMPtr<nsIURI> uri(GetURIToMutate());
     284               0 :   if (!uri) {
     285                 :     // Ignore failures to be compatible with NS4.
     286               0 :     return NS_OK;
     287                 :   }
     288                 : 
     289                 :   nsresult rv;
     290               0 :   nsAutoString portStr(aPort);
     291               0 :   PRInt32 port = portStr.ToInteger((PRInt32 *)&rv);
     292               0 :   if (NS_FAILED(rv)) {
     293               0 :     return NS_OK;
     294                 :   }
     295                 : 
     296               0 :   (void)uri->SetPort(port);
     297               0 :   SetHrefAttribute(uri);
     298               0 :   return NS_OK;
     299                 : }
     300                 : 
     301                 : nsresult
     302               0 : Link::SetHash(const nsAString &aHash)
     303                 : {
     304               0 :   nsCOMPtr<nsIURI> uri(GetURIToMutate());
     305               0 :   if (!uri) {
     306                 :     // Ignore failures to be compatible with NS4.
     307               0 :     return NS_OK;
     308                 :   }
     309                 : 
     310               0 :   (void)uri->SetRef(NS_ConvertUTF16toUTF8(aHash));
     311               0 :   SetHrefAttribute(uri);
     312               0 :   return NS_OK;
     313                 : }
     314                 : 
     315                 : nsresult
     316               0 : Link::GetProtocol(nsAString &_protocol)
     317                 : {
     318               0 :   nsCOMPtr<nsIURI> uri(GetURI());
     319               0 :   if (!uri) {
     320               0 :     _protocol.AssignLiteral("http");
     321                 :   }
     322                 :   else {
     323               0 :     nsCAutoString scheme;
     324               0 :     (void)uri->GetScheme(scheme);
     325               0 :     CopyASCIItoUTF16(scheme, _protocol);
     326                 :   }
     327               0 :   _protocol.Append(PRUnichar(':'));
     328               0 :   return NS_OK;
     329                 : }
     330                 : 
     331                 : nsresult
     332               0 : Link::GetHost(nsAString &_host)
     333                 : {
     334               0 :   _host.Truncate();
     335                 : 
     336               0 :   nsCOMPtr<nsIURI> uri(GetURI());
     337               0 :   if (!uri) {
     338                 :     // Do not throw!  Not having a valid URI should result in an empty string.
     339               0 :     return NS_OK;
     340                 :   }
     341                 : 
     342               0 :   nsCAutoString hostport;
     343               0 :   nsresult rv = uri->GetHostPort(hostport);
     344               0 :   if (NS_SUCCEEDED(rv)) {
     345               0 :     CopyUTF8toUTF16(hostport, _host);
     346                 :   }
     347               0 :   return NS_OK;
     348                 : }
     349                 : 
     350                 : nsresult
     351               0 : Link::GetHostname(nsAString &_hostname)
     352                 : {
     353               0 :   _hostname.Truncate();
     354                 : 
     355               0 :   nsCOMPtr<nsIURI> uri(GetURI());
     356               0 :   if (!uri) {
     357                 :     // Do not throw!  Not having a valid URI should result in an empty string.
     358               0 :     return NS_OK;
     359                 :   }
     360                 : 
     361               0 :   nsCAutoString host;
     362               0 :   nsresult rv = uri->GetHost(host);
     363                 :   // Note that failure to get the host from the URI is not necessarily a bad
     364                 :   // thing.  Some URIs do not have a host.
     365               0 :   if (NS_SUCCEEDED(rv)) {
     366               0 :     CopyUTF8toUTF16(host, _hostname);
     367                 :   }
     368               0 :   return NS_OK;
     369                 : }
     370                 : 
     371                 : nsresult
     372               0 : Link::GetPathname(nsAString &_pathname)
     373                 : {
     374               0 :   _pathname.Truncate();
     375                 : 
     376               0 :   nsCOMPtr<nsIURI> uri(GetURI());
     377               0 :   nsCOMPtr<nsIURL> url(do_QueryInterface(uri));
     378               0 :   if (!url) {
     379                 :     // Do not throw!  Not having a valid URI or URL should result in an empty
     380                 :     // string.
     381               0 :     return NS_OK;
     382                 :   }
     383                 : 
     384               0 :   nsCAutoString file;
     385               0 :   nsresult rv = url->GetFilePath(file);
     386               0 :   NS_ENSURE_SUCCESS(rv, rv);
     387               0 :   CopyUTF8toUTF16(file, _pathname);
     388               0 :   return NS_OK;
     389                 : }
     390                 : 
     391                 : nsresult
     392               0 : Link::GetSearch(nsAString &_search)
     393                 : {
     394               0 :   _search.Truncate();
     395                 : 
     396               0 :   nsCOMPtr<nsIURI> uri(GetURI());
     397               0 :   nsCOMPtr<nsIURL> url(do_QueryInterface(uri));
     398               0 :   if (!url) {
     399                 :     // Do not throw!  Not having a valid URI or URL should result in an empty
     400                 :     // string.
     401               0 :     return NS_OK;
     402                 :   }
     403                 : 
     404               0 :   nsCAutoString search;
     405               0 :   nsresult rv = url->GetQuery(search);
     406               0 :   if (NS_SUCCEEDED(rv) && !search.IsEmpty()) {
     407               0 :     CopyUTF8toUTF16(NS_LITERAL_CSTRING("?") + search, _search);
     408                 :   }
     409               0 :   return NS_OK;
     410                 : }
     411                 : 
     412                 : nsresult
     413               0 : Link::GetPort(nsAString &_port)
     414                 : {
     415               0 :   _port.Truncate();
     416                 : 
     417               0 :   nsCOMPtr<nsIURI> uri(GetURI());
     418               0 :   if (!uri) {
     419                 :     // Do not throw!  Not having a valid URI should result in an empty string.
     420               0 :     return NS_OK;
     421                 :   }
     422                 : 
     423                 :   PRInt32 port;
     424               0 :   nsresult rv = uri->GetPort(&port);
     425                 :   // Note that failure to get the port from the URI is not necessarily a bad
     426                 :   // thing.  Some URIs do not have a port.
     427               0 :   if (NS_SUCCEEDED(rv) && port != -1) {
     428               0 :     nsAutoString portStr;
     429               0 :     portStr.AppendInt(port, 10);
     430               0 :     _port.Assign(portStr);
     431                 :   }
     432               0 :   return NS_OK;
     433                 : }
     434                 : 
     435                 : nsresult
     436               0 : Link::GetHash(nsAString &_hash)
     437                 : {
     438               0 :   _hash.Truncate();
     439                 : 
     440               0 :   nsCOMPtr<nsIURI> uri(GetURI());
     441               0 :   if (!uri) {
     442                 :     // Do not throw!  Not having a valid URI should result in an empty
     443                 :     // string.
     444               0 :     return NS_OK;
     445                 :   }
     446                 : 
     447               0 :   nsCAutoString ref;
     448               0 :   nsresult rv = uri->GetRef(ref);
     449               0 :   if (NS_SUCCEEDED(rv) && !ref.IsEmpty()) {
     450               0 :     NS_UnescapeURL(ref); // XXX may result in random non-ASCII bytes!
     451               0 :     _hash.Assign(PRUnichar('#'));
     452               0 :     AppendUTF8toUTF16(ref, _hash);
     453                 :   }
     454               0 :   return NS_OK;
     455                 : }
     456                 : 
     457                 : void
     458               0 : Link::ResetLinkState(bool aNotify)
     459                 : {
     460                 :   // If we are in our default state, bail early.
     461               0 :   if (mLinkState == defaultState) {
     462               0 :     return;
     463                 :   }
     464                 : 
     465               0 :   Element *element = mElement;
     466                 : 
     467                 :   // Tell the document to forget about this link if we were a link before.
     468               0 :   nsIDocument *doc = element->GetCurrentDoc();
     469               0 :   if (doc && mLinkState != eLinkState_NotLink) {
     470               0 :     doc->ForgetLink(this);
     471                 :   }
     472                 : 
     473               0 :   UnregisterFromHistory();
     474                 : 
     475                 :   // Update our state back to the default.
     476               0 :   mLinkState = defaultState;
     477                 : 
     478                 :   // Get rid of our cached URI.
     479               0 :   mCachedURI = nsnull;
     480                 : 
     481                 :   // We have to be very careful here: if aNotify is false we do NOT
     482                 :   // want to call UpdateState, because that will call into LinkState()
     483                 :   // and try to start off loads, etc.  But ResetLinkState is called
     484                 :   // with aNotify false when things are in inconsistent states, so
     485                 :   // we'll get confused in that situation.  Instead, just silently
     486                 :   // update the link state on mElement.
     487               0 :   if (aNotify) {
     488               0 :     mElement->UpdateState(aNotify);
     489                 :   } else {
     490               0 :     mElement->UpdateLinkState(nsEventStates());
     491                 :   }
     492                 : }
     493                 : 
     494                 : void
     495               0 : Link::UnregisterFromHistory()
     496                 : {
     497                 :   // If we are not registered, we have nothing to do.
     498               0 :   if (!mRegistered) {
     499               0 :     return;
     500                 :   }
     501                 : 
     502               0 :   NS_ASSERTION(mCachedURI, "mRegistered is true, but we have no cached URI?!");
     503                 : 
     504                 :   // And tell History to stop tracking us.
     505               0 :   nsresult rv = mHistory->UnregisterVisitedCallback(mCachedURI, this);
     506               0 :   NS_ASSERTION(NS_SUCCEEDED(rv), "This should only fail if we misuse the API!");
     507               0 :   if (NS_SUCCEEDED(rv)) {
     508               0 :     mRegistered = false;
     509                 :   }
     510                 : }
     511                 : 
     512                 : already_AddRefed<nsIURI>
     513               0 : Link::GetURIToMutate()
     514                 : {
     515               0 :   nsCOMPtr<nsIURI> uri(GetURI());
     516               0 :   if (!uri) {
     517               0 :     return nsnull;
     518                 :   }
     519               0 :   nsCOMPtr<nsIURI> clone;
     520               0 :   (void)uri->Clone(getter_AddRefs(clone));
     521               0 :   return clone.forget();
     522                 : }
     523                 : 
     524                 : void
     525               0 : Link::SetHrefAttribute(nsIURI *aURI)
     526                 : {
     527               0 :   NS_ASSERTION(aURI, "Null URI is illegal!");
     528                 : 
     529               0 :   nsCAutoString href;
     530               0 :   (void)aURI->GetSpec(href);
     531                 :   (void)mElement->SetAttr(kNameSpaceID_None, nsGkAtoms::href,
     532               0 :                           NS_ConvertUTF8toUTF16(href), true);
     533               0 : }
     534                 : 
     535                 : size_t
     536               0 : Link::SizeOfExcludingThis(nsMallocSizeOfFun aMallocSizeOf) const
     537                 : {
     538               0 :   size_t n = 0;
     539                 : 
     540               0 :   if (mCachedURI) {
     541               0 :     nsCOMPtr<nsISizeOf> iface = do_QueryInterface(mCachedURI);
     542               0 :     if (iface) {
     543               0 :       n += iface->SizeOfIncludingThis(aMallocSizeOf);
     544                 :     }
     545                 :   }
     546                 : 
     547                 :   // The following members don't need to be measured:
     548                 :   // - mElement, because it is a pointer-to-self used to avoid QIs
     549                 :   // - mHistory, because it is non-owning
     550                 : 
     551               0 :   return n;
     552                 : }
     553                 : 
     554                 : } // namespace dom
     555                 : } // namespace mozilla

Generated by: LCOV version 1.7