LCOV - code coverage report
Current view: directory - toolkit/components/places - History.cpp (source / functions) Found Hit Coverage
Test: app.info Lines: 897 835 93.1 %
Date: 2012-06-02 Functions: 97 83 85.6 %

       1                 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
       2                 :  * vim: sw=2 ts=2 et lcs=trail\:.,tab\:>~ :
       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 Places code.
      17                 :  *
      18                 :  * The Initial Developer of the Original Code is
      19                 :  * the 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                 :  *   Allison Naaktgeboren <ally@mozilla.com>
      26                 :  *
      27                 :  * Alternatively, the contents of this file may be used under the terms of
      28                 :  * either the GNU General Public License Version 2 or later (the "GPL"), or
      29                 :  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
      30                 :  * in which case the provisions of the GPL or the LGPL are applicable instead
      31                 :  * of those above. If you wish to allow use of your version of this file only
      32                 :  * under the terms of either the GPL or the LGPL, and not to allow others to
      33                 :  * use your version of this file under the terms of the MPL, indicate your
      34                 :  * decision by deleting the provisions above and replace them with the notice
      35                 :  * and other provisions required by the GPL or the LGPL. If you do not delete
      36                 :  * the provisions above, a recipient may use your version of this file under
      37                 :  * the terms of any one of the MPL, the GPL or the LGPL.
      38                 :  *
      39                 :  * ***** END LICENSE BLOCK ***** */
      40                 : 
      41                 : #include "mozilla/dom/ContentChild.h"
      42                 : #include "mozilla/dom/ContentParent.h"
      43                 : #include "nsXULAppAPI.h"
      44                 : 
      45                 : #include "History.h"
      46                 : #include "nsNavHistory.h"
      47                 : #include "nsNavBookmarks.h"
      48                 : #include "nsAnnotationService.h"
      49                 : #include "Helpers.h"
      50                 : #include "PlaceInfo.h"
      51                 : #include "VisitInfo.h"
      52                 : 
      53                 : #include "mozilla/storage.h"
      54                 : #include "mozilla/dom/Link.h"
      55                 : #include "nsDocShellCID.h"
      56                 : #include "mozilla/Services.h"
      57                 : #include "nsThreadUtils.h"
      58                 : #include "nsNetUtil.h"
      59                 : #include "nsIXPConnect.h"
      60                 : #include "mozilla/unused.h"
      61                 : #include "mozilla/Util.h"
      62                 : #include "nsContentUtils.h"
      63                 : #include "nsIMemoryReporter.h"
      64                 : 
      65                 : // Initial size for the cache holding visited status observers.
      66                 : #define VISIT_OBSERVERS_INITIAL_CACHE_SIZE 128
      67                 : 
      68                 : using namespace mozilla::dom;
      69                 : using mozilla::unused;
      70                 : 
      71                 : namespace mozilla {
      72                 : namespace places {
      73                 : 
      74                 : ////////////////////////////////////////////////////////////////////////////////
      75                 : //// Global Defines
      76                 : 
      77                 : #define URI_VISITED "visited"
      78                 : #define URI_NOT_VISITED "not visited"
      79                 : #define URI_VISITED_RESOLUTION_TOPIC "visited-status-resolution"
      80                 : // Observer event fired after a visit has been registered in the DB.
      81                 : #define URI_VISIT_SAVED "uri-visit-saved"
      82                 : 
      83                 : #define DESTINATIONFILEURI_ANNO \
      84                 :         NS_LITERAL_CSTRING("downloads/destinationFileURI")
      85                 : #define DESTINATIONFILENAME_ANNO \
      86                 :         NS_LITERAL_CSTRING("downloads/destinationFileName")
      87                 : 
      88                 : ////////////////////////////////////////////////////////////////////////////////
      89                 : //// VisitData
      90                 : 
      91            4865 : struct VisitData {
      92             500 :   VisitData()
      93                 :   : placeId(0)
      94                 :   , visitId(0)
      95                 :   , sessionId(0)
      96                 :   , hidden(true)
      97                 :   , typed(false)
      98                 :   , transitionType(PR_UINT32_MAX)
      99                 :   , visitTime(0)
     100             500 :   , titleChanged(false)
     101                 :   {
     102             500 :     guid.SetIsVoid(true);
     103             500 :     title.SetIsVoid(true);
     104             500 :   }
     105                 : 
     106             581 :   VisitData(nsIURI* aURI,
     107                 :             nsIURI* aReferrer = NULL)
     108                 :   : placeId(0)
     109                 :   , visitId(0)
     110                 :   , sessionId(0)
     111                 :   , hidden(true)
     112                 :   , typed(false)
     113                 :   , transitionType(PR_UINT32_MAX)
     114                 :   , visitTime(0)
     115             581 :   , titleChanged(false)
     116                 :   {
     117             581 :     (void)aURI->GetSpec(spec);
     118             581 :     (void)GetReversedHostname(aURI, revHost);
     119             581 :     if (aReferrer) {
     120              12 :       (void)aReferrer->GetSpec(referrerSpec);
     121                 :     }
     122             581 :     guid.SetIsVoid(true);
     123             581 :     title.SetIsVoid(true);
     124             581 :   }
     125                 : 
     126                 :   /**
     127                 :    * Sets the transition type of the visit, as well as if it was typed and
     128                 :    * should be hidden (based on the transition type specified).
     129                 :    *
     130                 :    * @param aTransitionType
     131                 :    *        The transition type constant to set.  Must be one of the
     132                 :    *        TRANSITION_ constants on nsINavHistoryService.
     133                 :    */
     134             484 :   void SetTransitionType(PRUint32 aTransitionType)
     135                 :   {
     136             484 :     typed = aTransitionType == nsINavHistoryService::TRANSITION_TYPED;
     137                 :     bool redirected =
     138                 :       aTransitionType == nsINavHistoryService::TRANSITION_REDIRECT_TEMPORARY ||
     139             484 :       aTransitionType == nsINavHistoryService::TRANSITION_REDIRECT_PERMANENT;
     140             484 :     hidden = GetHiddenState(redirected, aTransitionType);
     141             484 :     transitionType = aTransitionType;
     142             484 :   }
     143                 : 
     144                 :   /**
     145                 :    * Determines if this refers to the same url as aOther, and updates aOther
     146                 :    * with missing information if so.
     147                 :    *
     148                 :    * @param aOther
     149                 :    *        The other place to check against.
     150                 :    * @return true if this is a visit for the same place as aOther, false
     151                 :    *         otherwise.
     152                 :    */
     153             353 :   bool IsSamePlaceAs(VisitData& aOther)
     154                 :   {
     155             353 :     if (!spec.Equals(aOther.spec)) {
     156             346 :       return false;
     157                 :     }
     158                 : 
     159               7 :     aOther.placeId = placeId;
     160               7 :     aOther.guid = guid;
     161               7 :     return true;
     162                 :   }
     163                 : 
     164                 :   PRInt64 placeId;
     165                 :   nsCString guid;
     166                 :   PRInt64 visitId;
     167                 :   PRInt64 sessionId;
     168                 :   nsCString spec;
     169                 :   nsString revHost;
     170                 :   bool hidden;
     171                 :   bool typed;
     172                 :   PRUint32 transitionType;
     173                 :   PRTime visitTime;
     174                 : 
     175                 :   /**
     176                 :    * Stores the title.  If this is empty (IsEmpty() returns true), then the
     177                 :    * title should be removed from the Place.  If the title is void (IsVoid()
     178                 :    * returns true), then no title has been set on this object, and titleChanged
     179                 :    * should remain false.
     180                 :    */
     181                 :   nsString title;
     182                 : 
     183                 :   nsCString referrerSpec;
     184                 : 
     185                 :   // TODO bug 626836 hook up hidden and typed change tracking too!
     186                 :   bool titleChanged;
     187                 : };
     188                 : 
     189                 : ////////////////////////////////////////////////////////////////////////////////
     190                 : //// Anonymous Helpers
     191                 : 
     192                 : namespace {
     193                 : 
     194                 : /**
     195                 :  * Obtains an nsIURI from the "uri" property of a JSObject.
     196                 :  *
     197                 :  * @param aCtx
     198                 :  *        The JSContext for aObject.
     199                 :  * @param aObject
     200                 :  *        The JSObject to get the URI from.
     201                 :  * @param aProperty
     202                 :  *        The name of the property to get the URI from.
     203                 :  * @return the URI if it exists.
     204                 :  */
     205                 : already_AddRefed<nsIURI>
     206             997 : GetURIFromJSObject(JSContext* aCtx,
     207                 :                    JSObject* aObject,
     208                 :                    const char* aProperty)
     209                 : {
     210                 :   jsval uriVal;
     211             997 :   JSBool rc = JS_GetProperty(aCtx, aObject, aProperty, &uriVal);
     212             997 :   NS_ENSURE_TRUE(rc, nsnull);
     213                 : 
     214             997 :   if (!JSVAL_IS_PRIMITIVE(uriVal)) {
     215            1100 :     nsCOMPtr<nsIXPConnect> xpc = mozilla::services::GetXPConnect();
     216                 : 
     217            1100 :     nsCOMPtr<nsIXPConnectWrappedNative> wrappedObj;
     218             550 :     nsresult rv = xpc->GetWrappedNativeOfJSObject(aCtx, JSVAL_TO_OBJECT(uriVal),
     219             550 :                                                   getter_AddRefs(wrappedObj));
     220             550 :     NS_ENSURE_SUCCESS(rv, nsnull);
     221            1096 :     nsCOMPtr<nsIURI> uri = do_QueryWrappedNative(wrappedObj);
     222             548 :     return uri.forget();
     223                 :   }
     224             447 :   return nsnull;
     225                 : }
     226                 : 
     227                 : /**
     228                 :  * Obtains the specified property of a JSObject.
     229                 :  *
     230                 :  * @param aCtx
     231                 :  *        The JSContext for aObject.
     232                 :  * @param aObject
     233                 :  *        The JSObject to get the string from.
     234                 :  * @param aProperty
     235                 :  *        The property to get the value from.
     236                 :  * @param _string
     237                 :  *        The string to populate with the value, or set it to void.
     238                 :  */
     239                 : void
     240            1006 : GetStringFromJSObject(JSContext* aCtx,
     241                 :                       JSObject* aObject,
     242                 :                       const char* aProperty,
     243                 :                       nsString& _string)
     244                 : {
     245                 :   jsval val;
     246            1006 :   JSBool rc = JS_GetProperty(aCtx, aObject, aProperty, &val);
     247            2012 :   if (!rc || JSVAL_IS_VOID(val) ||
     248            1276 :       !(JSVAL_IS_NULL(val) || JSVAL_IS_STRING(val))) {
     249             367 :     _string.SetIsVoid(true);
     250             367 :     return;
     251                 :   }
     252                 :   // |null| in JS maps to the empty string.
     253             639 :   if (JSVAL_IS_NULL(val)) {
     254               2 :     _string.Truncate();
     255               2 :     return;
     256                 :   }
     257                 :   size_t length;
     258                 :   const jschar* chars =
     259             637 :     JS_GetStringCharsZAndLength(aCtx, JSVAL_TO_STRING(val), &length);
     260             637 :   if (!chars) {
     261               0 :     _string.SetIsVoid(true);
     262               0 :     return;
     263                 :   }
     264             637 :   _string.Assign(static_cast<const PRUnichar*>(chars), length);
     265                 : }
     266                 : 
     267                 : /**
     268                 :  * Obtains the specified property of a JSObject.
     269                 :  *
     270                 :  * @param aCtx
     271                 :  *        The JSContext for aObject.
     272                 :  * @param aObject
     273                 :  *        The JSObject to get the int from.
     274                 :  * @param aProperty
     275                 :  *        The property to get the value from.
     276                 :  * @param _int
     277                 :  *        The integer to populate with the value on success.
     278                 :  */
     279                 : template <typename IntType>
     280                 : nsresult
     281            1349 : GetIntFromJSObject(JSContext* aCtx,
     282                 :                    JSObject* aObject,
     283                 :                    const char* aProperty,
     284                 :                    IntType* _int)
     285                 : {
     286                 :   jsval value;
     287            1349 :   JSBool rc = JS_GetProperty(aCtx, aObject, aProperty, &value);
     288            1349 :   NS_ENSURE_TRUE(rc, NS_ERROR_UNEXPECTED);
     289            1349 :   if (JSVAL_IS_VOID(value)) {
     290             441 :     return NS_ERROR_INVALID_ARG;
     291                 :   }
     292             908 :   NS_ENSURE_ARG(JSVAL_IS_PRIMITIVE(value));
     293             908 :   NS_ENSURE_ARG(JSVAL_IS_NUMBER(value));
     294                 : 
     295                 :   double num;
     296             908 :   rc = JS_ValueToNumber(aCtx, value, &num);
     297             908 :   NS_ENSURE_TRUE(rc, NS_ERROR_UNEXPECTED);
     298             908 :   NS_ENSURE_ARG(IntType(num) == num);
     299                 : 
     300             908 :   *_int = IntType(num);
     301             908 :   return NS_OK;
     302                 : }
     303                 : 
     304                 : /**
     305                 :  * Obtains the specified property of a JSObject.
     306                 :  *
     307                 :  * @pre aArray must be an Array object.
     308                 :  *
     309                 :  * @param aCtx
     310                 :  *        The JSContext for aArray.
     311                 :  * @param aArray
     312                 :  *        The JSObject to get the object from.
     313                 :  * @param aIndex
     314                 :  *        The index to get the object from.
     315                 :  * @param _object
     316                 :  *        The JSObject pointer on success.
     317                 :  */
     318                 : nsresult
     319            1009 : GetJSObjectFromArray(JSContext* aCtx,
     320                 :                      JSObject* aArray,
     321                 :                      uint32_t aIndex,
     322                 :                      JSObject** _rooter)
     323                 : {
     324            1009 :   NS_PRECONDITION(JS_IsArrayObject(aCtx, aArray),
     325                 :                   "Must provide an object that is an array!");
     326                 : 
     327                 :   jsval value;
     328            1009 :   JSBool rc = JS_GetElement(aCtx, aArray, aIndex, &value);
     329            1009 :   NS_ENSURE_TRUE(rc, NS_ERROR_UNEXPECTED);
     330            1009 :   NS_ENSURE_ARG(!JSVAL_IS_PRIMITIVE(value));
     331            1009 :   *_rooter = JSVAL_TO_OBJECT(value);
     332            1009 :   return NS_OK;
     333                 : }
     334                 : 
     335                 : class VisitedQuery : public AsyncStatementCallback
     336            1384 : {
     337                 : public:
     338             346 :   static nsresult Start(nsIURI* aURI,
     339                 :                         mozIVisitedStatusCallback* aCallback=nsnull)
     340                 :   {
     341             346 :     NS_PRECONDITION(aURI, "Null URI");
     342                 : 
     343                 :   // If we are a content process, always remote the request to the
     344                 :   // parent process.
     345             346 :   if (XRE_GetProcessType() == GeckoProcessType_Content) {
     346                 :     mozilla::dom::ContentChild* cpc =
     347               0 :       mozilla::dom::ContentChild::GetSingleton();
     348               0 :     NS_ASSERTION(cpc, "Content Protocol is NULL!");
     349               0 :     (void)cpc->SendStartVisitedQuery(aURI);
     350               0 :     return NS_OK;
     351                 :   }
     352                 : 
     353             346 :     nsNavHistory* navHistory = nsNavHistory::GetHistoryService();
     354             346 :     NS_ENSURE_STATE(navHistory);
     355             346 :     if (navHistory->hasEmbedVisit(aURI)) {
     356               8 :       nsRefPtr<VisitedQuery> callback = new VisitedQuery(aURI, aCallback, true);
     357               4 :       NS_ENSURE_TRUE(callback, NS_ERROR_OUT_OF_MEMORY);
     358                 :       // As per IHistory contract, we must notify asynchronously.
     359                 :       nsCOMPtr<nsIRunnable> event =
     360               8 :         NS_NewRunnableMethod(callback, &VisitedQuery::NotifyVisitedStatus);
     361               4 :       NS_DispatchToMainThread(event);
     362                 : 
     363               4 :       return NS_OK;
     364                 :     }
     365                 : 
     366             342 :     History* history = History::GetService();
     367             342 :     NS_ENSURE_STATE(history);
     368             342 :     mozIStorageAsyncStatement* stmt = history->GetIsVisitedStatement();
     369             342 :     NS_ENSURE_STATE(stmt);
     370                 : 
     371                 :     // Bind by index for performance.
     372             342 :     nsresult rv = URIBinder::Bind(stmt, 0, aURI);
     373             342 :     NS_ENSURE_SUCCESS(rv, rv);
     374                 : 
     375             684 :     nsRefPtr<VisitedQuery> callback = new VisitedQuery(aURI, aCallback);
     376             342 :     NS_ENSURE_TRUE(callback, NS_ERROR_OUT_OF_MEMORY);
     377                 : 
     378             684 :     nsCOMPtr<mozIStoragePendingStatement> handle;
     379             342 :     return stmt->ExecuteAsync(callback, getter_AddRefs(handle));
     380                 :   }
     381                 : 
     382              32 :   NS_IMETHOD HandleResult(mozIStorageResultSet* aResults)
     383                 :   {
     384                 :     // If this method is called, we've gotten results, which means we have a
     385                 :     // visit.
     386              32 :     mIsVisited = true;
     387              32 :     return NS_OK;
     388                 :   }
     389                 : 
     390               0 :   NS_IMETHOD HandleError(mozIStorageError* aError)
     391                 :   {
     392                 :     // mIsVisited is already set to false, and that's the assumption we will
     393                 :     // make if an error occurred.
     394               0 :     return NS_OK;
     395                 :   }
     396                 : 
     397             342 :   NS_IMETHOD HandleCompletion(PRUint16 aReason)
     398                 :   {
     399             342 :     if (aReason != mozIStorageStatementCallback::REASON_FINISHED) {
     400               0 :       return NS_OK;
     401                 :     }
     402                 : 
     403             342 :     nsresult rv = NotifyVisitedStatus();
     404             342 :     NS_ENSURE_SUCCESS(rv, rv);
     405             342 :     return NS_OK;
     406                 :   }
     407                 : 
     408             346 :   nsresult NotifyVisitedStatus()
     409                 :   {
     410                 :     // If an external handling callback is provided, just notify through it.
     411             346 :     if (mCallback) {
     412             336 :       mCallback->IsVisited(mURI, mIsVisited);
     413             336 :       return NS_OK;
     414                 :     }
     415                 : 
     416              10 :     if (mIsVisited) {
     417               4 :       History* history = History::GetService();
     418               4 :       NS_ENSURE_STATE(history);
     419               4 :       history->NotifyVisited(mURI);
     420                 :     }
     421                 : 
     422                 :     nsCOMPtr<nsIObserverService> observerService =
     423              20 :       mozilla::services::GetObserverService();
     424              10 :     if (observerService) {
     425              20 :       nsAutoString status;
     426              10 :       if (mIsVisited) {
     427               4 :         status.AssignLiteral(URI_VISITED);
     428                 :       }
     429                 :       else {
     430               6 :         status.AssignLiteral(URI_NOT_VISITED);
     431                 :       }
     432              10 :       (void)observerService->NotifyObservers(mURI,
     433                 :                                              URI_VISITED_RESOLUTION_TOPIC,
     434              10 :                                              status.get());
     435                 :     }
     436                 : 
     437              10 :     return NS_OK;
     438                 :   }
     439                 : 
     440                 : private:
     441             346 :   VisitedQuery(nsIURI* aURI,
     442                 :                mozIVisitedStatusCallback *aCallback=nsnull,
     443                 :                bool aIsVisited=false)
     444                 :   : mURI(aURI)
     445                 :   , mCallback(aCallback)
     446             346 :   , mIsVisited(aIsVisited)
     447                 :   {
     448             346 :   }
     449                 : 
     450                 :   nsCOMPtr<nsIURI> mURI;
     451                 :   nsCOMPtr<mozIVisitedStatusCallback> mCallback;
     452                 :   bool mIsVisited;
     453                 : };
     454                 : 
     455                 : /**
     456                 :  * Notifies observers about a visit.
     457                 :  */
     458                 : class NotifyVisitObservers : public nsRunnable
     459            1932 : {
     460                 : public:
     461             483 :   NotifyVisitObservers(VisitData& aPlace,
     462                 :                        VisitData& aReferrer)
     463                 :   : mPlace(aPlace)
     464                 :   , mReferrer(aReferrer)
     465             483 :   , mHistory(History::GetService())
     466                 :   {
     467             483 :   }
     468                 : 
     469             483 :   NS_IMETHOD Run()
     470                 :   {
     471             483 :     NS_PRECONDITION(NS_IsMainThread(),
     472                 :                     "This should be called on the main thread");
     473                 :     // We are in the main thread, no need to lock.
     474             483 :     if (mHistory->IsShuttingDown()) {
     475                 :       // If we are shutting down, we cannot notify the observers.
     476               0 :       return NS_OK;
     477                 :     }
     478                 : 
     479             483 :     nsNavHistory* navHistory = nsNavHistory::GetHistoryService();
     480             483 :     if (!navHistory) {
     481               0 :       NS_WARNING("Trying to notify about a visit but cannot get the history service!");
     482               0 :       return NS_OK;
     483                 :     }
     484                 : 
     485             966 :     nsCOMPtr<nsIURI> uri;
     486             483 :     (void)NS_NewURI(getter_AddRefs(uri), mPlace.spec);
     487                 : 
     488                 :     // Notify nsNavHistory observers of visit, but only for certain types of
     489                 :     // visits to maintain consistency with nsNavHistory::GetQueryResults.
     490             483 :     if (!mPlace.hidden &&
     491                 :         mPlace.transitionType != nsINavHistoryService::TRANSITION_EMBED &&
     492                 :         mPlace.transitionType != nsINavHistoryService::TRANSITION_FRAMED_LINK) {
     493                 :       navHistory->NotifyOnVisit(uri, mPlace.visitId, mPlace.visitTime,
     494                 :                                 mPlace.sessionId, mReferrer.visitId,
     495             455 :                                 mPlace.transitionType, mPlace.guid);
     496                 :     }
     497                 : 
     498                 :     nsCOMPtr<nsIObserverService> obsService =
     499             966 :       mozilla::services::GetObserverService();
     500             483 :     if (obsService) {
     501                 :       DebugOnly<nsresult> rv =
     502             966 :         obsService->NotifyObservers(uri, URI_VISIT_SAVED, nsnull);
     503             483 :       NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "Could not notify observers");
     504                 :     }
     505                 : 
     506             483 :     History* history = History::GetService();
     507             483 :     NS_ENSURE_STATE(history);
     508             483 :     history->NotifyVisited(uri);
     509                 : 
     510             483 :     return NS_OK;
     511                 :   }
     512                 : private:
     513                 :   VisitData mPlace;
     514                 :   VisitData mReferrer;
     515                 :   nsRefPtr<History> mHistory;
     516                 : };
     517                 : 
     518                 : /**
     519                 :  * Notifies observers about a pages title changing.
     520                 :  */
     521                 : class NotifyTitleObservers : public nsRunnable
     522            1600 : {
     523                 : public:
     524                 :   /**
     525                 :    * Notifies observers on the main thread.
     526                 :    *
     527                 :    * @param aSpec
     528                 :    *        The spec of the URI to notify about.
     529                 :    * @param aTitle
     530                 :    *        The new title to notify about.
     531                 :    */
     532             400 :   NotifyTitleObservers(const nsCString& aSpec,
     533                 :                        const nsString& aTitle,
     534                 :                        const nsCString& aGUID)
     535                 :   : mSpec(aSpec)
     536                 :   , mTitle(aTitle)
     537             400 :   , mGUID(aGUID)
     538                 :   {
     539             400 :   }
     540                 : 
     541             400 :   NS_IMETHOD Run()
     542                 :   {
     543             400 :     NS_PRECONDITION(NS_IsMainThread(),
     544                 :                     "This should be called on the main thread");
     545                 : 
     546             400 :     nsNavHistory* navHistory = nsNavHistory::GetHistoryService();
     547             400 :     NS_ENSURE_TRUE(navHistory, NS_ERROR_OUT_OF_MEMORY);
     548             800 :     nsCOMPtr<nsIURI> uri;
     549             400 :     (void)NS_NewURI(getter_AddRefs(uri), mSpec);
     550             400 :     navHistory->NotifyTitleChange(uri, mTitle, mGUID);
     551                 : 
     552             400 :     return NS_OK;
     553                 :   }
     554                 : private:
     555                 :   const nsCString mSpec;
     556                 :   const nsString mTitle;
     557                 :   const nsCString mGUID;
     558                 : };
     559                 : 
     560                 : /**
     561                 :  * Notifies a callback object when a visit has been handled.
     562                 :  */
     563                 : class NotifyVisitInfoCallback : public nsRunnable
     564            1752 : {
     565                 : public:
     566             438 :   NotifyVisitInfoCallback(mozIVisitInfoCallback* aCallback,
     567                 :                           const VisitData& aPlace,
     568                 :                           nsresult aResult)
     569                 :   : mCallback(aCallback)
     570                 :   , mPlace(aPlace)
     571             438 :   , mResult(aResult)
     572                 :   {
     573             438 :     NS_PRECONDITION(aCallback, "Must pass a non-null callback!");
     574             438 :   }
     575                 : 
     576             438 :   NS_IMETHOD Run()
     577                 :   {
     578             438 :     NS_PRECONDITION(NS_IsMainThread(),
     579                 :                     "This should be called on the main thread");
     580                 : 
     581             876 :     nsCOMPtr<nsIURI> referrerURI;
     582             438 :     if (!mPlace.referrerSpec.IsEmpty()) {
     583               2 :       (void)NS_NewURI(getter_AddRefs(referrerURI), mPlace.referrerSpec);
     584                 :     }
     585                 : 
     586                 :     nsCOMPtr<mozIVisitInfo> visit =
     587                 :       new VisitInfo(mPlace.visitId, mPlace.visitTime, mPlace.transitionType,
     588            1314 :                     referrerURI.forget(), mPlace.sessionId);
     589             876 :     PlaceInfo::VisitsArray visits;
     590             438 :     (void)visits.AppendElement(visit);
     591                 : 
     592             876 :     nsCOMPtr<nsIURI> uri;
     593             438 :     (void)NS_NewURI(getter_AddRefs(uri), mPlace.spec);
     594                 : 
     595                 :     // We do not notify about the frecency of the place.
     596                 :     nsCOMPtr<mozIPlaceInfo> place =
     597                 :       new PlaceInfo(mPlace.placeId, mPlace.guid, uri.forget(), mPlace.title,
     598            1314 :                     -1, visits);
     599             438 :     if (NS_SUCCEEDED(mResult)) {
     600             344 :       (void)mCallback->HandleResult(place);
     601                 :     }
     602                 :     else {
     603              94 :       (void)mCallback->HandleError(mResult, place);
     604                 :     }
     605                 : 
     606             438 :     return NS_OK;
     607                 :   }
     608                 : 
     609                 : private:
     610                 :   /**
     611                 :    * Callers MUST hold a strong reference to this that outlives us because we
     612                 :    * may be created off of the main thread, and therefore cannot call AddRef on
     613                 :    * this object (and therefore cannot hold a strong reference to it).
     614                 :    */
     615                 :   mozIVisitInfoCallback* mCallback;
     616                 :   VisitData mPlace;
     617                 :   const nsresult mResult;
     618                 : };
     619                 : 
     620                 : /**
     621                 :  * Notifies a callback object when the operation is complete.
     622                 :  */
     623                 : class NotifyCompletion : public nsRunnable
     624             616 : {
     625                 : public:
     626             154 :   NotifyCompletion(mozIVisitInfoCallback* aCallback)
     627             154 :   : mCallback(aCallback)
     628                 :   {
     629             154 :     NS_PRECONDITION(aCallback, "Must pass a non-null callback!");
     630             154 :   }
     631                 : 
     632             308 :   NS_IMETHOD Run()
     633                 :   {
     634             308 :     if (NS_IsMainThread()) {
     635             154 :       (void)mCallback->HandleCompletion();
     636                 :     }
     637                 :     else {
     638             154 :       (void)NS_DispatchToMainThread(this);
     639                 : 
     640                 :       // Also dispatch an event to release the reference to the callback after
     641                 :       // we have run.
     642             308 :       nsCOMPtr<nsIThread> mainThread = do_GetMainThread();
     643             154 :       (void)NS_ProxyRelease(mainThread, mCallback, true);
     644                 :     }
     645             308 :     return NS_OK;
     646                 :   }
     647                 : 
     648                 : private:
     649                 :   /**
     650                 :    * Callers MUST hold a strong reference to this because we may be created
     651                 :    * off of the main thread, and therefore cannot call AddRef on this object
     652                 :    * (and therefore cannot hold a strong reference to it). If invoked from a
     653                 :    * background thread, NotifyCompletion will release the reference to this.
     654                 :    */
     655                 :   mozIVisitInfoCallback* mCallback;
     656                 : };
     657                 : 
     658                 : /**
     659                 :  * Checks to see if we can add aURI to history, and dispatches an error to
     660                 :  * aCallback (if provided) if we cannot.
     661                 :  *
     662                 :  * @param aURI
     663                 :  *        The URI to check.
     664                 :  * @param [optional] aGUID
     665                 :  *        The guid of the URI to check.  This is passed back to the callback.
     666                 :  * @param [optional] aCallback
     667                 :  *        The callback to notify if the URI cannot be added to history.
     668                 :  * @return true if the URI can be added to history, false otherwise.
     669                 :  */
     670                 : bool
     671            1018 : CanAddURI(nsIURI* aURI,
     672             474 :           const nsCString& aGUID = EmptyCString(),
     673                 :           mozIVisitInfoCallback* aCallback = NULL)
     674                 : {
     675            1018 :   nsNavHistory* navHistory = nsNavHistory::GetHistoryService();
     676            1018 :   NS_ENSURE_TRUE(navHistory, false);
     677                 : 
     678                 :   bool canAdd;
     679            1018 :   nsresult rv = navHistory->CanAddURI(aURI, &canAdd);
     680            1018 :   if (NS_SUCCEEDED(rv) && canAdd) {
     681             925 :     return true;
     682                 :   };
     683                 : 
     684                 :   // We cannot add the URI.  Notify the callback, if we were given one.
     685              93 :   if (aCallback) {
     686                 :     // NotifyVisitInfoCallback does not hold a strong reference to the callback, so we
     687                 :     // have to manage it by AddRefing now and then releasing it after the event
     688                 :     // has run.
     689              93 :     NS_ADDREF(aCallback);
     690                 : 
     691             186 :     VisitData place(aURI);
     692              93 :     place.guid = aGUID;
     693                 :     nsCOMPtr<nsIRunnable> event =
     694             186 :       new NotifyVisitInfoCallback(aCallback, place, NS_ERROR_INVALID_ARG);
     695              93 :     (void)NS_DispatchToMainThread(event);
     696                 : 
     697                 :     // Also dispatch an event to release our reference to the callback after
     698                 :     // NotifyVisitInfoCallback has run.
     699             186 :     nsCOMPtr<nsIThread> mainThread = do_GetMainThread();
     700              93 :     (void)NS_ProxyRelease(mainThread, aCallback, true);
     701                 :   }
     702                 : 
     703              93 :   return false;
     704                 : }
     705                 : 
     706                 : /**
     707                 :  * Adds a visit to the database.
     708                 :  */
     709                 : class InsertVisitedURIs : public nsRunnable
     710                 : {
     711                 : public:
     712                 :   /**
     713                 :    * Adds a visit to the database asynchronously.
     714                 :    *
     715                 :    * @param aConnection
     716                 :    *        The database connection to use for these operations.
     717                 :    * @param aPlaces
     718                 :    *        The locations to record visits.
     719                 :    * @param [optional] aCallback
     720                 :    *        The callback to notify about the visit.
     721                 :    */
     722             121 :   static nsresult Start(mozIStorageConnection* aConnection,
     723                 :                         nsTArray<VisitData>& aPlaces,
     724                 :                         mozIVisitInfoCallback* aCallback = NULL)
     725                 :   {
     726             121 :     NS_PRECONDITION(NS_IsMainThread(),
     727                 :                     "This should be called on the main thread");
     728             121 :     NS_PRECONDITION(aPlaces.Length() > 0, "Must pass a non-empty array!");
     729                 : 
     730                 :     nsRefPtr<InsertVisitedURIs> event =
     731             242 :       new InsertVisitedURIs(aConnection, aPlaces, aCallback);
     732                 : 
     733                 :     // Get the target thread, and then start the work!
     734             242 :     nsCOMPtr<nsIEventTarget> target = do_GetInterface(aConnection);
     735             121 :     NS_ENSURE_TRUE(target, NS_ERROR_UNEXPECTED);
     736             121 :     nsresult rv = target->Dispatch(event, NS_DISPATCH_NORMAL);
     737             121 :     NS_ENSURE_SUCCESS(rv, rv);
     738                 : 
     739             121 :     return NS_OK;
     740                 :   }
     741                 : 
     742             121 :   NS_IMETHOD Run()
     743                 :   {
     744             121 :     NS_PRECONDITION(!NS_IsMainThread(),
     745                 :                     "This should not be called on the main thread");
     746                 : 
     747                 :     // Prevent the main thread from shutting down while this is running.
     748             242 :     MutexAutoLock lockedScope(mHistory->GetShutdownMutex());
     749             121 :     if(mHistory->IsShuttingDown()) {
     750                 :       // If we were already shutting down, we cannot insert the URIs.
     751               0 :       return NS_OK;
     752                 :     }
     753                 : 
     754                 :     mozStorageTransaction transaction(mDBConn, false,
     755             242 :                                       mozIStorageConnection::TRANSACTION_IMMEDIATE);
     756                 : 
     757             121 :     VisitData* lastPlace = NULL;
     758             594 :     for (nsTArray<VisitData>::size_type i = 0; i < mPlaces.Length(); i++) {
     759             474 :       VisitData& place = mPlaces.ElementAt(i);
     760             474 :       VisitData& referrer = mReferrers.ElementAt(i);
     761                 : 
     762                 :       // We can avoid a database lookup if it's the same place as the last
     763                 :       // visit we added.
     764             353 :       bool known = (lastPlace && lastPlace->IsSamePlaceAs(place)) ||
     765             827 :                    mHistory->FetchPageInfo(place);
     766                 : 
     767             474 :       FetchReferrerInfo(referrer, place);
     768                 : 
     769             474 :       nsresult rv = DoDatabaseInserts(known, place, referrer);
     770             474 :       if (mCallback) {
     771                 :         nsCOMPtr<nsIRunnable> event =
     772             674 :           new NotifyVisitInfoCallback(mCallback, place, rv);
     773             337 :         nsresult rv2 = NS_DispatchToMainThread(event);
     774             337 :         NS_ENSURE_SUCCESS(rv2, rv2);
     775                 :       }
     776             474 :       NS_ENSURE_SUCCESS(rv, rv);
     777                 : 
     778             946 :       nsCOMPtr<nsIRunnable> event = new NotifyVisitObservers(place, referrer);
     779             473 :       rv = NS_DispatchToMainThread(event);
     780             473 :       NS_ENSURE_SUCCESS(rv, rv);
     781                 : 
     782                 :       // Notify about title change if needed.
     783             473 :       if ((!known && !place.title.IsVoid()) || place.titleChanged) {
     784             384 :         event = new NotifyTitleObservers(place.spec, place.title, place.guid);
     785             384 :         rv = NS_DispatchToMainThread(event);
     786             384 :         NS_ENSURE_SUCCESS(rv, rv);
     787                 :       }
     788                 : 
     789             946 :       lastPlace = &mPlaces.ElementAt(i);
     790                 :     }
     791                 : 
     792             120 :     nsresult rv = transaction.Commit();
     793             120 :     NS_ENSURE_SUCCESS(rv, rv);
     794                 : 
     795             120 :     return NS_OK;
     796                 :   }
     797                 : private:
     798             121 :   InsertVisitedURIs(mozIStorageConnection* aConnection,
     799                 :                     nsTArray<VisitData>& aPlaces,
     800                 :                     mozIVisitInfoCallback* aCallback)
     801                 :   : mDBConn(aConnection)
     802                 :   , mCallback(aCallback)
     803             121 :   , mHistory(History::GetService())
     804                 :   {
     805             121 :     NS_PRECONDITION(NS_IsMainThread(),
     806                 :                     "This should be called on the main thread");
     807                 : 
     808             121 :     (void)mPlaces.SwapElements(aPlaces);
     809             121 :     (void)mReferrers.SetLength(mPlaces.Length());
     810                 : 
     811             121 :     nsNavHistory* navHistory = nsNavHistory::GetHistoryService();
     812             121 :     NS_ABORT_IF_FALSE(navHistory, "Could not get nsNavHistory?!");
     813                 : 
     814             595 :     for (nsTArray<VisitData>::size_type i = 0; i < mPlaces.Length(); i++) {
     815             474 :       mReferrers[i].spec = mPlaces[i].referrerSpec;
     816                 : 
     817                 :       // If we are inserting a place into an empty mPlaces array, we need to
     818                 :       // check to make sure we do not store a bogus session id that is higher
     819                 :       // than the current maximum session id.
     820             474 :       if (i == 0) {
     821             121 :         PRInt64 newSessionId = navHistory->GetNewSessionID();
     822             121 :         if (mPlaces[0].sessionId > newSessionId) {
     823               1 :           mPlaces[0].sessionId = newSessionId;
     824                 :         }
     825                 :       }
     826                 : 
     827                 :       // Speculatively get a new session id for our visit if the current session
     828                 :       // id is non-valid or if it is larger than the current largest session id.
     829                 :       // While it is true that we will use the session id from the referrer if
     830                 :       // the visit was "recent" enough, we cannot call this method off of the
     831                 :       // main thread, so we have to consume an id now.
     832             474 :       if (mPlaces[i].sessionId <= 0 ||
     833               0 :           (i > 0 && mPlaces[i].sessionId >= mPlaces[0].sessionId)) {
     834             472 :         mPlaces[i].sessionId = navHistory->GetNewSessionID();
     835                 :       }
     836                 : 
     837                 : #ifdef DEBUG
     838             948 :       nsCOMPtr<nsIURI> uri;
     839             474 :       (void)NS_NewURI(getter_AddRefs(uri), mPlaces[i].spec);
     840             474 :       NS_ASSERTION(CanAddURI(uri),
     841                 :                    "Passed a VisitData with a URI we cannot add to history!");
     842                 : #endif
     843                 :     }
     844                 : 
     845                 :     // We AddRef on the main thread, and release it when we are destroyed.
     846             121 :     NS_IF_ADDREF(mCallback);
     847             121 :   }
     848                 : 
     849             242 :   virtual ~InsertVisitedURIs()
     850             242 :   {
     851             121 :     if (mCallback) {
     852             172 :       nsCOMPtr<nsIThread> mainThread = do_GetMainThread();
     853              86 :       (void)NS_ProxyRelease(mainThread, mCallback, true);
     854                 :     }
     855             484 :   }
     856                 : 
     857                 :   /**
     858                 :    * Inserts or updates the entry in moz_places for this visit, adds the visit,
     859                 :    * and updates the frecency of the place.
     860                 :    *
     861                 :    * @param aKnown
     862                 :    *        True if we already have an entry for this place in moz_places, false
     863                 :    *        otherwise.
     864                 :    * @param aPlace
     865                 :    *        The place we are adding a visit for.
     866                 :    * @param aReferrer
     867                 :    *        The referrer for aPlace.
     868                 :    */
     869             474 :   nsresult DoDatabaseInserts(bool aKnown,
     870                 :                              VisitData& aPlace,
     871                 :                              VisitData& aReferrer)
     872                 :   {
     873             474 :     NS_PRECONDITION(!NS_IsMainThread(),
     874                 :                     "This should not be called on the main thread");
     875                 : 
     876                 :     // If the page was in moz_places, we need to update the entry.
     877                 :     nsresult rv;
     878             474 :     if (aKnown) {
     879              18 :       rv = mHistory->UpdatePlace(aPlace);
     880              18 :       NS_ENSURE_SUCCESS(rv, rv);
     881                 :     }
     882                 :     // Otherwise, the page was not in moz_places, so now we have to add it.
     883                 :     else {
     884             456 :       rv = mHistory->InsertPlace(aPlace);
     885             456 :       NS_ENSURE_SUCCESS(rv, rv);
     886                 : 
     887                 :       // We need the place id and guid of the page we just inserted when we
     888                 :       // have a callback or when the GUID isn't known.  No point in doing the
     889                 :       // disk I/O if we do not need it.
     890             455 :       if (mCallback || aPlace.guid.IsEmpty()) {
     891             454 :         bool exists = mHistory->FetchPageInfo(aPlace);
     892             454 :         if (!exists) {
     893               0 :           NS_NOTREACHED("should have an entry in moz_places");
     894                 :         }
     895                 :       }
     896                 :     }
     897                 : 
     898             473 :     rv = AddVisit(aPlace, aReferrer);
     899             473 :     NS_ENSURE_SUCCESS(rv, rv);
     900                 : 
     901                 :     // TODO (bug 623969) we shouldn't update this after each visit, but
     902                 :     // rather only for each unique place to save disk I/O.
     903             473 :     rv = UpdateFrecency(aPlace);
     904             473 :     NS_ENSURE_SUCCESS(rv, rv);
     905                 : 
     906             473 :     return NS_OK;
     907                 :   }
     908                 : 
     909                 :   /**
     910                 :    * Loads visit information about the page into _place.
     911                 :    *
     912                 :    * @param _place
     913                 :    *        The VisitData for the place we need to know visit information about.
     914                 :    * @param [optional] aThresholdStart
     915                 :    *        The timestamp of a new visit (not represented by _place) used to
     916                 :    *        determine if the page was recently visited or not.
     917                 :    * @return true if the page was recently (determined with aThresholdStart)
     918                 :    *         visited, false otherwise.
     919                 :    */
     920             487 :   bool FetchVisitInfo(VisitData& _place,
     921                 :                       PRTime aThresholdStart = 0)
     922                 :   {
     923             487 :     NS_PRECONDITION(!_place.spec.IsEmpty(), "must have a non-empty spec!");
     924                 : 
     925             974 :     nsCOMPtr<mozIStorageStatement> stmt;
     926                 :     // If we have a visitTime, we want information on that specific visit.
     927             487 :     if (_place.visitTime) {
     928                 :       stmt = mHistory->GetStatement(
     929                 :         "SELECT id, session, visit_date "
     930                 :         "FROM moz_historyvisits "
     931                 :         "WHERE place_id = (SELECT id FROM moz_places WHERE url = :page_url) "
     932                 :         "AND visit_date = :visit_date "
     933             473 :       );
     934             473 :       NS_ENSURE_TRUE(stmt, false);
     935                 : 
     936             946 :       mozStorageStatementScoper scoper(stmt);
     937             946 :       nsresult rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("visit_date"),
     938             473 :                                           _place.visitTime);
     939             473 :       NS_ENSURE_SUCCESS(rv, rv);
     940                 : 
     941             946 :       scoper.Abandon();
     942                 :     }
     943                 :     // Otherwise, we want information about the most recent visit.
     944                 :     else {
     945                 :       stmt = mHistory->GetStatement(
     946                 :         "SELECT id, session, visit_date "
     947                 :         "FROM moz_historyvisits "
     948                 :         "WHERE place_id = (SELECT id FROM moz_places WHERE url = :page_url) "
     949                 :         "ORDER BY visit_date DESC "
     950              14 :       );
     951              14 :       NS_ENSURE_TRUE(stmt, false);
     952                 :     }
     953             974 :     mozStorageStatementScoper scoper(stmt);
     954                 : 
     955             487 :     nsresult rv = URIBinder::Bind(stmt, NS_LITERAL_CSTRING("page_url"),
     956             487 :                                   _place.spec);
     957             487 :     NS_ENSURE_SUCCESS(rv, false);
     958                 : 
     959                 :     bool hasResult;
     960             487 :     rv = stmt->ExecuteStep(&hasResult);
     961             487 :     NS_ENSURE_SUCCESS(rv, false);
     962             487 :     if (!hasResult) {
     963              10 :       return false;
     964                 :     }
     965                 : 
     966             477 :     rv = stmt->GetInt64(0, &_place.visitId);
     967             477 :     NS_ENSURE_SUCCESS(rv, false);
     968             477 :     rv = stmt->GetInt64(1, &_place.sessionId);
     969             477 :     NS_ENSURE_SUCCESS(rv, false);
     970             477 :     rv = stmt->GetInt64(2, &_place.visitTime);
     971             477 :     NS_ENSURE_SUCCESS(rv, false);
     972                 : 
     973                 :     // If we have been given a visit threshold start time, go ahead and
     974                 :     // calculate if we have been recently visited.
     975             477 :     if (aThresholdStart &&
     976                 :         aThresholdStart - _place.visitTime <= RECENT_EVENT_THRESHOLD) {
     977               3 :       return true;
     978                 :     }
     979                 : 
     980             474 :     return false;
     981                 :   }
     982                 : 
     983                 :   /**
     984                 :    * Fetches information about a referrer and sets the session id for aPlace if
     985                 :    * it was a recent visit or not.
     986                 :    *
     987                 :    * @param aReferrer
     988                 :    *        The VisitData for the referrer.  This will be populated with
     989                 :    *        FetchVisitInfo.
     990                 :    * @param aPlace
     991                 :    *        The VisitData for the visit we will eventually add.
     992                 :    *
     993                 :    */
     994             474 :   void FetchReferrerInfo(VisitData& aReferrer,
     995                 :                          VisitData& aPlace)
     996                 :   {
     997             474 :     if (aReferrer.spec.IsEmpty()) {
     998             460 :       return;
     999                 :     }
    1000                 : 
    1001                 :     // If we had a referrer, we want to know about its last visit to put this
    1002                 :     // new visit into the same session.
    1003              14 :     bool recentVisit = FetchVisitInfo(aReferrer, aPlace.visitTime);
    1004                 :     // At this point, we know the referrer's session id, which this new visit
    1005                 :     // should also share.
    1006              14 :     if (recentVisit) {
    1007               3 :       aPlace.sessionId = aReferrer.sessionId;
    1008                 :     }
    1009                 :     // However, if it isn't recent enough, we don't care to log anything about
    1010                 :     // the referrer and we'll start a new session.
    1011                 :     else {
    1012                 :       // We must change both the place and referrer to indicate that we will
    1013                 :       // not be using the referrer's data. This behavior has test coverage, so
    1014                 :       // if this invariant changes, we'll know.
    1015              11 :       aPlace.referrerSpec.Truncate();
    1016              11 :       aReferrer.visitId = 0;
    1017                 :     }
    1018                 :   }
    1019                 : 
    1020                 :   /**
    1021                 :    * Adds a visit for _place and updates it with the right visit id.
    1022                 :    *
    1023                 :    * @param _place
    1024                 :    *        The VisitData for the place we need to know visit information about.
    1025                 :    * @param aReferrer
    1026                 :    *        A reference to the referrer's visit data.
    1027                 :    */
    1028             473 :   nsresult AddVisit(VisitData& _place,
    1029                 :                     const VisitData& aReferrer)
    1030                 :   {
    1031                 :     nsresult rv;
    1032             946 :     nsCOMPtr<mozIStorageStatement> stmt;
    1033             473 :     if (_place.placeId) {
    1034                 :       stmt = mHistory->GetStatement(
    1035                 :         "INSERT INTO moz_historyvisits "
    1036                 :           "(from_visit, place_id, visit_date, visit_type, session) "
    1037                 :         "VALUES (:from_visit, :page_id, :visit_date, :visit_type, :session) "
    1038             472 :       );
    1039             472 :       NS_ENSURE_STATE(stmt);
    1040             472 :       rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("page_id"), _place.placeId);
    1041             472 :       NS_ENSURE_SUCCESS(rv, rv);
    1042                 :     }
    1043                 :     else {
    1044                 :       stmt = mHistory->GetStatement(
    1045                 :         "INSERT INTO moz_historyvisits "
    1046                 :           "(from_visit, place_id, visit_date, visit_type, session) "
    1047                 :         "VALUES (:from_visit, (SELECT id FROM moz_places WHERE url = :page_url), :visit_date, :visit_type, :session) "
    1048               1 :       );
    1049               1 :       NS_ENSURE_STATE(stmt);
    1050               1 :       rv = URIBinder::Bind(stmt, NS_LITERAL_CSTRING("page_url"), _place.spec);
    1051               1 :       NS_ENSURE_SUCCESS(rv, rv);
    1052                 :     }
    1053             946 :     rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("from_visit"),
    1054             473 :                                aReferrer.visitId);
    1055             473 :     NS_ENSURE_SUCCESS(rv, rv);
    1056             946 :     rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("visit_date"),
    1057             473 :                                _place.visitTime);
    1058             473 :     NS_ENSURE_SUCCESS(rv, rv);
    1059             473 :     PRInt32 transitionType = _place.transitionType;
    1060             473 :     NS_ASSERTION(transitionType >= nsINavHistoryService::TRANSITION_LINK &&
    1061                 :                  transitionType <= nsINavHistoryService::TRANSITION_FRAMED_LINK,
    1062                 :                  "Invalid transition type!");
    1063             946 :     rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("visit_type"),
    1064             473 :                                transitionType);
    1065             473 :     NS_ENSURE_SUCCESS(rv, rv);
    1066             946 :     rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("session"),
    1067             473 :                                _place.sessionId);
    1068             473 :     NS_ENSURE_SUCCESS(rv, rv);
    1069                 : 
    1070             946 :     mozStorageStatementScoper scoper(stmt);
    1071             473 :     rv = stmt->Execute();
    1072             473 :     NS_ENSURE_SUCCESS(rv, rv);
    1073                 : 
    1074                 :     // Now that it should be in the database, we need to obtain the id of the
    1075                 :     // visit we just added.
    1076             473 :     (void)FetchVisitInfo(_place);
    1077                 : 
    1078             473 :     return NS_OK;
    1079                 :   }
    1080                 : 
    1081                 :   /**
    1082                 :    * Updates the frecency, and possibly the hidden-ness of aPlace.
    1083                 :    *
    1084                 :    * @param aPlace
    1085                 :    *        The VisitData for the place we want to update.
    1086                 :    */
    1087             473 :   nsresult UpdateFrecency(const VisitData& aPlace)
    1088                 :   {
    1089                 :     nsresult rv;
    1090                 :     { // First, set our frecency to the proper value.
    1091             946 :       nsCOMPtr<mozIStorageStatement> stmt;
    1092             473 :       if (aPlace.placeId) {
    1093                 :         stmt = mHistory->GetStatement(
    1094                 :           "UPDATE moz_places "
    1095                 :           "SET frecency = CALCULATE_FRECENCY(:page_id) "
    1096                 :           "WHERE id = :page_id"
    1097             472 :         );
    1098             472 :         NS_ENSURE_STATE(stmt);
    1099             472 :         rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("page_id"), aPlace.placeId);
    1100             472 :         NS_ENSURE_SUCCESS(rv, rv);
    1101                 :       }
    1102                 :       else {
    1103                 :         stmt = mHistory->GetStatement(
    1104                 :           "UPDATE moz_places "
    1105                 :           "SET frecency = CALCULATE_FRECENCY(id) "
    1106                 :           "WHERE url = :page_url"
    1107               1 :         );
    1108               1 :         NS_ENSURE_STATE(stmt);
    1109               1 :         rv = URIBinder::Bind(stmt, NS_LITERAL_CSTRING("page_url"), aPlace.spec);
    1110               1 :         NS_ENSURE_SUCCESS(rv, rv);
    1111                 :       }
    1112             946 :       mozStorageStatementScoper scoper(stmt);
    1113                 : 
    1114             473 :       rv = stmt->Execute();
    1115             473 :       NS_ENSURE_SUCCESS(rv, rv);
    1116                 :     }
    1117                 : 
    1118                 :     { // Now, we need to mark the page as not hidden if the frecency is now
    1119                 :       // nonzero.
    1120             946 :       nsCOMPtr<mozIStorageStatement> stmt;
    1121             473 :       if (aPlace.placeId) {
    1122                 :         stmt = mHistory->GetStatement(
    1123                 :           "UPDATE moz_places "
    1124                 :           "SET hidden = 0 "
    1125                 :           "WHERE id = :page_id AND frecency <> 0"
    1126             472 :         );
    1127             472 :         NS_ENSURE_STATE(stmt);
    1128             472 :         rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("page_id"), aPlace.placeId);
    1129             472 :         NS_ENSURE_SUCCESS(rv, rv);
    1130                 :       }
    1131                 :       else {
    1132                 :         stmt = mHistory->GetStatement(
    1133                 :           "UPDATE moz_places "
    1134                 :           "SET hidden = 0 "
    1135                 :           "WHERE url = :page_url AND frecency <> 0"
    1136               1 :         );
    1137               1 :         NS_ENSURE_STATE(stmt);
    1138               1 :         rv = URIBinder::Bind(stmt, NS_LITERAL_CSTRING("page_url"), aPlace.spec);
    1139               1 :         NS_ENSURE_SUCCESS(rv, rv);
    1140                 :       }
    1141                 : 
    1142             946 :       mozStorageStatementScoper scoper(stmt);
    1143             473 :       rv = stmt->Execute();
    1144             473 :       NS_ENSURE_SUCCESS(rv, rv);
    1145                 :     }
    1146                 : 
    1147             473 :     return NS_OK;
    1148                 :   }
    1149                 : 
    1150                 :   mozIStorageConnection* mDBConn;
    1151                 : 
    1152                 :   nsTArray<VisitData> mPlaces;
    1153                 :   nsTArray<VisitData> mReferrers;
    1154                 : 
    1155                 :   /**
    1156                 :    * We own a strong reference to this, but in an indirect way.  We call AddRef
    1157                 :    * in our constructor, which happens on the main thread, and proxy the relase
    1158                 :    * of the object to the main thread in our destructor.
    1159                 :    */
    1160                 :   mozIVisitInfoCallback* mCallback;
    1161                 : 
    1162                 :   /**
    1163                 :    * Strong reference to the History object because we do not want it to
    1164                 :    * disappear out from under us.
    1165                 :    */
    1166                 :   nsRefPtr<History> mHistory;
    1167                 : };
    1168                 : 
    1169                 : /**
    1170                 :  * Sets the page title for a page in moz_places (if necessary).
    1171                 :  */
    1172                 : class SetPageTitle : public nsRunnable
    1173              64 : {
    1174                 : public:
    1175                 :   /**
    1176                 :    * Sets a pages title in the database asynchronously.
    1177                 :    *
    1178                 :    * @param aConnection
    1179                 :    *        The database connection to use for this operation.
    1180                 :    * @param aURI
    1181                 :    *        The URI to set the page title on.
    1182                 :    * @param aTitle
    1183                 :    *        The title to set for the page, if the page exists.
    1184                 :    */
    1185              16 :   static nsresult Start(mozIStorageConnection* aConnection,
    1186                 :                         nsIURI* aURI,
    1187                 :                         const nsAString& aTitle)
    1188                 :   {
    1189              16 :     NS_PRECONDITION(NS_IsMainThread(),
    1190                 :                     "This should be called on the main thread");
    1191              16 :     NS_PRECONDITION(aURI, "Must pass a non-null URI object!");
    1192                 : 
    1193              32 :     nsCString spec;
    1194              16 :     nsresult rv = aURI->GetSpec(spec);
    1195              16 :     NS_ENSURE_SUCCESS(rv, rv);
    1196                 : 
    1197              32 :     nsRefPtr<SetPageTitle> event = new SetPageTitle(spec, aTitle);
    1198                 : 
    1199                 :     // Get the target thread, and then start the work!
    1200              32 :     nsCOMPtr<nsIEventTarget> target = do_GetInterface(aConnection);
    1201              16 :     NS_ENSURE_TRUE(target, NS_ERROR_UNEXPECTED);
    1202              16 :     rv = target->Dispatch(event, NS_DISPATCH_NORMAL);
    1203              16 :     NS_ENSURE_SUCCESS(rv, rv);
    1204                 : 
    1205              16 :     return NS_OK;
    1206                 :   }
    1207                 : 
    1208              16 :   NS_IMETHOD Run()
    1209                 :   {
    1210              16 :     NS_PRECONDITION(!NS_IsMainThread(),
    1211                 :                     "This should not be called on the main thread");
    1212                 : 
    1213                 :     // First, see if the page exists in the database (we'll need its id later).
    1214              16 :     bool exists = mHistory->FetchPageInfo(mPlace);
    1215              16 :     if (!exists || !mPlace.titleChanged) {
    1216                 :       // We have no record of this page, or we have no title change, so there
    1217                 :       // is no need to do any further work.
    1218               0 :       return NS_OK;
    1219                 :     }
    1220                 : 
    1221              16 :     NS_ASSERTION(mPlace.placeId > 0,
    1222                 :                  "We somehow have an invalid place id here!");
    1223                 : 
    1224                 :     // Now we can update our database record.
    1225                 :     nsCOMPtr<mozIStorageStatement> stmt =
    1226                 :       mHistory->GetStatement(
    1227                 :         "UPDATE moz_places "
    1228                 :         "SET title = :page_title "
    1229                 :         "WHERE id = :page_id "
    1230              32 :       );
    1231              16 :     NS_ENSURE_STATE(stmt);
    1232                 : 
    1233                 :     {
    1234              32 :       mozStorageStatementScoper scoper(stmt);
    1235              32 :       nsresult rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("page_id"),
    1236              16 :                                           mPlace.placeId);
    1237              16 :       NS_ENSURE_SUCCESS(rv, rv);
    1238                 :       // Empty strings should clear the title, just like
    1239                 :       // nsNavHistory::SetPageTitle.
    1240              16 :       if (mPlace.title.IsEmpty()) {
    1241               0 :         rv = stmt->BindNullByName(NS_LITERAL_CSTRING("page_title"));
    1242                 :       }
    1243                 :       else {
    1244              32 :         rv = stmt->BindStringByName(NS_LITERAL_CSTRING("page_title"),
    1245              32 :                                     StringHead(mPlace.title, TITLE_LENGTH_MAX));
    1246                 :       }
    1247              16 :       NS_ENSURE_SUCCESS(rv, rv);
    1248              16 :       rv = stmt->Execute();
    1249              16 :       NS_ENSURE_SUCCESS(rv, rv);
    1250                 :     }
    1251                 : 
    1252                 :     nsCOMPtr<nsIRunnable> event =
    1253              32 :       new NotifyTitleObservers(mPlace.spec, mPlace.title, mPlace.guid);
    1254              16 :     nsresult rv = NS_DispatchToMainThread(event);
    1255              16 :     NS_ENSURE_SUCCESS(rv, rv);
    1256                 : 
    1257              16 :     return NS_OK;
    1258                 :   }
    1259                 : 
    1260                 : private:
    1261              16 :   SetPageTitle(const nsCString& aSpec,
    1262                 :                const nsAString& aTitle)
    1263              16 :   : mHistory(History::GetService())
    1264                 :   {
    1265              16 :     mPlace.spec = aSpec;
    1266              16 :     mPlace.title = aTitle;
    1267              16 :   }
    1268                 : 
    1269                 :   VisitData mPlace;
    1270                 : 
    1271                 :   /**
    1272                 :    * Strong reference to the History object because we do not want it to
    1273                 :    * disappear out from under us.
    1274                 :    */
    1275                 :   nsRefPtr<History> mHistory;
    1276                 : };
    1277                 : 
    1278                 : /**
    1279                 :  * Adds download-specific annotations to a download page.
    1280                 :  */
    1281                 : class SetDownloadAnnotations : public mozIVisitInfoCallback
    1282              18 : {
    1283                 : public:
    1284                 :   NS_DECL_ISUPPORTS
    1285                 : 
    1286              18 :   SetDownloadAnnotations(nsIURI* aDestination)
    1287                 :   : mDestination(aDestination)
    1288              18 :   , mHistory(History::GetService())
    1289                 :   {
    1290              18 :     MOZ_ASSERT(mDestination);
    1291              18 :     MOZ_ASSERT(NS_IsMainThread());
    1292              18 :   }
    1293                 : 
    1294               0 :   NS_IMETHOD HandleError(nsresult aResultCode, mozIPlaceInfo *aPlaceInfo)
    1295                 :   {
    1296                 :     // Just don't add the annotations in case the visit isn't added.
    1297               0 :     return NS_OK;
    1298                 :   }
    1299                 : 
    1300              18 :   NS_IMETHOD HandleResult(mozIPlaceInfo *aPlaceInfo)
    1301                 :   {
    1302                 :     // Exit silently if the download destination is not a local file.
    1303              36 :     nsCOMPtr<nsIFileURL> destinationFileURL = do_QueryInterface(mDestination);
    1304              18 :     if (!destinationFileURL) {
    1305               1 :       return NS_OK;
    1306                 :     }
    1307                 : 
    1308              34 :     nsCOMPtr<nsIURI> source;
    1309              17 :     nsresult rv = aPlaceInfo->GetUri(getter_AddRefs(source));
    1310              17 :     NS_ENSURE_SUCCESS(rv, rv);
    1311                 : 
    1312              34 :     nsCOMPtr<nsIFile> destinationFile;
    1313              17 :     rv = destinationFileURL->GetFile(getter_AddRefs(destinationFile));
    1314              17 :     NS_ENSURE_SUCCESS(rv, rv);
    1315                 : 
    1316              34 :     nsAutoString destinationFileName;
    1317              17 :     rv = destinationFile->GetLeafName(destinationFileName);
    1318              17 :     NS_ENSURE_SUCCESS(rv, rv);
    1319                 : 
    1320              34 :     nsCAutoString destinationURISpec;
    1321              17 :     rv = destinationFileURL->GetSpec(destinationURISpec);
    1322              17 :     NS_ENSURE_SUCCESS(rv, rv);
    1323                 : 
    1324                 :     // Use annotations for storing the additional download metadata.
    1325              17 :     nsAnnotationService* annosvc = nsAnnotationService::GetAnnotationService();
    1326              17 :     NS_ENSURE_TRUE(annosvc, NS_ERROR_OUT_OF_MEMORY);
    1327                 : 
    1328                 :     rv = annosvc->SetPageAnnotationString(
    1329                 :       source,
    1330              17 :       DESTINATIONFILEURI_ANNO,
    1331              17 :       NS_ConvertUTF8toUTF16(destinationURISpec),
    1332                 :       0,
    1333                 :       nsIAnnotationService::EXPIRE_WITH_HISTORY
    1334              17 :     );
    1335              17 :     NS_ENSURE_SUCCESS(rv, rv);
    1336                 : 
    1337                 :     rv = annosvc->SetPageAnnotationString(
    1338                 :       source,
    1339              17 :       DESTINATIONFILENAME_ANNO,
    1340                 :       destinationFileName,
    1341                 :       0,
    1342                 :       nsIAnnotationService::EXPIRE_WITH_HISTORY
    1343              17 :     );
    1344              17 :     NS_ENSURE_SUCCESS(rv, rv);
    1345                 : 
    1346              34 :     nsAutoString title;
    1347              17 :     rv = aPlaceInfo->GetTitle(title);
    1348              17 :     NS_ENSURE_SUCCESS(rv, rv);
    1349                 : 
    1350                 :     // In case we are downloading a file that does not correspond to a web
    1351                 :     // page for which the title is present, we populate the otherwise empty
    1352                 :     // history title with the name of the destination file, to allow it to be
    1353                 :     // visible and searchable in history results.
    1354              17 :     if (title.IsEmpty()) {
    1355              17 :       rv = mHistory->SetURITitle(source, destinationFileName);
    1356              17 :       NS_ENSURE_SUCCESS(rv, rv);
    1357                 :     }
    1358                 : 
    1359              17 :     return NS_OK;
    1360                 :   }
    1361                 : 
    1362               0 :   NS_IMETHOD HandleCompletion()
    1363                 :   {
    1364               0 :     return NS_OK;
    1365                 :   }
    1366                 : 
    1367                 : private:
    1368                 :   nsCOMPtr<nsIURI> mDestination;
    1369                 : 
    1370                 :   /**
    1371                 :    * Strong reference to the History object because we do not want it to
    1372                 :    * disappear out from under us.
    1373                 :    */
    1374                 :   nsRefPtr<History> mHistory;
    1375                 : };
    1376             144 : NS_IMPL_ISUPPORTS1(
    1377                 :   SetDownloadAnnotations,
    1378                 :   mozIVisitInfoCallback
    1379                 : )
    1380                 : 
    1381                 : /**
    1382                 :  * Stores an embed visit, and notifies observers.
    1383                 :  *
    1384                 :  * @param aPlace
    1385                 :  *        The VisitData of the visit to store as an embed visit.
    1386                 :  * @param [optional] aCallback
    1387                 :  *        The mozIVisitInfoCallback to notify, if provided.
    1388                 :  */
    1389                 : void
    1390              10 : StoreAndNotifyEmbedVisit(VisitData& aPlace,
    1391                 :                          mozIVisitInfoCallback* aCallback = NULL)
    1392                 : {
    1393              10 :   NS_PRECONDITION(aPlace.transitionType == nsINavHistoryService::TRANSITION_EMBED,
    1394                 :                   "Must only pass TRANSITION_EMBED visits to this!");
    1395              10 :   NS_PRECONDITION(NS_IsMainThread(), "Must be called on the main thread!");
    1396                 : 
    1397              20 :   nsCOMPtr<nsIURI> uri;
    1398              10 :   (void)NS_NewURI(getter_AddRefs(uri), aPlace.spec);
    1399                 : 
    1400              10 :   nsNavHistory* navHistory = nsNavHistory::GetHistoryService();
    1401              10 :   if (!navHistory || !uri) {
    1402                 :     return;
    1403                 :   }
    1404                 : 
    1405              10 :   navHistory->registerEmbedVisit(uri, aPlace.visitTime);
    1406                 : 
    1407              10 :   if (aCallback) {
    1408                 :     // NotifyVisitInfoCallback does not hold a strong reference to the callback,
    1409                 :     // so we have to manage it by AddRefing now and then releasing it after the
    1410                 :     // event has run.
    1411               8 :     NS_ADDREF(aCallback);
    1412                 :     nsCOMPtr<nsIRunnable> event =
    1413              16 :       new NotifyVisitInfoCallback(aCallback, aPlace, NS_OK);
    1414               8 :     (void)NS_DispatchToMainThread(event);
    1415                 : 
    1416                 :     // Also dispatch an event to release our reference to the callback after
    1417                 :     // NotifyVisitInfoCallback has run.
    1418              16 :     nsCOMPtr<nsIThread> mainThread = do_GetMainThread();
    1419               8 :     (void)NS_ProxyRelease(mainThread, aCallback, true);
    1420                 :   }
    1421                 : 
    1422              20 :   VisitData noReferrer;
    1423              20 :   nsCOMPtr<nsIRunnable> event = new NotifyVisitObservers(aPlace, noReferrer);
    1424              10 :   (void)NS_DispatchToMainThread(event);
    1425                 : }
    1426                 : 
    1427               0 : NS_MEMORY_REPORTER_MALLOC_SIZEOF_FUN(HistoryLinksHashtableMallocSizeOf,
    1428                 :                                      "history-links-hashtable")
    1429                 : 
    1430               0 : PRInt64 GetHistoryObserversSize()
    1431                 : {
    1432               0 :   History* history = History::GetService();
    1433               0 :   if (!history)
    1434               0 :     return 0;
    1435               0 :   return history->SizeOfIncludingThis(HistoryLinksHashtableMallocSizeOf);
    1436                 : }
    1437                 : 
    1438             123 : NS_MEMORY_REPORTER_IMPLEMENT(HistoryService,
    1439                 :     "explicit/history-links-hashtable",
    1440                 :     KIND_HEAP,
    1441                 :     UNITS_BYTES,
    1442                 :     GetHistoryObserversSize,
    1443                 :     "Memory used by the hashtable of observers Places uses to notify objects of "
    1444             369 :     "changes to links' visited state.")
    1445                 : 
    1446                 : } // anonymous namespace
    1447                 : 
    1448                 : ////////////////////////////////////////////////////////////////////////////////
    1449                 : //// History
    1450                 : 
    1451                 : History* History::gService = NULL;
    1452                 : 
    1453             123 : History::History()
    1454                 :   : mShuttingDown(false)
    1455             123 :   , mShutdownMutex("History::mShutdownMutex")
    1456                 : {
    1457             123 :   NS_ASSERTION(!gService, "Ruh-roh!  This service has already been created!");
    1458             123 :   gService = this;
    1459                 : 
    1460             246 :   nsCOMPtr<nsIObserverService> os = services::GetObserverService();
    1461             123 :   NS_WARN_IF_FALSE(os, "Observer service was not found!");
    1462             123 :   if (os) {
    1463             123 :     (void)os->AddObserver(this, TOPIC_PLACES_SHUTDOWN, false);
    1464                 :   }
    1465                 : 
    1466             123 :   NS_RegisterMemoryReporter(new NS_MEMORY_REPORTER_NAME(HistoryService));
    1467             123 : }
    1468                 : 
    1469             369 : History::~History()
    1470                 : {
    1471             123 :   gService = NULL;
    1472                 : 
    1473                 : #ifdef DEBUG
    1474             123 :   if (mObservers.IsInitialized()) {
    1475               1 :     NS_ASSERTION(mObservers.Count() == 0,
    1476                 :                  "Not all Links were removed before we disappear!");
    1477                 :   }
    1478                 : #endif
    1479             492 : }
    1480                 : 
    1481                 : void
    1482            2323 : History::NotifyVisited(nsIURI* aURI)
    1483                 : {
    1484            2323 :   NS_ASSERTION(aURI, "Ruh-roh!  A NULL URI was passed to us!");
    1485                 : 
    1486            4646 :   nsAutoScriptBlocker scriptBlocker;
    1487                 : 
    1488            2323 :   if (XRE_GetProcessType() == GeckoProcessType_Default) {
    1489            4646 :     nsTArray<ContentParent*> cplist;
    1490            2323 :     ContentParent::GetAll(cplist);
    1491            2323 :     for (PRUint32 i = 0; i < cplist.Length(); ++i) {
    1492               0 :       unused << cplist[i]->SendNotifyVisited(aURI);
    1493                 :     }
    1494                 :   }
    1495                 : 
    1496                 :   // If the hash table has not been initialized, then we have nothing to notify
    1497                 :   // about.
    1498            2323 :   if (!mObservers.IsInitialized()) {
    1499                 :     return;
    1500                 :   }
    1501                 : 
    1502                 :   // Additionally, if we have no observers for this URI, we have nothing to
    1503                 :   // notify about.
    1504              20 :   KeyClass* key = mObservers.GetEntry(aURI);
    1505              20 :   if (!key) {
    1506                 :     return;
    1507                 :   }
    1508                 : 
    1509                 :   // Update status of each Link node.
    1510                 :   {
    1511                 :     // RemoveEntry will destroy the array, this iterator should not survive it.
    1512               8 :     ObserverArray::ForwardIterator iter(key->array);
    1513              13 :     while (iter.HasMore()) {
    1514               5 :       Link* link = iter.GetNext();
    1515               5 :       link->SetLinkState(eLinkState_Visited);
    1516                 :       // Verify that the observers hash doesn't mutate while looping through
    1517                 :       // the links associated with this URI.
    1518               5 :       NS_ABORT_IF_FALSE(key == mObservers.GetEntry(aURI),
    1519                 :                         "The URIs hash mutated!");
    1520                 :     }
    1521                 :   }
    1522                 : 
    1523                 :   // All the registered nodes can now be removed for this URI.
    1524               4 :   mObservers.RemoveEntry(aURI);
    1525                 : }
    1526                 : 
    1527                 : mozIStorageAsyncStatement*
    1528             342 : History::GetIsVisitedStatement()
    1529                 : {
    1530             342 :   if (mIsVisitedStatement) {
    1531             340 :     return mIsVisitedStatement;
    1532                 :   }
    1533                 : 
    1534                 :   // If we don't yet have a database connection, go ahead and clone it now.
    1535               2 :   if (!mReadOnlyDBConn) {
    1536               2 :     mozIStorageConnection* dbConn = GetDBConn();
    1537               2 :     NS_ENSURE_TRUE(dbConn, nsnull);
    1538                 : 
    1539               2 :     (void)dbConn->Clone(true, getter_AddRefs(mReadOnlyDBConn));
    1540               2 :     NS_ENSURE_TRUE(mReadOnlyDBConn, nsnull);
    1541                 :   }
    1542                 : 
    1543                 :   // Now we can create our cached statement.
    1544               4 :   nsresult rv = mReadOnlyDBConn->CreateAsyncStatement(NS_LITERAL_CSTRING(
    1545                 :     "SELECT 1 "
    1546                 :     "FROM moz_places h "
    1547                 :     "WHERE url = ?1 "
    1548                 :       "AND last_visit_date NOTNULL "
    1549               4 :   ),  getter_AddRefs(mIsVisitedStatement));
    1550               2 :   NS_ENSURE_SUCCESS(rv, nsnull);
    1551               2 :   return mIsVisitedStatement;
    1552                 : }
    1553                 : 
    1554                 : nsresult
    1555             456 : History::InsertPlace(const VisitData& aPlace)
    1556                 : {
    1557             456 :   NS_PRECONDITION(aPlace.placeId == 0, "should not have a valid place id!");
    1558             456 :   NS_PRECONDITION(!NS_IsMainThread(), "must be called off of the main thread!");
    1559                 : 
    1560                 :   nsCOMPtr<mozIStorageStatement> stmt = GetStatement(
    1561                 :       "INSERT INTO moz_places "
    1562                 :         "(url, title, rev_host, hidden, typed, guid) "
    1563                 :       "VALUES (:url, :title, :rev_host, :hidden, :typed, :guid) "
    1564             912 :     );
    1565             456 :   NS_ENSURE_STATE(stmt);
    1566             912 :   mozStorageStatementScoper scoper(stmt);
    1567                 : 
    1568             912 :   nsresult rv = stmt->BindStringByName(NS_LITERAL_CSTRING("rev_host"),
    1569             456 :                                        aPlace.revHost);
    1570             456 :   NS_ENSURE_SUCCESS(rv, rv);
    1571             456 :   rv = URIBinder::Bind(stmt, NS_LITERAL_CSTRING("url"), aPlace.spec);
    1572             456 :   NS_ENSURE_SUCCESS(rv, rv);
    1573                 :   // Empty strings should have no title, just like nsNavHistory::SetPageTitle.
    1574             456 :   if (aPlace.title.IsEmpty()) {
    1575              78 :     rv = stmt->BindNullByName(NS_LITERAL_CSTRING("title"));
    1576                 :   }
    1577                 :   else {
    1578             756 :     rv = stmt->BindStringByName(NS_LITERAL_CSTRING("title"),
    1579             756 :                                 StringHead(aPlace.title, TITLE_LENGTH_MAX));
    1580                 :   }
    1581             456 :   NS_ENSURE_SUCCESS(rv, rv);
    1582             456 :   rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("typed"), aPlace.typed);
    1583             456 :   NS_ENSURE_SUCCESS(rv, rv);
    1584             456 :   rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("hidden"), aPlace.hidden);
    1585             456 :   NS_ENSURE_SUCCESS(rv, rv);
    1586             912 :   nsCAutoString guid(aPlace.guid);
    1587             456 :   if (aPlace.guid.IsVoid()) {
    1588             212 :     rv = GenerateGUID(guid);
    1589             212 :     NS_ENSURE_SUCCESS(rv, rv);
    1590                 :   }
    1591             456 :   rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("guid"), guid);
    1592             456 :   NS_ENSURE_SUCCESS(rv, rv);
    1593             456 :   rv = stmt->Execute();
    1594             456 :   NS_ENSURE_SUCCESS(rv, rv);
    1595                 : 
    1596             455 :   return NS_OK;
    1597                 : }
    1598                 : 
    1599                 : nsresult
    1600              18 : History::UpdatePlace(const VisitData& aPlace)
    1601                 : {
    1602              18 :   NS_PRECONDITION(!NS_IsMainThread(), "must be called off of the main thread!");
    1603              18 :   NS_PRECONDITION(aPlace.placeId > 0, "must have a valid place id!");
    1604              18 :   NS_PRECONDITION(!aPlace.guid.IsVoid(), "must have a guid!");
    1605                 : 
    1606                 :   nsCOMPtr<mozIStorageStatement> stmt = GetStatement(
    1607                 :       "UPDATE moz_places "
    1608                 :       "SET title = :title, "
    1609                 :           "hidden = :hidden, "
    1610                 :           "typed = :typed, "
    1611                 :           "guid = :guid "
    1612                 :       "WHERE id = :page_id "
    1613              36 :     );
    1614              18 :   NS_ENSURE_STATE(stmt);
    1615              36 :   mozStorageStatementScoper scoper(stmt);
    1616                 : 
    1617                 :   nsresult rv;
    1618                 :   // Empty strings should clear the title, just like nsNavHistory::SetPageTitle.
    1619              18 :   if (aPlace.title.IsEmpty()) {
    1620               7 :     rv = stmt->BindNullByName(NS_LITERAL_CSTRING("title"));
    1621                 :   }
    1622                 :   else {
    1623              22 :     rv = stmt->BindStringByName(NS_LITERAL_CSTRING("title"),
    1624              22 :                                 StringHead(aPlace.title, TITLE_LENGTH_MAX));
    1625                 :   }
    1626              18 :   NS_ENSURE_SUCCESS(rv, rv);
    1627              18 :   rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("typed"), aPlace.typed);
    1628              18 :   NS_ENSURE_SUCCESS(rv, rv);
    1629              18 :   rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("hidden"), aPlace.hidden);
    1630              18 :   NS_ENSURE_SUCCESS(rv, rv);
    1631              18 :   rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("guid"), aPlace.guid);
    1632              18 :   NS_ENSURE_SUCCESS(rv, rv);
    1633              36 :   rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("page_id"),
    1634              18 :                              aPlace.placeId);
    1635              18 :   NS_ENSURE_SUCCESS(rv, rv);
    1636              18 :   rv = stmt->Execute();
    1637              18 :   NS_ENSURE_SUCCESS(rv, rv);
    1638                 : 
    1639              18 :   return NS_OK;
    1640                 : }
    1641                 : 
    1642                 : bool
    1643             937 : History::FetchPageInfo(VisitData& _place)
    1644                 : {
    1645             937 :   NS_PRECONDITION(!_place.spec.IsEmpty(), "must have a non-empty spec!");
    1646             937 :   NS_PRECONDITION(!NS_IsMainThread(), "must be called off of the main thread!");
    1647                 : 
    1648                 :   nsCOMPtr<mozIStorageStatement> stmt = GetStatement(
    1649                 :       "SELECT id, title, hidden, typed, guid "
    1650                 :       "FROM moz_places "
    1651                 :       "WHERE url = :page_url "
    1652            1874 :     );
    1653             937 :   NS_ENSURE_TRUE(stmt, false);
    1654            1874 :   mozStorageStatementScoper scoper(stmt);
    1655                 : 
    1656             937 :   nsresult rv = URIBinder::Bind(stmt, NS_LITERAL_CSTRING("page_url"),
    1657             937 :                                 _place.spec);
    1658             937 :   NS_ENSURE_SUCCESS(rv, false);
    1659                 : 
    1660                 :   bool hasResult;
    1661             937 :   rv = stmt->ExecuteStep(&hasResult);
    1662             937 :   NS_ENSURE_SUCCESS(rv, false);
    1663             937 :   if (!hasResult) {
    1664             456 :     return false;
    1665                 :   }
    1666                 : 
    1667             481 :   rv = stmt->GetInt64(0, &_place.placeId);
    1668             481 :   NS_ENSURE_SUCCESS(rv, false);
    1669                 : 
    1670             962 :   nsAutoString title;
    1671             481 :   rv = stmt->GetString(1, title);
    1672             481 :   NS_ENSURE_SUCCESS(rv, true);
    1673                 : 
    1674                 :   // If the title we were given was void, that means we did not bother to set
    1675                 :   // it to anything.  As a result, ignore the fact that we may have changed the
    1676                 :   // title (because we don't want to, that would be empty), and set the title
    1677                 :   // to what is currently stored in the datbase.
    1678             481 :   if (_place.title.IsVoid()) {
    1679              81 :     _place.title = title;
    1680                 :   }
    1681                 :   // Otherwise, just indicate if the title has changed.
    1682                 :   else {
    1683             400 :     _place.titleChanged = !(_place.title.Equals(title) ||
    1684             400 :                             (_place.title.IsEmpty() && title.IsVoid()));
    1685                 :   }
    1686                 : 
    1687             481 :   if (_place.hidden) {
    1688                 :     // If this transition was hidden, it is possible that others were not.
    1689                 :     // Any one visible transition makes this location visible. If database
    1690                 :     // has location as visible, reflect that in our data structure.
    1691                 :     PRInt32 hidden;
    1692              31 :     rv = stmt->GetInt32(2, &hidden);
    1693              31 :     _place.hidden = !!hidden;
    1694              31 :     NS_ENSURE_SUCCESS(rv, true);
    1695                 :   }
    1696                 : 
    1697             481 :   if (!_place.typed) {
    1698                 :     // If this transition wasn't typed, others might have been. If database
    1699                 :     // has location as typed, reflect that in our data structure.
    1700                 :     PRInt32 typed;
    1701             467 :     rv = stmt->GetInt32(3, &typed);
    1702             467 :     _place.typed = !!typed;
    1703             467 :     NS_ENSURE_SUCCESS(rv, true);
    1704                 :   }
    1705                 : 
    1706             481 :   if (_place.guid.IsVoid()) {
    1707             237 :     rv = stmt->GetUTF8String(4, _place.guid);
    1708             237 :     NS_ENSURE_SUCCESS(rv, true);
    1709                 :   }
    1710                 : 
    1711             481 :   return true;
    1712                 : }
    1713                 : 
    1714                 : /* static */ size_t
    1715               0 : History::SizeOfEntryExcludingThis(KeyClass* aEntry, nsMallocSizeOfFun aMallocSizeOf, void *)
    1716                 : {
    1717               0 :   return aEntry->array.SizeOfExcludingThis(aMallocSizeOf);
    1718                 : }
    1719                 : 
    1720                 : size_t
    1721               0 : History::SizeOfIncludingThis(nsMallocSizeOfFun aMallocSizeOfThis)
    1722                 : {
    1723               0 :   return aMallocSizeOfThis(this) +
    1724               0 :          mObservers.SizeOfExcludingThis(SizeOfEntryExcludingThis, aMallocSizeOfThis);
    1725                 : }
    1726                 : 
    1727                 : /* static */
    1728                 : History*
    1729            3303 : History::GetService()
    1730                 : {
    1731            3303 :   if (gService) {
    1732            3201 :     return gService;
    1733                 :   }
    1734                 : 
    1735             204 :   nsCOMPtr<IHistory> service(do_GetService(NS_IHISTORY_CONTRACTID));
    1736             102 :   NS_ABORT_IF_FALSE(service, "Cannot obtain IHistory service!");
    1737             102 :   NS_ASSERTION(gService, "Our constructor was not run?!");
    1738                 : 
    1739             102 :   return gService;
    1740                 : }
    1741                 : 
    1742                 : /* static */
    1743                 : History*
    1744             123 : History::GetSingleton()
    1745                 : {
    1746             123 :   if (!gService) {
    1747             123 :     gService = new History();
    1748             123 :     NS_ENSURE_TRUE(gService, nsnull);
    1749                 :   }
    1750                 : 
    1751             123 :   NS_ADDREF(gService);
    1752             123 :   return gService;
    1753                 : }
    1754                 : 
    1755                 : mozIStorageConnection*
    1756            3558 : History::GetDBConn()
    1757                 : {
    1758            3558 :   if (!mDB) {
    1759              22 :     mDB = Database::GetDatabase();
    1760              22 :     NS_ENSURE_TRUE(mDB, nsnull);
    1761                 :   }
    1762            3558 :   return mDB->MainConn();
    1763                 : }
    1764                 : 
    1765                 : void
    1766             123 : History::Shutdown()
    1767                 : {
    1768             123 :   MOZ_ASSERT(NS_IsMainThread());
    1769                 : 
    1770                 :   // Prevent other threads from scheduling uses of the DB while we mark
    1771                 :   // ourselves as shutting down.
    1772             246 :   MutexAutoLock lockedScope(mShutdownMutex);
    1773             123 :   MOZ_ASSERT(!mShuttingDown && "Shutdown was called more than once!");
    1774                 : 
    1775             123 :   mShuttingDown = true;
    1776                 : 
    1777             123 :   if (mReadOnlyDBConn) {
    1778               2 :     if (mIsVisitedStatement) {
    1779               2 :       (void)mIsVisitedStatement->Finalize();
    1780                 :     }
    1781               2 :     (void)mReadOnlyDBConn->AsyncClose(nsnull);
    1782                 :   }
    1783             123 : }
    1784                 : 
    1785                 : ////////////////////////////////////////////////////////////////////////////////
    1786                 : //// IHistory
    1787                 : 
    1788                 : NS_IMETHODIMP
    1789              10 : History::VisitURI(nsIURI* aURI,
    1790                 :                   nsIURI* aLastVisitedURI,
    1791                 :                   PRUint32 aFlags)
    1792                 : {
    1793              10 :   NS_PRECONDITION(aURI, "URI should not be NULL.");
    1794              10 :   if (mShuttingDown) {
    1795               0 :     return NS_OK;
    1796                 :   }
    1797                 : 
    1798              10 :   if (XRE_GetProcessType() == GeckoProcessType_Content) {
    1799                 :     mozilla::dom::ContentChild* cpc =
    1800               0 :       mozilla::dom::ContentChild::GetSingleton();
    1801               0 :     NS_ASSERTION(cpc, "Content Protocol is NULL!");
    1802               0 :     (void)cpc->SendVisitURI(aURI, aLastVisitedURI, aFlags);
    1803               0 :     return NS_OK;
    1804                 :   } 
    1805                 : 
    1806              10 :   nsNavHistory* navHistory = nsNavHistory::GetHistoryService();
    1807              10 :   NS_ENSURE_TRUE(navHistory, NS_ERROR_OUT_OF_MEMORY);
    1808                 : 
    1809                 :   // Silently return if URI is something we shouldn't add to DB.
    1810                 :   bool canAdd;
    1811              10 :   nsresult rv = navHistory->CanAddURI(aURI, &canAdd);
    1812              10 :   NS_ENSURE_SUCCESS(rv, rv);
    1813              10 :   if (!canAdd) {
    1814               0 :     return NS_OK;
    1815                 :   }
    1816                 : 
    1817              10 :   if (aLastVisitedURI) {
    1818                 :     bool same;
    1819               8 :     rv = aURI->Equals(aLastVisitedURI, &same);
    1820               8 :     NS_ENSURE_SUCCESS(rv, rv);
    1821               8 :     if (same) {
    1822                 :       // Do not save refresh-page visits.
    1823               0 :       return NS_OK;
    1824                 :     }
    1825                 :   }
    1826                 : 
    1827              20 :   nsTArray<VisitData> placeArray(1);
    1828              10 :   NS_ENSURE_TRUE(placeArray.AppendElement(VisitData(aURI, aLastVisitedURI)),
    1829                 :                  NS_ERROR_OUT_OF_MEMORY);
    1830              10 :   VisitData& place = placeArray.ElementAt(0);
    1831              10 :   NS_ENSURE_FALSE(place.spec.IsEmpty(), NS_ERROR_INVALID_ARG);
    1832                 : 
    1833              10 :   place.visitTime = PR_Now();
    1834                 : 
    1835                 :   // Assigns a type to the edge in the visit linked list. Each type will be
    1836                 :   // considered differently when weighting the frecency of a location.
    1837              10 :   PRUint32 recentFlags = navHistory->GetRecentFlags(aURI);
    1838              10 :   bool isFollowedLink = recentFlags & nsNavHistory::RECENT_ACTIVATED;
    1839                 : 
    1840                 :   // Embed visits should never be added to the database, and the same is valid
    1841                 :   // for redirects across frames.
    1842                 :   // For the above reasoning non-toplevel transitions are handled at first.
    1843                 :   // if the visit is toplevel or a non-toplevel followed link, then it can be
    1844                 :   // handled as usual and stored on disk.
    1845                 : 
    1846              10 :   if (!(aFlags & IHistory::TOP_LEVEL) && !isFollowedLink) {
    1847                 :     // A frame redirected to a new site without user interaction.
    1848               2 :     place.SetTransitionType(nsINavHistoryService::TRANSITION_EMBED);
    1849                 :   }
    1850               8 :   else if (aFlags & IHistory::REDIRECT_TEMPORARY) {
    1851               0 :     place.SetTransitionType(nsINavHistoryService::TRANSITION_REDIRECT_TEMPORARY);
    1852                 :   }
    1853               8 :   else if (aFlags & IHistory::REDIRECT_PERMANENT) {
    1854               0 :     place.SetTransitionType(nsINavHistoryService::TRANSITION_REDIRECT_PERMANENT);
    1855                 :   }
    1856               8 :   else if (recentFlags & nsNavHistory::RECENT_TYPED) {
    1857               1 :     place.SetTransitionType(nsINavHistoryService::TRANSITION_TYPED);
    1858                 :   }
    1859               7 :   else if (recentFlags & nsNavHistory::RECENT_BOOKMARKED) {
    1860               0 :     place.SetTransitionType(nsINavHistoryService::TRANSITION_BOOKMARK);
    1861                 :   }
    1862               7 :   else if (!(aFlags & IHistory::TOP_LEVEL) && isFollowedLink) {
    1863                 :     // User activated a link in a frame.
    1864               0 :     place.SetTransitionType(nsINavHistoryService::TRANSITION_FRAMED_LINK);
    1865                 :   }
    1866                 :   else {
    1867                 :     // User was redirected or link was clicked in the main window.
    1868               7 :     place.SetTransitionType(nsINavHistoryService::TRANSITION_LINK);
    1869                 :   }
    1870                 : 
    1871                 :   // EMBED visits are session-persistent and should not go through the database.
    1872                 :   // They exist only to keep track of isVisited status during the session.
    1873              10 :   if (place.transitionType == nsINavHistoryService::TRANSITION_EMBED) {
    1874               2 :     StoreAndNotifyEmbedVisit(place);
    1875                 :   }
    1876                 :   else {
    1877               8 :     mozIStorageConnection* dbConn = GetDBConn();
    1878               8 :     NS_ENSURE_STATE(dbConn);
    1879                 : 
    1880               8 :     rv = InsertVisitedURIs::Start(dbConn, placeArray);
    1881               8 :     NS_ENSURE_SUCCESS(rv, rv);
    1882                 :   }
    1883                 : 
    1884                 :   // Finally, notify that we've been visited.
    1885                 :   nsCOMPtr<nsIObserverService> obsService =
    1886              20 :     mozilla::services::GetObserverService();
    1887              10 :   if (obsService) {
    1888              10 :     obsService->NotifyObservers(aURI, NS_LINK_VISITED_EVENT_TOPIC, nsnull);
    1889                 :   }
    1890                 : 
    1891              10 :   return NS_OK;
    1892                 : }
    1893                 : 
    1894                 : NS_IMETHODIMP
    1895              11 : History::RegisterVisitedCallback(nsIURI* aURI,
    1896                 :                                  Link* aLink)
    1897                 : {
    1898              11 :   NS_ASSERTION(aURI, "Must pass a non-null URI!");
    1899              11 :   if (XRE_GetProcessType() == GeckoProcessType_Content) {
    1900               0 :     NS_PRECONDITION(aLink, "Must pass a non-null Link!");
    1901                 :   }
    1902                 : 
    1903                 :   // First, ensure that our hash table is setup.
    1904              11 :   if (!mObservers.IsInitialized()) {
    1905               1 :     NS_ENSURE_TRUE(mObservers.Init(VISIT_OBSERVERS_INITIAL_CACHE_SIZE),
    1906                 :                    NS_ERROR_OUT_OF_MEMORY);
    1907                 :   }
    1908                 : 
    1909                 :   // Obtain our array of observers for this URI.
    1910                 : #ifdef DEBUG
    1911              11 :   bool keyAlreadyExists = !!mObservers.GetEntry(aURI);
    1912                 : #endif
    1913              11 :   KeyClass* key = mObservers.PutEntry(aURI);
    1914              11 :   NS_ENSURE_TRUE(key, NS_ERROR_OUT_OF_MEMORY);
    1915              11 :   ObserverArray& observers = key->array;
    1916                 : 
    1917              11 :   if (observers.IsEmpty()) {
    1918              10 :     NS_ASSERTION(!keyAlreadyExists,
    1919                 :                  "An empty key was kept around in our hashtable!");
    1920                 : 
    1921                 :     // We are the first Link node to ask about this URI, or there are no pending
    1922                 :     // Links wanting to know about this URI.  Therefore, we should query the
    1923                 :     // database now.
    1924              10 :     nsresult rv = VisitedQuery::Start(aURI);
    1925                 : 
    1926                 :     // In IPC builds, we are passed a NULL Link from
    1927                 :     // ContentParent::RecvStartVisitedQuery.  Since we won't be adding a NULL
    1928                 :     // entry to our list of observers, and the code after this point assumes
    1929                 :     // that aLink is non-NULL, we will need to return now.
    1930              10 :     if (NS_FAILED(rv) || !aLink) {
    1931                 :       // Remove our array from the hashtable so we don't keep it around.
    1932               2 :       mObservers.RemoveEntry(aURI);
    1933               2 :       return rv;
    1934                 :     }
    1935                 :   }
    1936                 :   // In IPC builds, we are passed a NULL Link from
    1937                 :   // ContentParent::RecvStartVisitedQuery.  All of our code after this point
    1938                 :   // assumes aLink is non-NULL, so we have to return now.
    1939               1 :   else if (!aLink) {
    1940               0 :     NS_ASSERTION(XRE_GetProcessType() == GeckoProcessType_Default,
    1941                 :                  "We should only ever get a null Link in the default process!");
    1942               0 :     return NS_OK;
    1943                 :   }
    1944                 : 
    1945                 :   // Sanity check that Links are not registered more than once for a given URI.
    1946                 :   // This will not catch a case where it is registered for two different URIs.
    1947               9 :   NS_ASSERTION(!observers.Contains(aLink),
    1948                 :                "Already tracking this Link object!");
    1949                 : 
    1950                 :   // Start tracking our Link.
    1951               9 :   if (!observers.AppendElement(aLink)) {
    1952                 :     // Curses - unregister and return failure.
    1953               0 :     (void)UnregisterVisitedCallback(aURI, aLink);
    1954               0 :     return NS_ERROR_OUT_OF_MEMORY;
    1955                 :   }
    1956                 : 
    1957               9 :   return NS_OK;
    1958                 : }
    1959                 : 
    1960                 : NS_IMETHODIMP
    1961               4 : History::UnregisterVisitedCallback(nsIURI* aURI,
    1962                 :                                    Link* aLink)
    1963                 : {
    1964               4 :   NS_ASSERTION(aURI, "Must pass a non-null URI!");
    1965               4 :   NS_ASSERTION(aLink, "Must pass a non-null Link object!");
    1966                 : 
    1967                 :   // Get the array, and remove the item from it.
    1968               4 :   KeyClass* key = mObservers.GetEntry(aURI);
    1969               4 :   if (!key) {
    1970               0 :     NS_ERROR("Trying to unregister for a URI that wasn't registered!");
    1971               0 :     return NS_ERROR_UNEXPECTED;
    1972                 :   }
    1973               4 :   ObserverArray& observers = key->array;
    1974               4 :   if (!observers.RemoveElement(aLink)) {
    1975               0 :     NS_ERROR("Trying to unregister a node that wasn't registered!");
    1976               0 :     return NS_ERROR_UNEXPECTED;
    1977                 :   }
    1978                 : 
    1979                 :   // If the array is now empty, we should remove it from the hashtable.
    1980               4 :   if (observers.IsEmpty()) {
    1981               4 :     mObservers.RemoveEntry(aURI);
    1982                 :   }
    1983                 : 
    1984               4 :   return NS_OK;
    1985                 : }
    1986                 : 
    1987                 : NS_IMETHODIMP
    1988              17 : History::SetURITitle(nsIURI* aURI, const nsAString& aTitle)
    1989                 : {
    1990              17 :   NS_PRECONDITION(aURI, "Must pass a non-null URI!");
    1991              17 :   if (mShuttingDown) {
    1992               0 :     return NS_OK;
    1993                 :   }
    1994                 : 
    1995              17 :   if (XRE_GetProcessType() == GeckoProcessType_Content) {
    1996                 :     mozilla::dom::ContentChild * cpc = 
    1997               0 :       mozilla::dom::ContentChild::GetSingleton();
    1998               0 :     NS_ASSERTION(cpc, "Content Protocol is NULL!");
    1999               0 :     (void)cpc->SendSetURITitle(aURI, nsString(aTitle));
    2000               0 :     return NS_OK;
    2001                 :   } 
    2002                 : 
    2003              17 :   nsNavHistory* navHistory = nsNavHistory::GetHistoryService();
    2004                 : 
    2005                 :   // At first, it seems like nav history should always be available here, no
    2006                 :   // matter what.
    2007                 :   //
    2008                 :   // nsNavHistory fails to register as a service if there is no profile in
    2009                 :   // place (for instance, if user is choosing a profile).
    2010                 :   //
    2011                 :   // Maybe the correct thing to do is to not register this service if no
    2012                 :   // profile has been selected?
    2013                 :   //
    2014              17 :   NS_ENSURE_TRUE(navHistory, NS_ERROR_FAILURE);
    2015                 : 
    2016                 :   bool canAdd;
    2017              17 :   nsresult rv = navHistory->CanAddURI(aURI, &canAdd);
    2018              17 :   NS_ENSURE_SUCCESS(rv, rv);
    2019              17 :   if (!canAdd) {
    2020               1 :     return NS_OK;
    2021                 :   }
    2022                 : 
    2023                 :   // Embed visits don't have a database entry, thus don't set a title on them.
    2024              16 :   if (navHistory->hasEmbedVisit(aURI)) {
    2025               0 :     return NS_OK;
    2026                 :   }
    2027                 : 
    2028              16 :   mozIStorageConnection* dbConn = GetDBConn();
    2029              16 :   NS_ENSURE_STATE(dbConn);
    2030                 : 
    2031              16 :   rv = SetPageTitle::Start(dbConn, aURI, aTitle);
    2032              16 :   NS_ENSURE_SUCCESS(rv, rv);
    2033                 : 
    2034              16 :   return NS_OK;
    2035                 : }
    2036                 : 
    2037                 : ////////////////////////////////////////////////////////////////////////////////
    2038                 : //// nsIDownloadHistory
    2039                 : 
    2040                 : NS_IMETHODIMP
    2041              27 : History::AddDownload(nsIURI* aSource, nsIURI* aReferrer,
    2042                 :                      PRTime aStartTime, nsIURI* aDestination)
    2043                 : {
    2044              27 :   MOZ_ASSERT(NS_IsMainThread());
    2045              27 :   NS_ENSURE_ARG(aSource);
    2046                 : 
    2047              27 :   if (mShuttingDown) {
    2048               0 :     return NS_OK;
    2049                 :   }
    2050                 : 
    2051              27 :   if (XRE_GetProcessType() == GeckoProcessType_Content) {
    2052               0 :     NS_ERROR("Cannot add downloads to history from content process!");
    2053               0 :     return NS_ERROR_NOT_AVAILABLE;
    2054                 :   }
    2055                 : 
    2056              27 :   nsNavHistory* navHistory = nsNavHistory::GetHistoryService();
    2057              27 :   NS_ENSURE_TRUE(navHistory, NS_ERROR_OUT_OF_MEMORY);
    2058                 : 
    2059                 :   // Silently return if URI is something we shouldn't add to DB.
    2060                 :   bool canAdd;
    2061              27 :   nsresult rv = navHistory->CanAddURI(aSource, &canAdd);
    2062              27 :   NS_ENSURE_SUCCESS(rv, rv);
    2063              27 :   if (!canAdd) {
    2064               3 :     return NS_OK;
    2065                 :   }
    2066                 : 
    2067              48 :   nsTArray<VisitData> placeArray(1);
    2068              24 :   NS_ENSURE_TRUE(placeArray.AppendElement(VisitData(aSource, aReferrer)),
    2069                 :                  NS_ERROR_OUT_OF_MEMORY);
    2070              24 :   VisitData& place = placeArray.ElementAt(0);
    2071              24 :   NS_ENSURE_FALSE(place.spec.IsEmpty(), NS_ERROR_INVALID_ARG);
    2072                 : 
    2073              24 :   place.visitTime = aStartTime;
    2074              24 :   place.SetTransitionType(nsINavHistoryService::TRANSITION_DOWNLOAD);
    2075                 : 
    2076              24 :   mozIStorageConnection* dbConn = GetDBConn();
    2077              24 :   NS_ENSURE_STATE(dbConn);
    2078                 : 
    2079                 :   nsCOMPtr<mozIVisitInfoCallback> callback = aDestination
    2080              18 :                                   ? new SetDownloadAnnotations(aDestination)
    2081              84 :                                   : nsnull;
    2082                 : 
    2083              24 :   rv = InsertVisitedURIs::Start(dbConn, placeArray, callback);
    2084              24 :   NS_ENSURE_SUCCESS(rv, rv);
    2085                 : 
    2086                 :   // Finally, notify that we've been visited.
    2087                 :   nsCOMPtr<nsIObserverService> obsService =
    2088              48 :     mozilla::services::GetObserverService();
    2089              24 :   if (obsService) {
    2090              24 :     obsService->NotifyObservers(aSource, NS_LINK_VISITED_EVENT_TOPIC, nsnull);
    2091                 :   }
    2092                 : 
    2093              24 :   return NS_OK;
    2094                 : }
    2095                 : 
    2096                 : ////////////////////////////////////////////////////////////////////////////////
    2097                 : //// mozIAsyncHistory
    2098                 : 
    2099                 : NS_IMETHODIMP
    2100             200 : History::UpdatePlaces(const jsval& aPlaceInfos,
    2101                 :                       mozIVisitInfoCallback* aCallback,
    2102                 :                       JSContext* aCtx)
    2103                 : {
    2104             200 :   NS_ENSURE_TRUE(NS_IsMainThread(), NS_ERROR_UNEXPECTED);
    2105             200 :   NS_ENSURE_TRUE(!JSVAL_IS_PRIMITIVE(aPlaceInfos), NS_ERROR_INVALID_ARG);
    2106                 : 
    2107             197 :   uint32_t infosLength = 1;
    2108                 :   JSObject* infos;
    2109             197 :   if (JS_IsArrayObject(aCtx, JSVAL_TO_OBJECT(aPlaceInfos))) {
    2110              36 :     infos = JSVAL_TO_OBJECT(aPlaceInfos);
    2111              36 :     (void)JS_GetArrayLength(aCtx, infos, &infosLength);
    2112              36 :     NS_ENSURE_ARG(infosLength > 0);
    2113                 :   }
    2114                 :   else {
    2115                 :     // Build a temporary array to store this one item so the code below can
    2116                 :     // just loop.
    2117             161 :     infos = JS_NewArrayObject(aCtx, 0, NULL);
    2118             161 :     NS_ENSURE_TRUE(infos, NS_ERROR_OUT_OF_MEMORY);
    2119                 : 
    2120             161 :     JSBool rc = JS_DefineElement(aCtx, infos, 0, aPlaceInfos, NULL, NULL, 0);
    2121             161 :     NS_ENSURE_TRUE(rc, NS_ERROR_UNEXPECTED);
    2122                 :   }
    2123                 : 
    2124             392 :   nsTArray<VisitData> visitData;
    2125             730 :   for (uint32_t i = 0; i < infosLength; i++) {
    2126                 :     JSObject* info;
    2127             555 :     nsresult rv = GetJSObjectFromArray(aCtx, infos, i, &info);
    2128             555 :     NS_ENSURE_SUCCESS(rv, rv);
    2129                 : 
    2130            1110 :     nsCOMPtr<nsIURI> uri = GetURIFromJSObject(aCtx, info, "uri");
    2131            1110 :     nsCString guid;
    2132                 :     {
    2133            1110 :       nsString fatGUID;
    2134             555 :       GetStringFromJSObject(aCtx, info, "guid", fatGUID);
    2135             555 :       if (fatGUID.IsVoid()) {
    2136             302 :         guid.SetIsVoid(true);
    2137                 :       }
    2138                 :       else {
    2139             253 :         guid = NS_ConvertUTF16toUTF8(fatGUID);
    2140                 :       }
    2141                 :     }
    2142                 : 
    2143                 :     // Make sure that any uri we are given can be added to history, and if not,
    2144                 :     // skip it (CanAddURI will notify our callback for us).
    2145             555 :     if (uri && !CanAddURI(uri, guid, aCallback)) {
    2146              93 :       continue;
    2147                 :     }
    2148                 : 
    2149                 :     // We must have at least one of uri or guid.
    2150             462 :     NS_ENSURE_ARG(uri || !guid.IsVoid());
    2151                 : 
    2152                 :     // If we were given a guid, make sure it is valid.
    2153             453 :     bool isValidGUID = IsValidGUID(guid);
    2154             453 :     NS_ENSURE_ARG(guid.IsVoid() || isValidGUID);
    2155                 : 
    2156             902 :     nsString title;
    2157             451 :     GetStringFromJSObject(aCtx, info, "title", title);
    2158                 : 
    2159             451 :     JSObject* visits = NULL;
    2160                 :     {
    2161                 :       jsval visitsVal;
    2162             451 :       JSBool rc = JS_GetProperty(aCtx, info, "visits", &visitsVal);
    2163             451 :       NS_ENSURE_TRUE(rc, NS_ERROR_UNEXPECTED);
    2164             451 :       if (!JSVAL_IS_PRIMITIVE(visitsVal)) {
    2165             448 :         visits = JSVAL_TO_OBJECT(visitsVal);
    2166             448 :         NS_ENSURE_ARG(JS_IsArrayObject(aCtx, visits));
    2167                 :       }
    2168                 :     }
    2169             451 :     NS_ENSURE_ARG(visits);
    2170                 : 
    2171             448 :     uint32_t visitsLength = 0;
    2172             448 :     if (visits) {
    2173             448 :       (void)JS_GetArrayLength(aCtx, visits, &visitsLength);
    2174                 :     }
    2175             448 :     NS_ENSURE_ARG(visitsLength > 0);
    2176                 : 
    2177                 :     // Check each visit, and build our array of VisitData objects.
    2178             445 :     visitData.SetCapacity(visitData.Length() + visitsLength);
    2179             895 :     for (uint32_t j = 0; j < visitsLength; j++) {
    2180                 :       JSObject* visit;
    2181             454 :       rv = GetJSObjectFromArray(aCtx, visits, j, &visit);
    2182             454 :       NS_ENSURE_SUCCESS(rv, rv);
    2183                 : 
    2184             454 :       VisitData& data = *visitData.AppendElement(VisitData(uri));
    2185             454 :       data.title = title;
    2186             454 :       data.guid = guid;
    2187                 : 
    2188                 :       // We must have a date and a transaction type!
    2189             454 :       rv = GetIntFromJSObject(aCtx, visit, "visitDate", &data.visitTime);
    2190             454 :       NS_ENSURE_SUCCESS(rv, rv);
    2191             453 :       PRUint32 transitionType = 0;
    2192             453 :       rv = GetIntFromJSObject(aCtx, visit, "transitionType", &transitionType);
    2193             453 :       NS_ENSURE_SUCCESS(rv, rv);
    2194             452 :       NS_ENSURE_ARG_RANGE(transitionType,
    2195                 :                           nsINavHistoryService::TRANSITION_LINK,
    2196                 :                           nsINavHistoryService::TRANSITION_FRAMED_LINK);
    2197             450 :       data.SetTransitionType(transitionType);
    2198                 : 
    2199                 :       // If the visit is an embed visit, we do not actually add it to the
    2200                 :       // database.
    2201             450 :       if (transitionType == nsINavHistoryService::TRANSITION_EMBED) {
    2202               8 :         StoreAndNotifyEmbedVisit(data, aCallback);
    2203               8 :         visitData.RemoveElementAt(visitData.Length() - 1);
    2204               8 :         continue;
    2205                 :       }
    2206                 : 
    2207                 :       // The session id is optional.
    2208             442 :       rv = GetIntFromJSObject(aCtx, visit, "sessionId", &data.sessionId);
    2209             442 :       if (rv == NS_ERROR_INVALID_ARG) {
    2210             439 :         data.sessionId = 0;
    2211                 :       }
    2212                 :       else {
    2213               3 :         NS_ENSURE_SUCCESS(rv, rv);
    2214                 :       }
    2215                 : 
    2216                 :       // The referrer is optional.
    2217                 :       nsCOMPtr<nsIURI> referrer = GetURIFromJSObject(aCtx, visit,
    2218             884 :                                                      "referrerURI");
    2219             442 :       if (referrer) {
    2220               4 :         (void)referrer->GetSpec(data.referrerSpec);
    2221                 :       }
    2222                 :     }
    2223                 :   }
    2224                 : 
    2225             175 :   mozIStorageConnection* dbConn = GetDBConn();
    2226             175 :   NS_ENSURE_STATE(dbConn);
    2227                 : 
    2228                 :   // It is possible that all of the visits we were passed were dissallowed by
    2229                 :   // CanAddURI, which isn't an error.  If we have no visits to add, however,
    2230                 :   // we should not call InsertVisitedURIs::Start.
    2231             175 :   if (visitData.Length()) {
    2232              89 :     nsresult rv = InsertVisitedURIs::Start(dbConn, visitData, aCallback);
    2233              89 :     NS_ENSURE_SUCCESS(rv, rv);
    2234                 :   }
    2235                 : 
    2236                 :   // Be sure to notify that all of our operations are complete.  This
    2237                 :   // is dispatched to the background thread first and redirected to the
    2238                 :   // main thread from there to make sure that all database notifications
    2239                 :   // and all embed or canAddURI notifications have finished.
    2240             175 :   if (aCallback) {
    2241                 :     // NotifyCompletion does not hold a strong reference to the callback,
    2242                 :     // so we have to manage it by AddRefing now. NotifyCompletion will
    2243                 :     // release it for us once it has dispatched the callback to the main
    2244                 :     // thread.
    2245             154 :     NS_ADDREF(aCallback);
    2246                 : 
    2247             308 :     nsCOMPtr<nsIEventTarget> backgroundThread = do_GetInterface(dbConn);
    2248             154 :     NS_ENSURE_TRUE(backgroundThread, NS_ERROR_UNEXPECTED);
    2249             462 :     nsCOMPtr<nsIRunnable> event = new NotifyCompletion(aCallback);
    2250             154 :     (void)backgroundThread->Dispatch(event, NS_DISPATCH_NORMAL);
    2251                 :   }
    2252                 : 
    2253             175 :   return NS_OK;
    2254                 : }
    2255                 : 
    2256                 : NS_IMETHODIMP
    2257             336 : History::IsURIVisited(nsIURI* aURI,
    2258                 :                       mozIVisitedStatusCallback* aCallback)
    2259                 : {
    2260             336 :   NS_ENSURE_STATE(NS_IsMainThread());
    2261             336 :   NS_ENSURE_ARG(aURI);
    2262             336 :   NS_ENSURE_ARG(aCallback);
    2263                 : 
    2264             336 :   nsresult rv = VisitedQuery::Start(aURI, aCallback);
    2265             336 :   NS_ENSURE_SUCCESS(rv, rv);
    2266                 : 
    2267             336 :   return NS_OK;
    2268                 : }
    2269                 : 
    2270                 : ////////////////////////////////////////////////////////////////////////////////
    2271                 : //// nsIObserver
    2272                 : 
    2273                 : NS_IMETHODIMP
    2274             123 : History::Observe(nsISupports* aSubject, const char* aTopic,
    2275                 :                  const PRUnichar* aData)
    2276                 : {
    2277             123 :   if (strcmp(aTopic, TOPIC_PLACES_SHUTDOWN) == 0) {
    2278             123 :     Shutdown();
    2279                 : 
    2280             246 :     nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
    2281             123 :     if (os) {
    2282             123 :       (void)os->RemoveObserver(this, TOPIC_PLACES_SHUTDOWN);
    2283                 :     }
    2284                 :   }
    2285                 : 
    2286             123 :   return NS_OK;
    2287                 : }
    2288                 : 
    2289                 : ////////////////////////////////////////////////////////////////////////////////
    2290                 : //// nsISupports
    2291                 : 
    2292            4067 : NS_IMPL_THREADSAFE_ISUPPORTS4(
    2293                 :   History
    2294                 : , IHistory
    2295                 : , nsIDownloadHistory
    2296                 : , mozIAsyncHistory
    2297                 : , nsIObserver
    2298                 : )
    2299                 : 
    2300                 : } // namespace places
    2301                 : } // namespace mozilla

Generated by: LCOV version 1.7