LCOV - app.info - toolkit/components/places/History.cpp

LCOV - code coverage report
| Current view: | directory - toolkit/components/places - History.cpp (source / functions) |  | Found | Hit | Coverage |
| Test: | app.info | Lines: | 897 | 745 | 83.1 %
| Date: | 2012-04-21 | Functions: | 97 | 80 | 82.5 %

           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            4785 : struct VisitData {
          92             490 :   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             490 :   , titleChanged(false)
         101                 :   {
         102             490 :     guid.SetIsVoid(true);
         103             490 :     title.SetIsVoid(true);
         104             490 :   }
         105                 :
         106             571 :   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             571 :   , titleChanged(false)
         116                 :   {
         117             571 :     (void)aURI->GetSpec(spec);
         118             571 :     (void)GetReversedHostname(aURI, revHost);
         119             571 :     if (aReferrer) {
         120               4 :       (void)aReferrer->GetSpec(referrerSpec);
         121                 :     }
         122             571 :     guid.SetIsVoid(true);
         123             571 :     title.SetIsVoid(true);
         124             571 :   }
         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             474 :   void SetTransitionType(PRUint32 aTransitionType)
         135                 :   {
         136             474 :     typed = aTransitionType == nsINavHistoryService::TRANSITION_TYPED;
         137                 :     bool redirected =
         138                 :       aTransitionType == nsINavHistoryService::TRANSITION_REDIRECT_TEMPORARY ||
         139             474 :       aTransitionType == nsINavHistoryService::TRANSITION_REDIRECT_PERMANENT;
         140             474 :     hidden = GetHiddenState(redirected, aTransitionType);
         141             474 :     transitionType = aTransitionType;
         142             474 :   }
         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            1344 : {
         337                 : public:
         338             336 :   static nsresult Start(nsIURI* aURI,
         339                 :                         mozIVisitedStatusCallback* aCallback=nsnull)
         340                 :   {
         341             336 :     NS_PRECONDITION(aURI, "Null URI");
         342                 :
         343                 :   // If we are a content process, always remote the request to the
         344                 :   // parent process.
         345             336 :   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             336 :     nsNavHistory* navHistory = nsNavHistory::GetHistoryService();
         354             336 :     NS_ENSURE_STATE(navHistory);
         355             336 :     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             332 :     History* history = History::GetService();
         367             332 :     NS_ENSURE_STATE(history);
         368             332 :     mozIStorageAsyncStatement* stmt = history->GetIsVisitedStatement();
         369             332 :     NS_ENSURE_STATE(stmt);
         370                 :
         371                 :     // Bind by index for performance.
         372             332 :     nsresult rv = URIBinder::Bind(stmt, 0, aURI);
         373             332 :     NS_ENSURE_SUCCESS(rv, rv);
         374                 :
         375             664 :     nsRefPtr<VisitedQuery> callback = new VisitedQuery(aURI, aCallback);
         376             332 :     NS_ENSURE_TRUE(callback, NS_ERROR_OUT_OF_MEMORY);
         377                 :
         378             664 :     nsCOMPtr<mozIStoragePendingStatement> handle;
         379             332 :     return stmt->ExecuteAsync(callback, getter_AddRefs(handle));
         380                 :   }
         381                 :
         382              28 :   NS_IMETHOD HandleResult(mozIStorageResultSet* aResults)
         383                 :   {
         384                 :     // If this method is called, we've gotten results, which means we have a
         385                 :     // visit.
         386              28 :     mIsVisited = true;
         387              28 :     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             332 :   NS_IMETHOD HandleCompletion(PRUint16 aReason)
         398                 :   {
         399             332 :     if (aReason != mozIStorageStatementCallback::REASON_FINISHED) {
         400               0 :       return NS_OK;
         401                 :     }
         402                 :
         403             332 :     nsresult rv = NotifyVisitedStatus();
         404             332 :     NS_ENSURE_SUCCESS(rv, rv);
         405             332 :     return NS_OK;
         406                 :   }
         407                 :
         408             336 :   nsresult NotifyVisitedStatus()
         409                 :   {
         410                 :     // If an external handling callback is provided, just notify through it.
         411             336 :     if (mCallback) {
         412             336 :       mCallback->IsVisited(mURI, mIsVisited);
         413             336 :       return NS_OK;
         414                 :     }
         415                 :
         416               0 :     if (mIsVisited) {
         417               0 :       History* history = History::GetService();
         418               0 :       NS_ENSURE_STATE(history);
         419               0 :       history->NotifyVisited(mURI);
         420                 :     }
         421                 :
         422                 :     nsCOMPtr<nsIObserverService> observerService =
         423               0 :       mozilla::services::GetObserverService();
         424               0 :     if (observerService) {
         425               0 :       nsAutoString status;
         426               0 :       if (mIsVisited) {
         427               0 :         status.AssignLiteral(URI_VISITED);
         428                 :       }
         429                 :       else {
         430               0 :         status.AssignLiteral(URI_NOT_VISITED);
         431                 :       }
         432               0 :       (void)observerService->NotifyObservers(mURI,
         433                 :                                              URI_VISITED_RESOLUTION_TOPIC,
         434               0 :                                              status.get());
         435                 :     }
         436                 :
         437               0 :     return NS_OK;
         438                 :   }
         439                 :
         440                 : private:
         441             336 :   VisitedQuery(nsIURI* aURI,
         442                 :                mozIVisitedStatusCallback *aCallback=nsnull,
         443                 :                bool aIsVisited=false)
         444                 :   : mURI(aURI)
         445                 :   , mCallback(aCallback)
         446             336 :   , mIsVisited(aIsVisited)
         447                 :   {
         448             336 :   }
         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            1892 : {
         460                 : public:
         461             473 :   NotifyVisitObservers(VisitData& aPlace,
         462                 :                        VisitData& aReferrer)
         463                 :   : mPlace(aPlace)
         464                 :   , mReferrer(aReferrer)
         465             473 :   , mHistory(History::GetService())
         466                 :   {
         467             473 :   }
         468                 :
         469             473 :   NS_IMETHOD Run()
         470                 :   {
         471             473 :     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             473 :     if (mHistory->IsShuttingDown()) {
         475                 :       // If we are shutting down, we cannot notify the observers.
         476               0 :       return NS_OK;
         477                 :     }
         478                 :
         479             473 :     nsNavHistory* navHistory = nsNavHistory::GetHistoryService();
         480             473 :     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             946 :     nsCOMPtr<nsIURI> uri;
         486             473 :     (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             473 :     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             447 :                                 mPlace.transitionType, mPlace.guid);
         496                 :     }
         497                 :
         498                 :     nsCOMPtr<nsIObserverService> obsService =
         499             946 :       mozilla::services::GetObserverService();
         500             473 :     if (obsService) {
         501                 :       DebugOnly<nsresult> rv =
         502             946 :         obsService->NotifyObservers(uri, URI_VISIT_SAVED, nsnull);
         503             473 :       NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "Could not notify observers");
         504                 :     }
         505                 :
         506             473 :     History* history = History::GetService();
         507             473 :     NS_ENSURE_STATE(history);
         508             473 :     history->NotifyVisited(uri);
         509                 :
         510             473 :     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            1010 : CanAddURI(nsIURI* aURI,
         672             466 :           const nsCString& aGUID = EmptyCString(),
         673                 :           mozIVisitInfoCallback* aCallback = NULL)
         674                 : {
         675            1010 :   nsNavHistory* navHistory = nsNavHistory::GetHistoryService();
         676            1010 :   NS_ENSURE_TRUE(navHistory, false);
         677                 :
         678                 :   bool canAdd;
         679            1010 :   nsresult rv = navHistory->CanAddURI(aURI, &canAdd);
         680            1010 :   if (NS_SUCCEEDED(rv) && canAdd) {
         681             917 :     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             113 :   static nsresult Start(mozIStorageConnection* aConnection,
         723                 :                         nsTArray<VisitData>& aPlaces,
         724                 :                         mozIVisitInfoCallback* aCallback = NULL)
         725                 :   {
         726             113 :     NS_PRECONDITION(NS_IsMainThread(),
         727                 :                     "This should be called on the main thread");
         728             113 :     NS_PRECONDITION(aPlaces.Length() > 0, "Must pass a non-empty array!");
         729                 :
         730                 :     nsRefPtr<InsertVisitedURIs> event =
         731             226 :       new InsertVisitedURIs(aConnection, aPlaces, aCallback);
         732                 :
         733                 :     // Get the target thread, and then start the work!
         734             226 :     nsCOMPtr<nsIEventTarget> target = do_GetInterface(aConnection);
         735             113 :     NS_ENSURE_TRUE(target, NS_ERROR_UNEXPECTED);
         736             113 :     nsresult rv = target->Dispatch(event, NS_DISPATCH_NORMAL);
         737             113 :     NS_ENSURE_SUCCESS(rv, rv);
         738                 :
         739             113 :     return NS_OK;
         740                 :   }
         741                 :
         742             113 :   NS_IMETHOD Run()
         743                 :   {
         744             113 :     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             226 :     MutexAutoLock lockedScope(mHistory->GetShutdownMutex());
         749             113 :     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             226 :                                       mozIStorageConnection::TRANSACTION_IMMEDIATE);
         756                 :
         757             113 :     VisitData* lastPlace = NULL;
         758             578 :     for (nsTArray<VisitData>::size_type i = 0; i < mPlaces.Length(); i++) {
         759             466 :       VisitData& place = mPlaces.ElementAt(i);
         760             466 :       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             819 :                    mHistory->FetchPageInfo(place);
         766                 :
         767             466 :       FetchReferrerInfo(referrer, place);
         768                 :
         769             466 :       nsresult rv = DoDatabaseInserts(known, place, referrer);
         770             466 :       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             466 :       NS_ENSURE_SUCCESS(rv, rv);
         777                 :
         778             930 :       nsCOMPtr<nsIRunnable> event = new NotifyVisitObservers(place, referrer);
         779             465 :       rv = NS_DispatchToMainThread(event);
         780             465 :       NS_ENSURE_SUCCESS(rv, rv);
         781                 :
         782                 :       // Notify about title change if needed.
         783             465 :       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             930 :       lastPlace = &mPlaces.ElementAt(i);
         790                 :     }
         791                 :
         792             112 :     nsresult rv = transaction.Commit();
         793             112 :     NS_ENSURE_SUCCESS(rv, rv);
         794                 :
         795             112 :     return NS_OK;
         796                 :   }
         797                 : private:
         798             113 :   InsertVisitedURIs(mozIStorageConnection* aConnection,
         799                 :                     nsTArray<VisitData>& aPlaces,
         800                 :                     mozIVisitInfoCallback* aCallback)
         801                 :   : mDBConn(aConnection)
         802                 :   , mCallback(aCallback)
         803             113 :   , mHistory(History::GetService())
         804                 :   {
         805             113 :     NS_PRECONDITION(NS_IsMainThread(),
         806                 :                     "This should be called on the main thread");
         807                 :
         808             113 :     (void)mPlaces.SwapElements(aPlaces);
         809             113 :     (void)mReferrers.SetLength(mPlaces.Length());
         810                 :
         811             113 :     nsNavHistory* navHistory = nsNavHistory::GetHistoryService();
         812             113 :     NS_ABORT_IF_FALSE(navHistory, "Could not get nsNavHistory?!");
         813                 :
         814             579 :     for (nsTArray<VisitData>::size_type i = 0; i < mPlaces.Length(); i++) {
         815             466 :       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             466 :       if (i == 0) {
         821             113 :         PRInt64 newSessionId = navHistory->GetNewSessionID();
         822             113 :         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             466 :       if (mPlaces[i].sessionId <= 0 ||
         833               0 :           (i > 0 && mPlaces[i].sessionId >= mPlaces[0].sessionId)) {
         834             464 :         mPlaces[i].sessionId = navHistory->GetNewSessionID();
         835                 :       }
         836                 :
         837                 : #ifdef DEBUG
         838             932 :       nsCOMPtr<nsIURI> uri;
         839             466 :       (void)NS_NewURI(getter_AddRefs(uri), mPlaces[i].spec);
         840             466 :       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             113 :     NS_IF_ADDREF(mCallback);
         847             113 :   }
         848                 :
         849             226 :   virtual ~InsertVisitedURIs()
         850             226 :   {
         851             113 :     if (mCallback) {
         852             172 :       nsCOMPtr<nsIThread> mainThread = do_GetMainThread();
         853              86 :       (void)NS_ProxyRelease(mainThread, mCallback, true);
         854                 :     }
         855             452 :   }
         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             466 :   nsresult DoDatabaseInserts(bool aKnown,
         870                 :                              VisitData& aPlace,
         871                 :                              VisitData& aReferrer)
         872                 :   {
         873             466 :     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             466 :     if (aKnown) {
         879              17 :       rv = mHistory->UpdatePlace(aPlace);
         880              17 :       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             449 :       rv = mHistory->InsertPlace(aPlace);
         885             449 :       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             448 :       if (mCallback || aPlace.guid.IsEmpty()) {
         891             447 :         bool exists = mHistory->FetchPageInfo(aPlace);
         892             447 :         if (!exists) {
         893               0 :           NS_NOTREACHED("should have an entry in moz_places");
         894                 :         }
         895                 :       }
         896                 :     }
         897                 :
         898             465 :     rv = AddVisit(aPlace, aReferrer);
         899             465 :     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             465 :     rv = UpdateFrecency(aPlace);
         904             465 :     NS_ENSURE_SUCCESS(rv, rv);
         905                 :
         906             465 :     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             473 :   bool FetchVisitInfo(VisitData& _place,
         921                 :                       PRTime aThresholdStart = 0)
         922                 :   {
         923             473 :     NS_PRECONDITION(!_place.spec.IsEmpty(), "must have a non-empty spec!");
         924                 :
         925             946 :     nsCOMPtr<mozIStorageStatement> stmt;
         926                 :     // If we have a visitTime, we want information on that specific visit.
         927             473 :     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             465 :       );
         934             465 :       NS_ENSURE_TRUE(stmt, false);
         935                 :
         936             930 :       mozStorageStatementScoper scoper(stmt);
         937             930 :       nsresult rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("visit_date"),
         938             465 :                                           _place.visitTime);
         939             465 :       NS_ENSURE_SUCCESS(rv, rv);
         940                 :
         941             930 :       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               8 :       );
         951               8 :       NS_ENSURE_TRUE(stmt, false);
         952                 :     }
         953             946 :     mozStorageStatementScoper scoper(stmt);
         954                 :
         955             473 :     nsresult rv = URIBinder::Bind(stmt, NS_LITERAL_CSTRING("page_url"),
         956             473 :                                   _place.spec);
         957             473 :     NS_ENSURE_SUCCESS(rv, false);
         958                 :
         959                 :     bool hasResult;
         960             473 :     rv = stmt->ExecuteStep(&hasResult);
         961             473 :     NS_ENSURE_SUCCESS(rv, false);
         962             473 :     if (!hasResult) {
         963               4 :       return false;
         964                 :     }
         965                 :
         966             469 :     rv = stmt->GetInt64(0, &_place.visitId);
         967             469 :     NS_ENSURE_SUCCESS(rv, false);
         968             469 :     rv = stmt->GetInt64(1, &_place.sessionId);
         969             469 :     NS_ENSURE_SUCCESS(rv, false);
         970             469 :     rv = stmt->GetInt64(2, &_place.visitTime);
         971             469 :     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             469 :     if (aThresholdStart &&
         976                 :         aThresholdStart - _place.visitTime <= RECENT_EVENT_THRESHOLD) {
         977               3 :       return true;
         978                 :     }
         979                 :
         980             466 :     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             466 :   void FetchReferrerInfo(VisitData& aReferrer,
         995                 :                          VisitData& aPlace)
         996                 :   {
         997             466 :     if (aReferrer.spec.IsEmpty()) {
         998             458 :       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               8 :     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               8 :     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               5 :       aPlace.referrerSpec.Truncate();
        1016               5 :       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             465 :   nsresult AddVisit(VisitData& _place,
        1029                 :                     const VisitData& aReferrer)
        1030                 :   {
        1031                 :     nsresult rv;
        1032             930 :     nsCOMPtr<mozIStorageStatement> stmt;
        1033             465 :     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             464 :       );
        1039             464 :       NS_ENSURE_STATE(stmt);
        1040             464 :       rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("page_id"), _place.placeId);
        1041             464 :       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             930 :     rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("from_visit"),
        1054             465 :                                aReferrer.visitId);
        1055             465 :     NS_ENSURE_SUCCESS(rv, rv);
        1056             930 :     rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("visit_date"),
        1057             465 :                                _place.visitTime);
        1058             465 :     NS_ENSURE_SUCCESS(rv, rv);
        1059             465 :     PRInt32 transitionType = _place.transitionType;
        1060             465 :     NS_ASSERTION(transitionType >= nsINavHistoryService::TRANSITION_LINK &&
        1061                 :                  transitionType <= nsINavHistoryService::TRANSITION_FRAMED_LINK,
        1062                 :                  "Invalid transition type!");
        1063             930 :     rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("visit_type"),
        1064             465 :                                transitionType);
        1065             465 :     NS_ENSURE_SUCCESS(rv, rv);
        1066             930 :     rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("session"),
        1067             465 :                                _place.sessionId);
        1068             465 :     NS_ENSURE_SUCCESS(rv, rv);
        1069                 :
        1070             930 :     mozStorageStatementScoper scoper(stmt);
        1071             465 :     rv = stmt->Execute();
        1072             465 :     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             465 :     (void)FetchVisitInfo(_place);
        1077                 :
        1078             465 :     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             465 :   nsresult UpdateFrecency(const VisitData& aPlace)
        1088                 :   {
        1089                 :     nsresult rv;
        1090                 :     { // First, set our frecency to the proper value.
        1091             930 :       nsCOMPtr<mozIStorageStatement> stmt;
        1092             465 :       if (aPlace.placeId) {
        1093                 :         stmt = mHistory->GetStatement(
        1094                 :           "UPDATE moz_places "
        1095                 :           "SET frecency = CALCULATE_FRECENCY(:page_id) "
        1096                 :           "WHERE id = :page_id"
        1097             464 :         );
        1098             464 :         NS_ENSURE_STATE(stmt);
        1099             464 :         rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("page_id"), aPlace.placeId);
        1100             464 :         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             930 :       mozStorageStatementScoper scoper(stmt);
        1113                 :
        1114             465 :       rv = stmt->Execute();
        1115             465 :       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             930 :       nsCOMPtr<mozIStorageStatement> stmt;
        1121             465 :       if (aPlace.placeId) {
        1122                 :         stmt = mHistory->GetStatement(
        1123                 :           "UPDATE moz_places "
        1124                 :           "SET hidden = 0 "
        1125                 :           "WHERE id = :page_id AND frecency <> 0"
        1126             464 :         );
        1127             464 :         NS_ENSURE_STATE(stmt);
        1128             464 :         rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("page_id"), aPlace.placeId);
        1129             464 :         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             930 :       mozStorageStatementScoper scoper(stmt);
        1143             465 :       rv = stmt->Execute();
        1144             465 :       NS_ENSURE_SUCCESS(rv, rv);
        1145                 :     }
        1146                 :
        1147             465 :     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               8 : StoreAndNotifyEmbedVisit(VisitData& aPlace,
        1391                 :                          mozIVisitInfoCallback* aCallback = NULL)
        1392                 : {
        1393               8 :   NS_PRECONDITION(aPlace.transitionType == nsINavHistoryService::TRANSITION_EMBED,
        1394                 :                   "Must only pass TRANSITION_EMBED visits to this!");
        1395               8 :   NS_PRECONDITION(NS_IsMainThread(), "Must be called on the main thread!");
        1396                 :
        1397              16 :   nsCOMPtr<nsIURI> uri;
        1398               8 :   (void)NS_NewURI(getter_AddRefs(uri), aPlace.spec);
        1399                 :
        1400               8 :   nsNavHistory* navHistory = nsNavHistory::GetHistoryService();
        1401               8 :   if (!navHistory || !uri) {
        1402                 :     return;
        1403                 :   }
        1404                 :
        1405               8 :   navHistory->registerEmbedVisit(uri, aPlace.visitTime);
        1406                 :
        1407               8 :   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              16 :   VisitData noReferrer;
        1423              16 :   nsCOMPtr<nsIRunnable> event = new NotifyVisitObservers(aPlace, noReferrer);
        1424               8 :   (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             122 : 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             366 :     "changes to links' visited state.")
        1445                 :
        1446                 : } // anonymous namespace
        1447                 :
        1448                 : ////////////////////////////////////////////////////////////////////////////////
        1449                 : //// History
        1450                 :
        1451                 : History* History::gService = NULL;
        1452                 :
        1453             122 : History::History()
        1454                 :   : mShuttingDown(false)
        1455             122 :   , mShutdownMutex("History::mShutdownMutex")
        1456                 : {
        1457             122 :   NS_ASSERTION(!gService, "Ruh-roh!  This service has already been created!");
        1458             122 :   gService = this;
        1459                 :
        1460             244 :   nsCOMPtr<nsIObserverService> os = services::GetObserverService();
        1461             122 :   NS_WARN_IF_FALSE(os, "Observer service was not found!");
        1462             122 :   if (os) {
        1463             122 :     (void)os->AddObserver(this, TOPIC_PLACES_SHUTDOWN, false);
        1464                 :   }
        1465                 :
        1466             122 :   NS_RegisterMemoryReporter(new NS_MEMORY_REPORTER_NAME(HistoryService));
        1467             122 : }
        1468                 :
        1469             366 : History::~History()
        1470                 : {
        1471             122 :   gService = NULL;
        1472                 :
        1473                 : #ifdef DEBUG
        1474             122 :   if (mObservers.IsInitialized()) {
        1475               0 :     NS_ASSERTION(mObservers.Count() == 0,
        1476                 :                  "Not all Links were removed before we disappear!");
        1477                 :   }
        1478                 : #endif
        1479             488 : }
        1480                 :
        1481                 : void
        1482            2303 : History::NotifyVisited(nsIURI* aURI)
        1483                 : {
        1484            2303 :   NS_ASSERTION(aURI, "Ruh-roh!  A NULL URI was passed to us!");
        1485                 :
        1486            4606 :   nsAutoScriptBlocker scriptBlocker;
        1487                 :
        1488            2303 :   if (XRE_GetProcessType() == GeckoProcessType_Default) {
        1489            4606 :     nsTArray<ContentParent*> cplist;
        1490            2303 :     ContentParent::GetAll(cplist);
        1491            2303 :     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            2303 :   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               0 :   KeyClass* key = mObservers.GetEntry(aURI);
        1505               0 :   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               0 :     ObserverArray::ForwardIterator iter(key->array);
        1513               0 :     while (iter.HasMore()) {
        1514               0 :       Link* link = iter.GetNext();
        1515               0 :       link->SetLinkState(eLinkState_Visited);
        1516                 :       // Verify that the observers hash doesn't mutate while looping through
        1517                 :       // the links associated with this URI.
        1518               0 :       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               0 :   mObservers.RemoveEntry(aURI);
        1525                 : }
        1526                 :
        1527                 : mozIStorageAsyncStatement*
        1528             332 : History::GetIsVisitedStatement()
        1529                 : {
        1530             332 :   if (mIsVisitedStatement) {
        1531             331 :     return mIsVisitedStatement;
        1532                 :   }
        1533                 :
        1534                 :   // If we don't yet have a database connection, go ahead and clone it now.
        1535               1 :   if (!mReadOnlyDBConn) {
        1536               1 :     mozIStorageConnection* dbConn = GetDBConn();
        1537               1 :     NS_ENSURE_TRUE(dbConn, nsnull);
        1538                 :
        1539               1 :     (void)dbConn->Clone(true, getter_AddRefs(mReadOnlyDBConn));
        1540               1 :     NS_ENSURE_TRUE(mReadOnlyDBConn, nsnull);
        1541                 :   }
        1542                 :
        1543                 :   // Now we can create our cached statement.
        1544               2 :   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               2 :   ),  getter_AddRefs(mIsVisitedStatement));
        1550               1 :   NS_ENSURE_SUCCESS(rv, nsnull);
        1551               1 :   return mIsVisitedStatement;
        1552                 : }
        1553                 :
        1554                 : nsresult
        1555             449 : History::InsertPlace(const VisitData& aPlace)
        1556                 : {
        1557             449 :   NS_PRECONDITION(aPlace.placeId == 0, "should not have a valid place id!");
        1558             449 :   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             898 :     );
        1565             449 :   NS_ENSURE_STATE(stmt);
        1566             898 :   mozStorageStatementScoper scoper(stmt);
        1567                 :
        1568             898 :   nsresult rv = stmt->BindStringByName(NS_LITERAL_CSTRING("rev_host"),
        1569             449 :                                        aPlace.revHost);
        1570             449 :   NS_ENSURE_SUCCESS(rv, rv);
        1571             449 :   rv = URIBinder::Bind(stmt, NS_LITERAL_CSTRING("url"), aPlace.spec);
        1572             449 :   NS_ENSURE_SUCCESS(rv, rv);
        1573                 :   // Empty strings should have no title, just like nsNavHistory::SetPageTitle.
        1574             449 :   if (aPlace.title.IsEmpty()) {
        1575              71 :     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             449 :   NS_ENSURE_SUCCESS(rv, rv);
        1582             449 :   rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("typed"), aPlace.typed);
        1583             449 :   NS_ENSURE_SUCCESS(rv, rv);
        1584             449 :   rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("hidden"), aPlace.hidden);
        1585             449 :   NS_ENSURE_SUCCESS(rv, rv);
        1586             898 :   nsCAutoString guid(aPlace.guid);
        1587             449 :   if (aPlace.guid.IsVoid()) {
        1588             205 :     rv = GenerateGUID(guid);
        1589             205 :     NS_ENSURE_SUCCESS(rv, rv);
        1590                 :   }
        1591             449 :   rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("guid"), guid);
        1592             449 :   NS_ENSURE_SUCCESS(rv, rv);
        1593             449 :   rv = stmt->Execute();
        1594             449 :   NS_ENSURE_SUCCESS(rv, rv);
        1595                 :
        1596             448 :   return NS_OK;
        1597                 : }
        1598                 :
        1599                 : nsresult
        1600              17 : History::UpdatePlace(const VisitData& aPlace)
        1601                 : {
        1602              17 :   NS_PRECONDITION(!NS_IsMainThread(), "must be called off of the main thread!");
        1603              17 :   NS_PRECONDITION(aPlace.placeId > 0, "must have a valid place id!");
        1604              17 :   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              34 :     );
        1614              17 :   NS_ENSURE_STATE(stmt);
        1615              34 :   mozStorageStatementScoper scoper(stmt);
        1616                 :
        1617                 :   nsresult rv;
        1618                 :   // Empty strings should clear the title, just like nsNavHistory::SetPageTitle.
        1619              17 :   if (aPlace.title.IsEmpty()) {
        1620               6 :     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              17 :   NS_ENSURE_SUCCESS(rv, rv);
        1627              17 :   rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("typed"), aPlace.typed);
        1628              17 :   NS_ENSURE_SUCCESS(rv, rv);
        1629              17 :   rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("hidden"), aPlace.hidden);
        1630              17 :   NS_ENSURE_SUCCESS(rv, rv);
        1631              17 :   rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("guid"), aPlace.guid);
        1632              17 :   NS_ENSURE_SUCCESS(rv, rv);
        1633              34 :   rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("page_id"),
        1634              17 :                              aPlace.placeId);
        1635              17 :   NS_ENSURE_SUCCESS(rv, rv);
        1636              17 :   rv = stmt->Execute();
        1637              17 :   NS_ENSURE_SUCCESS(rv, rv);
        1638                 :
        1639              17 :   return NS_OK;
        1640                 : }
        1641                 :
        1642                 : bool
        1643             922 : History::FetchPageInfo(VisitData& _place)
        1644                 : {
        1645             922 :   NS_PRECONDITION(!_place.spec.IsEmpty(), "must have a non-empty spec!");
        1646             922 :   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            1844 :     );
        1653             922 :   NS_ENSURE_TRUE(stmt, false);
        1654            1844 :   mozStorageStatementScoper scoper(stmt);
        1655                 :
        1656             922 :   nsresult rv = URIBinder::Bind(stmt, NS_LITERAL_CSTRING("page_url"),
        1657             922 :                                 _place.spec);
        1658             922 :   NS_ENSURE_SUCCESS(rv, false);
        1659                 :
        1660                 :   bool hasResult;
        1661             922 :   rv = stmt->ExecuteStep(&hasResult);
        1662             922 :   NS_ENSURE_SUCCESS(rv, false);
        1663             922 :   if (!hasResult) {
        1664             449 :     return false;
        1665                 :   }
        1666                 :
        1667             473 :   rv = stmt->GetInt64(0, &_place.placeId);
        1668             473 :   NS_ENSURE_SUCCESS(rv, false);
        1669                 :
        1670             946 :   nsAutoString title;
        1671             473 :   rv = stmt->GetString(1, title);
        1672             473 :   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             473 :   if (_place.title.IsVoid()) {
        1679              73 :     _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             473 :   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             473 :   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             460 :     rv = stmt->GetInt32(3, &typed);
        1702             460 :     _place.typed = !!typed;
        1703             460 :     NS_ENSURE_SUCCESS(rv, true);
        1704                 :   }
        1705                 :
        1706             473 :   if (_place.guid.IsVoid()) {
        1707             229 :     rv = stmt->GetUTF8String(4, _place.guid);
        1708             229 :     NS_ENSURE_SUCCESS(rv, true);
        1709                 :   }
        1710                 :
        1711             473 :   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            3255 : History::GetService()
        1730                 : {
        1731            3255 :   if (gService) {
        1732            3153 :     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             122 : History::GetSingleton()
        1745                 : {
        1746             122 :   if (!gService) {
        1747             122 :     gService = new History();
        1748             122 :     NS_ENSURE_TRUE(gService, nsnull);
        1749                 :   }
        1750                 :
        1751             122 :   NS_ADDREF(gService);
        1752             122 :   return gService;
        1753                 : }
        1754                 :
        1755                 : mozIStorageConnection*
        1756            3488 : History::GetDBConn()
        1757                 : {
        1758            3488 :   if (!mDB) {
        1759              21 :     mDB = Database::GetDatabase();
        1760              21 :     NS_ENSURE_TRUE(mDB, nsnull);
        1761                 :   }
        1762            3488 :   return mDB->MainConn();
        1763                 : }
        1764                 :
        1765                 : void
        1766             122 : History::Shutdown()
        1767                 : {
        1768             122 :   MOZ_ASSERT(NS_IsMainThread());
        1769                 :
        1770                 :   // Prevent other threads from scheduling uses of the DB while we mark
        1771                 :   // ourselves as shutting down.
        1772             244 :   MutexAutoLock lockedScope(mShutdownMutex);
        1773             122 :   MOZ_ASSERT(!mShuttingDown && "Shutdown was called more than once!");
        1774                 :
        1775             122 :   mShuttingDown = true;
        1776                 :
        1777             122 :   if (mReadOnlyDBConn) {
        1778               1 :     if (mIsVisitedStatement) {
        1779               1 :       (void)mIsVisitedStatement->Finalize();
        1780                 :     }
        1781               1 :     (void)mReadOnlyDBConn->AsyncClose(nsnull);
        1782                 :   }
        1783             122 : }
        1784                 :
        1785                 : ////////////////////////////////////////////////////////////////////////////////
        1786                 : //// IHistory
        1787                 :
        1788                 : NS_IMETHODIMP
        1789               0 : History::VisitURI(nsIURI* aURI,
        1790                 :                   nsIURI* aLastVisitedURI,
        1791                 :                   PRUint32 aFlags)
        1792                 : {
        1793               0 :   NS_PRECONDITION(aURI, "URI should not be NULL.");
        1794               0 :   if (mShuttingDown) {
        1795               0 :     return NS_OK;
        1796                 :   }
        1797                 :
        1798               0 :   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               0 :   nsNavHistory* navHistory = nsNavHistory::GetHistoryService();
        1807               0 :   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               0 :   nsresult rv = navHistory->CanAddURI(aURI, &canAdd);
        1812               0 :   NS_ENSURE_SUCCESS(rv, rv);
        1813               0 :   if (!canAdd) {
        1814               0 :     return NS_OK;
        1815                 :   }
        1816                 :
        1817               0 :   if (aLastVisitedURI) {
        1818                 :     bool same;
        1819               0 :     rv = aURI->Equals(aLastVisitedURI, &same);
        1820               0 :     NS_ENSURE_SUCCESS(rv, rv);
        1821               0 :     if (same) {
        1822                 :       // Do not save refresh-page visits.
        1823               0 :       return NS_OK;
        1824                 :     }
        1825                 :   }
        1826                 :
        1827               0 :   nsTArray<VisitData> placeArray(1);
        1828               0 :   NS_ENSURE_TRUE(placeArray.AppendElement(VisitData(aURI, aLastVisitedURI)),
        1829                 :                  NS_ERROR_OUT_OF_MEMORY);
        1830               0 :   VisitData& place = placeArray.ElementAt(0);
        1831               0 :   NS_ENSURE_FALSE(place.spec.IsEmpty(), NS_ERROR_INVALID_ARG);
        1832                 :
        1833               0 :   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               0 :   PRUint32 recentFlags = navHistory->GetRecentFlags(aURI);
        1838               0 :   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               0 :   if (!(aFlags & IHistory::TOP_LEVEL) && !isFollowedLink) {
        1847                 :     // A frame redirected to a new site without user interaction.
        1848               0 :     place.SetTransitionType(nsINavHistoryService::TRANSITION_EMBED);
        1849                 :   }
        1850               0 :   else if (aFlags & IHistory::REDIRECT_TEMPORARY) {
        1851               0 :     place.SetTransitionType(nsINavHistoryService::TRANSITION_REDIRECT_TEMPORARY);
        1852                 :   }
        1853               0 :   else if (aFlags & IHistory::REDIRECT_PERMANENT) {
        1854               0 :     place.SetTransitionType(nsINavHistoryService::TRANSITION_REDIRECT_PERMANENT);
        1855                 :   }
        1856               0 :   else if (recentFlags & nsNavHistory::RECENT_TYPED) {
        1857               0 :     place.SetTransitionType(nsINavHistoryService::TRANSITION_TYPED);
        1858                 :   }
        1859               0 :   else if (recentFlags & nsNavHistory::RECENT_BOOKMARKED) {
        1860               0 :     place.SetTransitionType(nsINavHistoryService::TRANSITION_BOOKMARK);
        1861                 :   }
        1862               0 :   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               0 :     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               0 :   if (place.transitionType == nsINavHistoryService::TRANSITION_EMBED) {
        1874               0 :     StoreAndNotifyEmbedVisit(place);
        1875                 :   }
        1876                 :   else {
        1877               0 :     mozIStorageConnection* dbConn = GetDBConn();
        1878               0 :     NS_ENSURE_STATE(dbConn);
        1879                 :
        1880               0 :     rv = InsertVisitedURIs::Start(dbConn, placeArray);
        1881               0 :     NS_ENSURE_SUCCESS(rv, rv);
        1882                 :   }
        1883                 :
        1884                 :   // Finally, notify that we've been visited.
        1885                 :   nsCOMPtr<nsIObserverService> obsService =
        1886               0 :     mozilla::services::GetObserverService();
        1887               0 :   if (obsService) {
        1888               0 :     obsService->NotifyObservers(aURI, NS_LINK_VISITED_EVENT_TOPIC, nsnull);
        1889                 :   }
        1890                 :
        1891               0 :   return NS_OK;
        1892                 : }
        1893                 :
        1894                 : NS_IMETHODIMP
        1895               0 : History::RegisterVisitedCallback(nsIURI* aURI,
        1896                 :                                  Link* aLink)
        1897                 : {
        1898               0 :   NS_ASSERTION(aURI, "Must pass a non-null URI!");
        1899               0 :   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               0 :   if (!mObservers.IsInitialized()) {
        1905               0 :     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               0 :   bool keyAlreadyExists = !!mObservers.GetEntry(aURI);
        1912                 : #endif
        1913               0 :   KeyClass* key = mObservers.PutEntry(aURI);
        1914               0 :   NS_ENSURE_TRUE(key, NS_ERROR_OUT_OF_MEMORY);
        1915               0 :   ObserverArray& observers = key->array;
        1916                 :
        1917               0 :   if (observers.IsEmpty()) {
        1918               0 :     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               0 :     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               0 :     if (NS_FAILED(rv) || !aLink) {
        1931                 :       // Remove our array from the hashtable so we don't keep it around.
        1932               0 :       mObservers.RemoveEntry(aURI);
        1933               0 :       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               0 :   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               0 :   NS_ASSERTION(!observers.Contains(aLink),
        1948                 :                "Already tracking this Link object!");
        1949                 :
        1950                 :   // Start tracking our Link.
        1951               0 :   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               0 :   return NS_OK;
        1958                 : }
        1959                 :
        1960                 : NS_IMETHODIMP
        1961               0 : History::UnregisterVisitedCallback(nsIURI* aURI,
        1962                 :                                    Link* aLink)
        1963                 : {
        1964               0 :   NS_ASSERTION(aURI, "Must pass a non-null URI!");
        1965               0 :   NS_ASSERTION(aLink, "Must pass a non-null Link object!");
        1966                 :
        1967                 :   // Get the array, and remove the item from it.
        1968               0 :   KeyClass* key = mObservers.GetEntry(aURI);
        1969               0 :   if (!key) {
        1970               0 :     NS_ERROR("Trying to unregister for a URI that wasn't registered!");
        1971               0 :     return NS_ERROR_UNEXPECTED;
        1972                 :   }
        1973               0 :   ObserverArray& observers = key->array;
        1974               0 :   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               0 :   if (observers.IsEmpty()) {
        1981               0 :     mObservers.RemoveEntry(aURI);
        1982                 :   }
        1983                 :
        1984               0 :   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             122 : History::Observe(nsISupports* aSubject, const char* aTopic,
        2275                 :                  const PRUnichar* aData)
        2276                 : {
        2277             122 :   if (strcmp(aTopic, TOPIC_PLACES_SHUTDOWN) == 0) {
        2278             122 :     Shutdown();
        2279                 :
        2280             244 :     nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
        2281             122 :     if (os) {
        2282             122 :       (void)os->RemoveObserver(this, TOPIC_PLACES_SHUTDOWN);
        2283                 :     }
        2284                 :   }
        2285                 :
        2286             122 :   return NS_OK;
        2287                 : }
        2288                 :
        2289                 : ////////////////////////////////////////////////////////////////////////////////
        2290                 : //// nsISupports
        2291                 :
        2292            3888 : 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