LCOV - code coverage report
Current view: directory - content/html/content/src - nsHTMLDNSPrefetch.cpp (source / functions) Found Hit Coverage
Test: app.info Lines: 178 49 27.5 %
Date: 2012-06-02 Functions: 35 12 34.3 %

       1                 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2                 : /* ***** BEGIN LICENSE BLOCK *****
       3                 :  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
       4                 :  *
       5                 :  * The contents of this file are subject to the Mozilla Public License Version
       6                 :  * 1.1 (the "License"); you may not use this file except in compliance with
       7                 :  * the License. You may obtain a copy of the License at
       8                 :  * http://www.mozilla.org/MPL/
       9                 :  *
      10                 :  * Software distributed under the License is distributed on an "AS IS" basis,
      11                 :  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
      12                 :  * for the specific language governing rights and limitations under the
      13                 :  * License.
      14                 :  *
      15                 :  * The Original Code is Mozilla.
      16                 :  *
      17                 :  * The Initial Developer of the Original Code is
      18                 :  * Mozilla Corporation.
      19                 :  * Portions created by the Initial Developer are Copyright (C) 2008
      20                 :  * the Initial Developer. All Rights Reserved.
      21                 :  *
      22                 :  * Contributor(s):
      23                 :  *   Patrick McManus <mcmanus@ducksong.com>
      24                 :  *
      25                 :  * Alternatively, the contents of this file may be used under the terms of
      26                 :  * either of the GNU General Public License Version 2 or later (the "GPL"),
      27                 :  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
      28                 :  * in which case the provisions of the GPL or the LGPL are applicable instead
      29                 :  * of those above. If you wish to allow use of your version of this file only
      30                 :  * under the terms of either the GPL or the LGPL, and not to allow others to
      31                 :  * use your version of this file under the terms of the MPL, indicate your
      32                 :  * decision by deleting the provisions above and replace them with the notice
      33                 :  * and other provisions required by the GPL or the LGPL. If you do not delete
      34                 :  * the provisions above, a recipient may use your version of this file under
      35                 :  * the terms of any one of the MPL, the GPL or the LGPL.
      36                 :  *
      37                 :  * ***** END LICENSE BLOCK ***** */
      38                 : 
      39                 : #include "base/basictypes.h"
      40                 : #include "mozilla/net/NeckoCommon.h"
      41                 : #include "mozilla/net/NeckoChild.h"
      42                 : #include "nsURLHelper.h"
      43                 : 
      44                 : #include "nsHTMLDNSPrefetch.h"
      45                 : #include "nsCOMPtr.h"
      46                 : #include "nsString.h"
      47                 : 
      48                 : #include "nsNetUtil.h"
      49                 : 
      50                 : #include "nsIDNSListener.h"
      51                 : #include "nsIWebProgressListener.h"
      52                 : #include "nsIWebProgress.h"
      53                 : #include "nsCURILoader.h"
      54                 : #include "nsIDNSRecord.h"
      55                 : #include "nsIDNSService.h"
      56                 : #include "nsICancelable.h"
      57                 : #include "nsGkAtoms.h"
      58                 : #include "nsIDocument.h"
      59                 : #include "nsThreadUtils.h"
      60                 : #include "nsITimer.h"
      61                 : #include "nsIObserverService.h"
      62                 : #include "mozilla/dom/Link.h"
      63                 : 
      64                 : #include "mozilla/Preferences.h"
      65                 : 
      66                 : using namespace mozilla;
      67                 : using namespace mozilla::dom;
      68                 : using namespace mozilla::net;
      69                 : 
      70                 : static NS_DEFINE_CID(kDNSServiceCID, NS_DNSSERVICE_CID);
      71                 : bool sDisablePrefetchHTTPSPref;
      72                 : static bool sInitialized = false;
      73                 : static nsIDNSService *sDNSService = nsnull;
      74                 : static nsHTMLDNSPrefetch::nsDeferrals *sPrefetches = nsnull;
      75                 : static nsHTMLDNSPrefetch::nsListener *sDNSListener = nsnull;
      76                 : 
      77                 : nsresult
      78            1404 : nsHTMLDNSPrefetch::Initialize()
      79                 : {
      80            1404 :   if (sInitialized) {
      81               0 :     NS_WARNING("Initialize() called twice");
      82               0 :     return NS_OK;
      83                 :   }
      84                 :   
      85            1404 :   sPrefetches = new nsHTMLDNSPrefetch::nsDeferrals();
      86            1404 :   if (!sPrefetches)
      87               0 :     return NS_ERROR_OUT_OF_MEMORY;
      88            1404 :   NS_ADDREF(sPrefetches);
      89                 : 
      90            1404 :   sDNSListener = new nsHTMLDNSPrefetch::nsListener();
      91            1404 :   if (!sDNSListener) {
      92               0 :     NS_IF_RELEASE(sPrefetches);
      93               0 :     return NS_ERROR_OUT_OF_MEMORY;
      94                 :   }
      95            1404 :   NS_ADDREF(sDNSListener);
      96                 : 
      97            1404 :   sPrefetches->Activate();
      98                 : 
      99                 :   Preferences::AddBoolVarCache(&sDisablePrefetchHTTPSPref,
     100            1404 :                                "network.dns.disablePrefetchFromHTTPS");
     101                 :   
     102                 :   // Default is false, so we need an explicit call to prime the cache.
     103                 :   sDisablePrefetchHTTPSPref = 
     104            1404 :     Preferences::GetBool("network.dns.disablePrefetchFromHTTPS", true);
     105                 :   
     106            1404 :   NS_IF_RELEASE(sDNSService);
     107                 :   nsresult rv;
     108            1404 :   rv = CallGetService(kDNSServiceCID, &sDNSService);
     109            1404 :   if (NS_FAILED(rv)) return rv;
     110                 :   
     111            1404 :   if (IsNeckoChild())
     112               0 :     NeckoChild::InitNeckoChild();
     113                 : 
     114            1404 :   sInitialized = true;
     115            1404 :   return NS_OK;
     116                 : }
     117                 : 
     118                 : nsresult
     119            1403 : nsHTMLDNSPrefetch::Shutdown()
     120                 : {
     121            1403 :   if (!sInitialized) {
     122               0 :     NS_WARNING("Not Initialized");
     123               0 :     return NS_OK;
     124                 :   }
     125            1403 :   sInitialized = false;
     126            1403 :   NS_IF_RELEASE(sDNSService);
     127            1403 :   NS_IF_RELEASE(sPrefetches);
     128            1403 :   NS_IF_RELEASE(sDNSListener);
     129                 :   
     130            1403 :   return NS_OK;
     131                 : }
     132                 : 
     133                 : bool
     134               0 : nsHTMLDNSPrefetch::IsAllowed (nsIDocument *aDocument)
     135                 : {
     136                 :   // There is no need to do prefetch on non UI scenarios such as XMLHttpRequest.
     137               0 :   return aDocument->IsDNSPrefetchAllowed() && aDocument->GetWindow();
     138                 : }
     139                 : 
     140                 : nsresult
     141               0 : nsHTMLDNSPrefetch::Prefetch(Link *aElement, PRUint16 flags)
     142                 : {
     143               0 :   if (!(sInitialized && sPrefetches && sDNSService && sDNSListener))
     144               0 :     return NS_ERROR_NOT_AVAILABLE;
     145                 : 
     146               0 :   return sPrefetches->Add(flags, aElement);
     147                 : }
     148                 : 
     149                 : nsresult
     150               0 : nsHTMLDNSPrefetch::PrefetchLow(Link *aElement)
     151                 : {
     152               0 :   return Prefetch(aElement, nsIDNSService::RESOLVE_PRIORITY_LOW);
     153                 : }
     154                 : 
     155                 : nsresult
     156               0 : nsHTMLDNSPrefetch::PrefetchMedium(Link *aElement)
     157                 : {
     158               0 :   return Prefetch(aElement, nsIDNSService::RESOLVE_PRIORITY_MEDIUM);
     159                 : }
     160                 : 
     161                 : nsresult
     162               0 : nsHTMLDNSPrefetch::PrefetchHigh(Link *aElement)
     163                 : {
     164               0 :   return Prefetch(aElement, 0);
     165                 : }
     166                 : 
     167                 : nsresult
     168               0 : nsHTMLDNSPrefetch::Prefetch(const nsAString &hostname, PRUint16 flags)
     169                 : {
     170               0 :   if (IsNeckoChild()) {
     171                 :     // We need to check IsEmpty() because net_IsValidHostName()
     172                 :     // considers empty strings to be valid hostnames
     173               0 :     if (!hostname.IsEmpty() &&
     174               0 :         net_IsValidHostName(NS_ConvertUTF16toUTF8(hostname))) {
     175               0 :       gNeckoChild->SendHTMLDNSPrefetch(nsAutoString(hostname), flags);
     176                 :     }
     177               0 :     return NS_OK;
     178                 :   }
     179                 : 
     180               0 :   if (!(sInitialized && sDNSService && sPrefetches && sDNSListener))
     181               0 :     return NS_ERROR_NOT_AVAILABLE;
     182                 : 
     183               0 :   nsCOMPtr<nsICancelable> tmpOutstanding;
     184               0 :   return sDNSService->AsyncResolve(NS_ConvertUTF16toUTF8(hostname),
     185                 :                                    flags | nsIDNSService::RESOLVE_SPECULATE,
     186                 :                                    sDNSListener, nsnull, 
     187               0 :                                    getter_AddRefs(tmpOutstanding));
     188                 : }
     189                 : 
     190                 : nsresult
     191               0 : nsHTMLDNSPrefetch::PrefetchLow(const nsAString &hostname)
     192                 : {
     193               0 :   return Prefetch(hostname, nsIDNSService::RESOLVE_PRIORITY_LOW);
     194                 : }
     195                 : 
     196                 : nsresult
     197               0 : nsHTMLDNSPrefetch::PrefetchMedium(const nsAString &hostname)
     198                 : {
     199               0 :   return Prefetch(hostname, nsIDNSService::RESOLVE_PRIORITY_MEDIUM);
     200                 : }
     201                 : 
     202                 : nsresult
     203               0 : nsHTMLDNSPrefetch::PrefetchHigh(const nsAString &hostname)
     204                 : {
     205               0 :   return Prefetch(hostname, 0);
     206                 : }
     207                 : 
     208                 : nsresult
     209               0 : nsHTMLDNSPrefetch::CancelPrefetch(Link *aElement,
     210                 :                                   PRUint16 flags,
     211                 :                                   nsresult aReason)
     212                 : {
     213               0 :   if (!(sInitialized && sPrefetches && sDNSService && sDNSListener))
     214               0 :     return NS_ERROR_NOT_AVAILABLE;
     215                 : 
     216               0 :   nsAutoString hostname;
     217               0 :   nsresult rv = aElement->GetHostname(hostname);
     218               0 :   NS_ENSURE_SUCCESS(rv, rv);
     219               0 :   return CancelPrefetch(hostname, flags, aReason);
     220                 : }
     221                 : 
     222                 : nsresult
     223               0 : nsHTMLDNSPrefetch::CancelPrefetch(const nsAString &hostname,
     224                 :                                   PRUint16 flags,
     225                 :                                   nsresult aReason)
     226                 : {
     227                 :   // Forward this request to Necko Parent if we're a child process
     228               0 :   if (IsNeckoChild()) {
     229                 :     // We need to check IsEmpty() because net_IsValidHostName()
     230                 :     // considers empty strings to be valid hostnames
     231               0 :     if (!hostname.IsEmpty() &&
     232               0 :         net_IsValidHostName(NS_ConvertUTF16toUTF8(hostname))) {
     233               0 :       gNeckoChild->SendCancelHTMLDNSPrefetch(nsString(hostname), flags,
     234               0 :                                              aReason);
     235                 :     }
     236               0 :     return NS_OK;
     237                 :   }
     238                 : 
     239               0 :   if (!(sInitialized && sDNSService && sPrefetches && sDNSListener))
     240               0 :     return NS_ERROR_NOT_AVAILABLE;
     241                 : 
     242                 :   // Forward cancellation to DNS service
     243               0 :   return sDNSService->CancelAsyncResolve(NS_ConvertUTF16toUTF8(hostname),
     244                 :                                          flags
     245                 :                                          | nsIDNSService::RESOLVE_SPECULATE,
     246               0 :                                          sDNSListener, aReason);
     247                 : }
     248                 : 
     249                 : nsresult
     250               0 : nsHTMLDNSPrefetch::CancelPrefetchLow(Link *aElement, nsresult aReason)
     251                 : {
     252                 :   return CancelPrefetch(aElement, nsIDNSService::RESOLVE_PRIORITY_LOW,
     253               0 :                         aReason);
     254                 : }
     255                 : 
     256                 : nsresult
     257               0 : nsHTMLDNSPrefetch::CancelPrefetchLow(const nsAString &hostname, nsresult aReason)
     258                 : {
     259                 :   return CancelPrefetch(hostname, nsIDNSService::RESOLVE_PRIORITY_LOW,
     260               0 :                         aReason);
     261                 : }
     262                 : 
     263                 : 
     264                 : /////////////////////////////////////////////////////////////////////////////////////////////////////////
     265                 : 
     266            4210 : NS_IMPL_THREADSAFE_ISUPPORTS1(nsHTMLDNSPrefetch::nsListener,
     267                 :                               nsIDNSListener)
     268                 : 
     269                 : NS_IMETHODIMP
     270               0 : nsHTMLDNSPrefetch::nsListener::OnLookupComplete(nsICancelable *request,
     271                 :                                               nsIDNSRecord  *rec,
     272                 :                                               nsresult       status)
     273                 : {
     274               0 :   return NS_OK;
     275                 : }
     276                 : 
     277                 : /////////////////////////////////////////////////////////////////////////////////////////////////////////
     278                 : 
     279            1404 : nsHTMLDNSPrefetch::nsDeferrals::nsDeferrals()
     280                 :   : mHead(0),
     281                 :     mTail(0),
     282                 :     mActiveLoaderCount(0),
     283            1404 :     mTimerArmed(false)
     284                 : {
     285            1404 :   mTimer = do_CreateInstance("@mozilla.org/timer;1");
     286            1404 : }
     287                 : 
     288          723948 : nsHTMLDNSPrefetch::nsDeferrals::~nsDeferrals()
     289                 : {
     290            1403 :   if (mTimerArmed) {
     291               0 :     mTimerArmed = false;
     292               0 :     mTimer->Cancel();
     293                 :   }
     294                 : 
     295            1403 :   Flush();
     296          722545 : }
     297                 : 
     298           28204 : NS_IMPL_ISUPPORTS3(nsHTMLDNSPrefetch::nsDeferrals,
     299                 :                    nsIWebProgressListener,
     300                 :                    nsISupportsWeakReference,
     301                 :                    nsIObserver)
     302                 : 
     303                 : void
     304            2807 : nsHTMLDNSPrefetch::nsDeferrals::Flush()
     305                 : {
     306            5614 :   while (mHead != mTail) {
     307               0 :     mEntries[mTail].mElement = nsnull;
     308               0 :     mTail = (mTail + 1) & sMaxDeferredMask;
     309                 :   }
     310            2807 : }
     311                 : 
     312                 : nsresult
     313               0 : nsHTMLDNSPrefetch::nsDeferrals::Add(PRUint16 flags, Link *aElement)
     314                 : {
     315                 :   // The FIFO has no lock, so it can only be accessed on main thread
     316               0 :   NS_ASSERTION(NS_IsMainThread(), "nsDeferrals::Add must be on main thread");
     317                 : 
     318               0 :   aElement->OnDNSPrefetchDeferred();
     319                 : 
     320               0 :   if (((mHead + 1) & sMaxDeferredMask) == mTail)
     321               0 :     return NS_ERROR_DNS_LOOKUP_QUEUE_FULL;
     322                 :     
     323               0 :   mEntries[mHead].mFlags = flags;
     324               0 :   mEntries[mHead].mElement = do_GetWeakReference(aElement);
     325               0 :   mHead = (mHead + 1) & sMaxDeferredMask;
     326                 : 
     327               0 :   if (!mActiveLoaderCount && !mTimerArmed && mTimer) {
     328               0 :     mTimerArmed = true;
     329               0 :     mTimer->InitWithFuncCallback(Tick, this, 2000, nsITimer::TYPE_ONE_SHOT);
     330                 :   }
     331                 :   
     332               0 :   return NS_OK;
     333                 : }
     334                 : 
     335                 : void
     336               0 : nsHTMLDNSPrefetch::nsDeferrals::SubmitQueue()
     337                 : {
     338               0 :   NS_ASSERTION(NS_IsMainThread(), "nsDeferrals::SubmitQueue must be on main thread");
     339               0 :   nsCString hostName;
     340               0 :   if (!sDNSService) return;
     341                 : 
     342               0 :   while (mHead != mTail) {
     343               0 :     nsCOMPtr<nsIContent> content = do_QueryReferent(mEntries[mTail].mElement);
     344               0 :     if (content) {
     345               0 :       nsCOMPtr<Link> link = do_QueryInterface(content);
     346                 :       // Only prefetch here if request was deferred and deferral not cancelled
     347               0 :       if (link && link->HasDeferredDNSPrefetchRequest()) {
     348               0 :         nsCOMPtr<nsIURI> hrefURI(link ? link->GetURI() : nsnull);
     349               0 :         if (hrefURI)
     350               0 :           hrefURI->GetAsciiHost(hostName);
     351                 : 
     352               0 :         if (!hostName.IsEmpty()) {
     353               0 :           if (IsNeckoChild()) {
     354               0 :             gNeckoChild->SendHTMLDNSPrefetch(NS_ConvertUTF8toUTF16(hostName),
     355               0 :                                            mEntries[mTail].mFlags);
     356                 :           } else {
     357               0 :             nsCOMPtr<nsICancelable> tmpOutstanding;
     358                 : 
     359                 :             nsresult rv = sDNSService->AsyncResolve(hostName, 
     360                 :                                     mEntries[mTail].mFlags
     361                 :                                     | nsIDNSService::RESOLVE_SPECULATE,
     362                 :                                     sDNSListener, nsnull,
     363               0 :                                     getter_AddRefs(tmpOutstanding));
     364                 :             // Tell link that deferred prefetch was requested
     365               0 :             if (NS_SUCCEEDED(rv))
     366               0 :               link->OnDNSPrefetchRequested();
     367                 :           }
     368                 :         }
     369                 :       }
     370                 :     }
     371                 :     
     372               0 :     mEntries[mTail].mElement = nsnull;
     373               0 :     mTail = (mTail + 1) & sMaxDeferredMask;
     374                 :   }
     375                 :   
     376               0 :   if (mTimerArmed) {
     377               0 :     mTimerArmed = false;
     378               0 :     mTimer->Cancel();
     379                 :   }
     380                 : }
     381                 : 
     382                 : void
     383            1404 : nsHTMLDNSPrefetch::nsDeferrals::Activate()
     384                 : {
     385                 :   // Register as an observer for the document loader  
     386                 :   nsCOMPtr<nsIWebProgress> progress = 
     387            2808 :     do_GetService(NS_DOCUMENTLOADER_SERVICE_CONTRACTID);
     388            1404 :   if (progress)
     389            1404 :     progress->AddProgressListener(this, nsIWebProgress::NOTIFY_STATE_DOCUMENT);
     390                 : 
     391                 :   // Register as an observer for xpcom shutdown events so we can drop any element refs
     392                 :   nsCOMPtr<nsIObserverService> observerService =
     393            2808 :     mozilla::services::GetObserverService();
     394            1404 :   if (observerService)
     395            1404 :     observerService->AddObserver(this, "xpcom-shutdown", true);
     396            1404 : }
     397                 : 
     398                 : // nsITimer related method
     399                 : 
     400                 : void 
     401               0 : nsHTMLDNSPrefetch::nsDeferrals::Tick(nsITimer *aTimer, void *aClosure)
     402                 : {
     403               0 :   nsHTMLDNSPrefetch::nsDeferrals *self = (nsHTMLDNSPrefetch::nsDeferrals *) aClosure;
     404                 : 
     405               0 :   NS_ASSERTION(NS_IsMainThread(), "nsDeferrals::Tick must be on main thread");
     406               0 :   NS_ASSERTION(self->mTimerArmed, "Timer is not armed");
     407                 :   
     408               0 :   self->mTimerArmed = false;
     409                 : 
     410                 :   // If the queue is not submitted here because there are outstanding pages being loaded,
     411                 :   // there is no need to rearm the timer as the queue will be submtited when those 
     412                 :   // loads complete.
     413               0 :   if (!self->mActiveLoaderCount) 
     414               0 :     self->SubmitQueue();
     415               0 : }
     416                 : 
     417                 : //////////// nsIWebProgressListener methods
     418                 : 
     419                 : NS_IMETHODIMP 
     420               0 : nsHTMLDNSPrefetch::nsDeferrals::OnStateChange(nsIWebProgress* aWebProgress, 
     421                 :                                               nsIRequest *aRequest, 
     422                 :                                               PRUint32 progressStateFlags, 
     423                 :                                               nsresult aStatus)
     424                 : {
     425                 :   // The FIFO has no lock, so it can only be accessed on main thread
     426               0 :   NS_ASSERTION(NS_IsMainThread(), "nsDeferrals::OnStateChange must be on main thread");
     427                 :   
     428               0 :   if (progressStateFlags & STATE_IS_DOCUMENT) {
     429               0 :     if (progressStateFlags & STATE_STOP) {
     430                 : 
     431                 :       // Initialization may have missed a STATE_START notification, so do
     432                 :       // not go negative
     433               0 :       if (mActiveLoaderCount)
     434               0 :         mActiveLoaderCount--;
     435                 : 
     436               0 :       if (!mActiveLoaderCount)
     437               0 :         SubmitQueue();
     438                 :     }
     439               0 :     else if (progressStateFlags & STATE_START)
     440               0 :       mActiveLoaderCount++;
     441                 :   }
     442                 :             
     443               0 :   return NS_OK;
     444                 : }
     445                 : 
     446                 : NS_IMETHODIMP
     447               0 : nsHTMLDNSPrefetch::nsDeferrals::OnProgressChange(nsIWebProgress *aProgress,
     448                 :                                                  nsIRequest *aRequest, 
     449                 :                                                  PRInt32 curSelfProgress, 
     450                 :                                                  PRInt32 maxSelfProgress, 
     451                 :                                                  PRInt32 curTotalProgress, 
     452                 :                                                  PRInt32 maxTotalProgress)
     453                 : {
     454               0 :   return NS_OK;
     455                 : }
     456                 : 
     457                 : NS_IMETHODIMP
     458               0 : nsHTMLDNSPrefetch::nsDeferrals::OnLocationChange(nsIWebProgress* aWebProgress,
     459                 :                                                  nsIRequest* aRequest,
     460                 :                                                  nsIURI *location,
     461                 :                                                  PRUint32 aFlags)
     462                 : {
     463               0 :   return NS_OK;
     464                 : }
     465                 : 
     466                 : NS_IMETHODIMP 
     467               0 : nsHTMLDNSPrefetch::nsDeferrals::OnStatusChange(nsIWebProgress* aWebProgress,
     468                 :                                                nsIRequest* aRequest,
     469                 :                                                nsresult aStatus,
     470                 :                                                const PRUnichar* aMessage)
     471                 : {
     472               0 :   return NS_OK;
     473                 : }
     474                 : 
     475                 : NS_IMETHODIMP 
     476               0 : nsHTMLDNSPrefetch::nsDeferrals::OnSecurityChange(nsIWebProgress *aWebProgress, 
     477                 :                                                  nsIRequest *aRequest, 
     478                 :                                                  PRUint32 state)
     479                 : {
     480               0 :   return NS_OK;
     481                 : }
     482                 : 
     483                 : //////////// nsIObserver method
     484                 : 
     485                 : NS_IMETHODIMP
     486            1404 : nsHTMLDNSPrefetch::nsDeferrals::Observe(nsISupports *subject,
     487                 :                                         const char *topic,
     488                 :                                         const PRUnichar *data)
     489                 : {
     490            1404 :   if (!strcmp(topic, "xpcom-shutdown"))
     491            1404 :     Flush();
     492                 :   
     493            1404 :   return NS_OK;
     494                 : }

Generated by: LCOV version 1.7