1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/toolkit/components/places/nsNavHistoryResult.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,4743 @@ 1.4 +//* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#include <stdio.h> 1.10 +#include "nsNavHistory.h" 1.11 +#include "nsNavBookmarks.h" 1.12 +#include "nsFaviconService.h" 1.13 +#include "nsITaggingService.h" 1.14 +#include "nsAnnotationService.h" 1.15 +#include "Helpers.h" 1.16 +#include "mozilla/DebugOnly.h" 1.17 +#include "nsDebug.h" 1.18 +#include "nsNetUtil.h" 1.19 +#include "nsString.h" 1.20 +#include "nsReadableUtils.h" 1.21 +#include "nsUnicharUtils.h" 1.22 +#include "prtime.h" 1.23 +#include "prprf.h" 1.24 + 1.25 +#include "nsCycleCollectionParticipant.h" 1.26 + 1.27 +// Thanks, Windows.h :( 1.28 +#undef CompareString 1.29 + 1.30 +#define TO_ICONTAINER(_node) \ 1.31 + static_cast<nsINavHistoryContainerResultNode*>(_node) 1.32 + 1.33 +#define TO_CONTAINER(_node) \ 1.34 + static_cast<nsNavHistoryContainerResultNode*>(_node) 1.35 + 1.36 +#define NOTIFY_RESULT_OBSERVERS_RET(_result, _method, _ret) \ 1.37 + PR_BEGIN_MACRO \ 1.38 + NS_ENSURE_TRUE(_result, _ret); \ 1.39 + if (!_result->mSuppressNotifications) { \ 1.40 + ENUMERATE_WEAKARRAY(_result->mObservers, nsINavHistoryResultObserver, \ 1.41 + _method) \ 1.42 + } \ 1.43 + PR_END_MACRO 1.44 + 1.45 +#define NOTIFY_RESULT_OBSERVERS(_result, _method) \ 1.46 + NOTIFY_RESULT_OBSERVERS_RET(_result, _method, NS_ERROR_UNEXPECTED) 1.47 + 1.48 +// What we want is: NS_INTERFACE_MAP_ENTRY(self) for static IID accessors, 1.49 +// but some of our classes (like nsNavHistoryResult) have an ambiguous base 1.50 +// class of nsISupports which prevents this from working (the default macro 1.51 +// converts it to nsISupports, then addrefs it, then returns it). Therefore, we 1.52 +// expand the macro here and change it so that it works. Yuck. 1.53 +#define NS_INTERFACE_MAP_STATIC_AMBIGUOUS(_class) \ 1.54 + if (aIID.Equals(NS_GET_IID(_class))) { \ 1.55 + NS_ADDREF(this); \ 1.56 + *aInstancePtr = this; \ 1.57 + return NS_OK; \ 1.58 + } else 1.59 + 1.60 +// Number of changes to handle separately in a batch. If more changes are 1.61 +// requested the node will switch to full refresh mode. 1.62 +#define MAX_BATCH_CHANGES_BEFORE_REFRESH 5 1.63 + 1.64 +// Emulate string comparison (used for sorting) for PRTime and int. 1.65 +inline int32_t ComparePRTime(PRTime a, PRTime b) 1.66 +{ 1.67 + if (a < b) 1.68 + return -1; 1.69 + else if (a > b) 1.70 + return 1; 1.71 + return 0; 1.72 +} 1.73 +inline int32_t CompareIntegers(uint32_t a, uint32_t b) 1.74 +{ 1.75 + return a - b; 1.76 +} 1.77 + 1.78 +using namespace mozilla; 1.79 +using namespace mozilla::places; 1.80 + 1.81 +NS_IMPL_CYCLE_COLLECTION(nsNavHistoryResultNode, mParent) 1.82 + 1.83 +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsNavHistoryResultNode) 1.84 + NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsINavHistoryResultNode) 1.85 + NS_INTERFACE_MAP_ENTRY(nsINavHistoryResultNode) 1.86 +NS_INTERFACE_MAP_END 1.87 + 1.88 +NS_IMPL_CYCLE_COLLECTING_ADDREF(nsNavHistoryResultNode) 1.89 +NS_IMPL_CYCLE_COLLECTING_RELEASE(nsNavHistoryResultNode) 1.90 + 1.91 +nsNavHistoryResultNode::nsNavHistoryResultNode( 1.92 + const nsACString& aURI, const nsACString& aTitle, uint32_t aAccessCount, 1.93 + PRTime aTime, const nsACString& aIconURI) : 1.94 + mParent(nullptr), 1.95 + mURI(aURI), 1.96 + mTitle(aTitle), 1.97 + mAreTagsSorted(false), 1.98 + mAccessCount(aAccessCount), 1.99 + mTime(aTime), 1.100 + mFaviconURI(aIconURI), 1.101 + mBookmarkIndex(-1), 1.102 + mItemId(-1), 1.103 + mFolderId(-1), 1.104 + mDateAdded(0), 1.105 + mLastModified(0), 1.106 + mIndentLevel(-1), 1.107 + mFrecency(0), 1.108 + mHidden(false), 1.109 + mTransitionType(0) 1.110 +{ 1.111 + mTags.SetIsVoid(true); 1.112 +} 1.113 + 1.114 + 1.115 +NS_IMETHODIMP 1.116 +nsNavHistoryResultNode::GetIcon(nsACString& aIcon) 1.117 +{ 1.118 + if (mFaviconURI.IsEmpty()) { 1.119 + aIcon.Truncate(); 1.120 + return NS_OK; 1.121 + } 1.122 + 1.123 + nsFaviconService* faviconService = nsFaviconService::GetFaviconService(); 1.124 + NS_ENSURE_TRUE(faviconService, NS_ERROR_OUT_OF_MEMORY); 1.125 + faviconService->GetFaviconSpecForIconString(mFaviconURI, aIcon); 1.126 + return NS_OK; 1.127 +} 1.128 + 1.129 + 1.130 +NS_IMETHODIMP 1.131 +nsNavHistoryResultNode::GetParent(nsINavHistoryContainerResultNode** aParent) 1.132 +{ 1.133 + NS_IF_ADDREF(*aParent = mParent); 1.134 + return NS_OK; 1.135 +} 1.136 + 1.137 + 1.138 +NS_IMETHODIMP 1.139 +nsNavHistoryResultNode::GetParentResult(nsINavHistoryResult** aResult) 1.140 +{ 1.141 + *aResult = nullptr; 1.142 + if (IsContainer()) 1.143 + NS_IF_ADDREF(*aResult = GetAsContainer()->mResult); 1.144 + else if (mParent) 1.145 + NS_IF_ADDREF(*aResult = mParent->mResult); 1.146 + 1.147 + NS_ENSURE_STATE(*aResult); 1.148 + return NS_OK; 1.149 +} 1.150 + 1.151 + 1.152 +NS_IMETHODIMP 1.153 +nsNavHistoryResultNode::GetTags(nsAString& aTags) { 1.154 + // Only URI-nodes may be associated with tags 1.155 + if (!IsURI()) { 1.156 + aTags.Truncate(); 1.157 + return NS_OK; 1.158 + } 1.159 + 1.160 + // Initially, the tags string is set to a void string (see constructor). We 1.161 + // then build it the first time this method called is called (and by that, 1.162 + // implicitly unset the void flag). Result observers may re-set the void flag 1.163 + // in order to force rebuilding of the tags string. 1.164 + if (!mTags.IsVoid()) { 1.165 + // If mTags is assigned by a history query it is unsorted for performance 1.166 + // reasons, it must be sorted by name on first read access. 1.167 + if (!mAreTagsSorted) { 1.168 + nsTArray<nsCString> tags; 1.169 + ParseString(NS_ConvertUTF16toUTF8(mTags), ',', tags); 1.170 + tags.Sort(); 1.171 + mTags.SetIsVoid(true); 1.172 + for (nsTArray<nsCString>::index_type i = 0; i < tags.Length(); ++i) { 1.173 + AppendUTF8toUTF16(tags[i], mTags); 1.174 + if (i < tags.Length() - 1 ) 1.175 + mTags.AppendLiteral(", "); 1.176 + } 1.177 + mAreTagsSorted = true; 1.178 + } 1.179 + aTags.Assign(mTags); 1.180 + return NS_OK; 1.181 + } 1.182 + 1.183 + // Fetch the tags 1.184 + nsRefPtr<Database> DB = Database::GetDatabase(); 1.185 + NS_ENSURE_STATE(DB); 1.186 + nsCOMPtr<mozIStorageStatement> stmt = DB->GetStatement( 1.187 + "/* do not warn (bug 487594) */ " 1.188 + "SELECT GROUP_CONCAT(tag_title, ', ') " 1.189 + "FROM ( " 1.190 + "SELECT t.title AS tag_title " 1.191 + "FROM moz_bookmarks b " 1.192 + "JOIN moz_bookmarks t ON t.id = +b.parent " 1.193 + "WHERE b.fk = (SELECT id FROM moz_places WHERE url = :page_url) " 1.194 + "AND t.parent = :tags_folder " 1.195 + "ORDER BY t.title COLLATE NOCASE ASC " 1.196 + ") " 1.197 + ); 1.198 + NS_ENSURE_STATE(stmt); 1.199 + mozStorageStatementScoper scoper(stmt); 1.200 + 1.201 + nsNavHistory* history = nsNavHistory::GetHistoryService(); 1.202 + NS_ENSURE_STATE(history); 1.203 + nsresult rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("tags_folder"), 1.204 + history->GetTagsFolder()); 1.205 + NS_ENSURE_SUCCESS(rv, rv); 1.206 + rv = URIBinder::Bind(stmt, NS_LITERAL_CSTRING("page_url"), mURI); 1.207 + NS_ENSURE_SUCCESS(rv, rv); 1.208 + 1.209 + bool hasTags = false; 1.210 + if (NS_SUCCEEDED(stmt->ExecuteStep(&hasTags)) && hasTags) { 1.211 + rv = stmt->GetString(0, mTags); 1.212 + NS_ENSURE_SUCCESS(rv, rv); 1.213 + aTags.Assign(mTags); 1.214 + mAreTagsSorted = true; 1.215 + } 1.216 + 1.217 + // If this node is a child of a history query, we need to make sure changes 1.218 + // to tags are properly live-updated. 1.219 + if (mParent && mParent->IsQuery() && 1.220 + mParent->mOptions->QueryType() == nsINavHistoryQueryOptions::QUERY_TYPE_HISTORY) { 1.221 + nsNavHistoryQueryResultNode* query = mParent->GetAsQuery(); 1.222 + nsNavHistoryResult* result = query->GetResult(); 1.223 + NS_ENSURE_STATE(result); 1.224 + result->AddAllBookmarksObserver(query); 1.225 + } 1.226 + 1.227 + return NS_OK; 1.228 +} 1.229 + 1.230 +NS_IMETHODIMP 1.231 +nsNavHistoryResultNode::GetPageGuid(nsACString& aPageGuid) { 1.232 + aPageGuid = mPageGuid; 1.233 + return NS_OK; 1.234 +} 1.235 + 1.236 + 1.237 +NS_IMETHODIMP 1.238 +nsNavHistoryResultNode::GetBookmarkGuid(nsACString& aBookmarkGuid) { 1.239 + aBookmarkGuid = mBookmarkGuid; 1.240 + return NS_OK; 1.241 +} 1.242 + 1.243 + 1.244 +void 1.245 +nsNavHistoryResultNode::OnRemoving() 1.246 +{ 1.247 + mParent = nullptr; 1.248 +} 1.249 + 1.250 + 1.251 +/** 1.252 + * This will find the result for this node. We can ask the nearest container 1.253 + * for this value (either ourselves or our parents should be a container, 1.254 + * and all containers have result pointers). 1.255 + * 1.256 + * @note The result may be null, if the container is detached from the result 1.257 + * who owns it. 1.258 + */ 1.259 +nsNavHistoryResult* 1.260 +nsNavHistoryResultNode::GetResult() 1.261 +{ 1.262 + nsNavHistoryResultNode* node = this; 1.263 + do { 1.264 + if (node->IsContainer()) { 1.265 + nsNavHistoryContainerResultNode* container = TO_CONTAINER(node); 1.266 + return container->mResult; 1.267 + } 1.268 + node = node->mParent; 1.269 + } while (node); 1.270 + MOZ_ASSERT(false, "No container node found in hierarchy!"); 1.271 + return nullptr; 1.272 +} 1.273 + 1.274 + 1.275 +/** 1.276 + * Searches up the tree for the closest ancestor node that has an options 1.277 + * structure. This will tell us the options that were used to generate this 1.278 + * node. 1.279 + * 1.280 + * Be careful, this function walks up the tree, so it can not be used when 1.281 + * result nodes are created because they have no parent. Only call this 1.282 + * function after the tree has been built. 1.283 + */ 1.284 +nsNavHistoryQueryOptions* 1.285 +nsNavHistoryResultNode::GetGeneratingOptions() 1.286 +{ 1.287 + if (!mParent) { 1.288 + // When we have no parent, it either means we haven't built the tree yet, 1.289 + // in which case calling this function is a bug, or this node is the root 1.290 + // of the tree. When we are the root of the tree, our own options are the 1.291 + // generating options. 1.292 + if (IsContainer()) 1.293 + return GetAsContainer()->mOptions; 1.294 + 1.295 + NS_NOTREACHED("Can't find a generating node for this container, perhaps FillStats has not been called on this tree yet?"); 1.296 + return nullptr; 1.297 + } 1.298 + 1.299 + // Look up the tree. We want the options that were used to create this node, 1.300 + // and since it has a parent, it's the options of an ancestor, not of the node 1.301 + // itself. So start at the parent. 1.302 + nsNavHistoryContainerResultNode* cur = mParent; 1.303 + while (cur) { 1.304 + if (cur->IsContainer()) 1.305 + return cur->GetAsContainer()->mOptions; 1.306 + cur = cur->mParent; 1.307 + } 1.308 + 1.309 + // We should always find a container node as an ancestor. 1.310 + NS_NOTREACHED("Can't find a generating node for this container, the tree seemes corrupted."); 1.311 + return nullptr; 1.312 +} 1.313 + 1.314 +NS_IMPL_CYCLE_COLLECTION_INHERITED(nsNavHistoryContainerResultNode, nsNavHistoryResultNode, 1.315 + mResult, 1.316 + mChildren) 1.317 + 1.318 +NS_IMPL_ADDREF_INHERITED(nsNavHistoryContainerResultNode, nsNavHistoryResultNode) 1.319 +NS_IMPL_RELEASE_INHERITED(nsNavHistoryContainerResultNode, nsNavHistoryResultNode) 1.320 + 1.321 +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(nsNavHistoryContainerResultNode) 1.322 + NS_INTERFACE_MAP_STATIC_AMBIGUOUS(nsNavHistoryContainerResultNode) 1.323 + NS_INTERFACE_MAP_ENTRY(nsINavHistoryContainerResultNode) 1.324 +NS_INTERFACE_MAP_END_INHERITING(nsNavHistoryResultNode) 1.325 + 1.326 +nsNavHistoryContainerResultNode::nsNavHistoryContainerResultNode( 1.327 + const nsACString& aURI, const nsACString& aTitle, 1.328 + const nsACString& aIconURI, uint32_t aContainerType, bool aReadOnly, 1.329 + nsNavHistoryQueryOptions* aOptions) : 1.330 + nsNavHistoryResultNode(aURI, aTitle, 0, 0, aIconURI), 1.331 + mResult(nullptr), 1.332 + mContainerType(aContainerType), 1.333 + mExpanded(false), 1.334 + mChildrenReadOnly(aReadOnly), 1.335 + mOptions(aOptions), 1.336 + mAsyncCanceledState(NOT_CANCELED) 1.337 +{ 1.338 +} 1.339 + 1.340 +nsNavHistoryContainerResultNode::nsNavHistoryContainerResultNode( 1.341 + const nsACString& aURI, const nsACString& aTitle, 1.342 + PRTime aTime, 1.343 + const nsACString& aIconURI, uint32_t aContainerType, bool aReadOnly, 1.344 + nsNavHistoryQueryOptions* aOptions) : 1.345 + nsNavHistoryResultNode(aURI, aTitle, 0, aTime, aIconURI), 1.346 + mResult(nullptr), 1.347 + mContainerType(aContainerType), 1.348 + mExpanded(false), 1.349 + mChildrenReadOnly(aReadOnly), 1.350 + mOptions(aOptions), 1.351 + mAsyncCanceledState(NOT_CANCELED) 1.352 +{ 1.353 +} 1.354 + 1.355 + 1.356 +nsNavHistoryContainerResultNode::~nsNavHistoryContainerResultNode() 1.357 +{ 1.358 + // Explicitly clean up array of children of this container. We must ensure 1.359 + // all references are gone and all of their destructors are called. 1.360 + mChildren.Clear(); 1.361 +} 1.362 + 1.363 + 1.364 +/** 1.365 + * Containers should notify their children that they are being removed when the 1.366 + * container is being removed. 1.367 + */ 1.368 +void 1.369 +nsNavHistoryContainerResultNode::OnRemoving() 1.370 +{ 1.371 + nsNavHistoryResultNode::OnRemoving(); 1.372 + for (int32_t i = 0; i < mChildren.Count(); ++i) 1.373 + mChildren[i]->OnRemoving(); 1.374 + mChildren.Clear(); 1.375 + mResult = nullptr; 1.376 +} 1.377 + 1.378 + 1.379 +bool 1.380 +nsNavHistoryContainerResultNode::AreChildrenVisible() 1.381 +{ 1.382 + nsNavHistoryResult* result = GetResult(); 1.383 + if (!result) { 1.384 + NS_NOTREACHED("Invalid result"); 1.385 + return false; 1.386 + } 1.387 + 1.388 + if (!mExpanded) 1.389 + return false; 1.390 + 1.391 + // Now check if any ancestor is closed. 1.392 + nsNavHistoryContainerResultNode* ancestor = mParent; 1.393 + while (ancestor) { 1.394 + if (!ancestor->mExpanded) 1.395 + return false; 1.396 + 1.397 + ancestor = ancestor->mParent; 1.398 + } 1.399 + 1.400 + return true; 1.401 +} 1.402 + 1.403 + 1.404 +NS_IMETHODIMP 1.405 +nsNavHistoryContainerResultNode::GetContainerOpen(bool *aContainerOpen) 1.406 +{ 1.407 + *aContainerOpen = mExpanded; 1.408 + return NS_OK; 1.409 +} 1.410 + 1.411 + 1.412 +NS_IMETHODIMP 1.413 +nsNavHistoryContainerResultNode::SetContainerOpen(bool aContainerOpen) 1.414 +{ 1.415 + if (aContainerOpen) { 1.416 + if (!mExpanded) { 1.417 + nsNavHistoryQueryOptions* options = GetGeneratingOptions(); 1.418 + if (options && options->AsyncEnabled()) 1.419 + OpenContainerAsync(); 1.420 + else 1.421 + OpenContainer(); 1.422 + } 1.423 + } 1.424 + else { 1.425 + if (mExpanded) 1.426 + CloseContainer(); 1.427 + else if (mAsyncPendingStmt) 1.428 + CancelAsyncOpen(false); 1.429 + } 1.430 + 1.431 + return NS_OK; 1.432 +} 1.433 + 1.434 + 1.435 +/** 1.436 + * Notifies the result's observers of a change in the container's state. The 1.437 + * notification includes both the old and new states: The old is aOldState, and 1.438 + * the new is the container's current state. 1.439 + * 1.440 + * @param aOldState 1.441 + * The state being transitioned out of. 1.442 + */ 1.443 +nsresult 1.444 +nsNavHistoryContainerResultNode::NotifyOnStateChange(uint16_t aOldState) 1.445 +{ 1.446 + nsNavHistoryResult* result = GetResult(); 1.447 + NS_ENSURE_STATE(result); 1.448 + 1.449 + nsresult rv; 1.450 + uint16_t currState; 1.451 + rv = GetState(&currState); 1.452 + NS_ENSURE_SUCCESS(rv, rv); 1.453 + 1.454 + // Notify via the new ContainerStateChanged observer method. 1.455 + NOTIFY_RESULT_OBSERVERS(result, 1.456 + ContainerStateChanged(this, aOldState, currState)); 1.457 + return NS_OK; 1.458 +} 1.459 + 1.460 + 1.461 +NS_IMETHODIMP 1.462 +nsNavHistoryContainerResultNode::GetState(uint16_t* _state) 1.463 +{ 1.464 + NS_ENSURE_ARG_POINTER(_state); 1.465 + 1.466 + *_state = mExpanded ? (uint16_t)STATE_OPENED 1.467 + : mAsyncPendingStmt ? (uint16_t)STATE_LOADING 1.468 + : (uint16_t)STATE_CLOSED; 1.469 + 1.470 + return NS_OK; 1.471 +} 1.472 + 1.473 + 1.474 +/** 1.475 + * This handles the generic container case. Other container types should 1.476 + * override this to do their own handling. 1.477 + */ 1.478 +nsresult 1.479 +nsNavHistoryContainerResultNode::OpenContainer() 1.480 +{ 1.481 + NS_ASSERTION(!mExpanded, "Container must not be expanded to open it"); 1.482 + mExpanded = true; 1.483 + 1.484 + nsresult rv = NotifyOnStateChange(STATE_CLOSED); 1.485 + NS_ENSURE_SUCCESS(rv, rv); 1.486 + 1.487 + return NS_OK; 1.488 +} 1.489 + 1.490 + 1.491 +/** 1.492 + * Unset aSuppressNotifications to notify observers on this change. That is 1.493 + * the normal operation. This is set to false for the recursive calls since the 1.494 + * root container that is being closed will handle recomputation of the visible 1.495 + * elements for its entire subtree. 1.496 + */ 1.497 +nsresult 1.498 +nsNavHistoryContainerResultNode::CloseContainer(bool aSuppressNotifications) 1.499 +{ 1.500 + NS_ASSERTION((mExpanded && !mAsyncPendingStmt) || 1.501 + (!mExpanded && mAsyncPendingStmt), 1.502 + "Container must be expanded or loading to close it"); 1.503 + 1.504 + nsresult rv; 1.505 + uint16_t oldState; 1.506 + rv = GetState(&oldState); 1.507 + NS_ENSURE_SUCCESS(rv, rv); 1.508 + 1.509 + if (mExpanded) { 1.510 + // Recursively close all child containers. 1.511 + for (int32_t i = 0; i < mChildren.Count(); ++i) { 1.512 + if (mChildren[i]->IsContainer() && 1.513 + mChildren[i]->GetAsContainer()->mExpanded) 1.514 + mChildren[i]->GetAsContainer()->CloseContainer(true); 1.515 + } 1.516 + 1.517 + mExpanded = false; 1.518 + } 1.519 + 1.520 + // Be sure to set this to null before notifying observers. It signifies that 1.521 + // the container is no longer loading (if it was in the first place). 1.522 + mAsyncPendingStmt = nullptr; 1.523 + 1.524 + if (!aSuppressNotifications) { 1.525 + rv = NotifyOnStateChange(oldState); 1.526 + NS_ENSURE_SUCCESS(rv, rv); 1.527 + } 1.528 + 1.529 + // If this is the root container of a result, we can tell the result to stop 1.530 + // observing changes, otherwise the result will stay in memory and updates 1.531 + // itself till it is cycle collected. 1.532 + nsNavHistoryResult* result = GetResult(); 1.533 + NS_ENSURE_STATE(result); 1.534 + if (result->mRootNode == this) { 1.535 + result->StopObserving(); 1.536 + // When reopening this node its result will be out of sync. 1.537 + // We must clear our children to ensure we will call FillChildren 1.538 + // again in such a case. 1.539 + if (this->IsQuery()) 1.540 + this->GetAsQuery()->ClearChildren(true); 1.541 + else if (this->IsFolder()) 1.542 + this->GetAsFolder()->ClearChildren(true); 1.543 + } 1.544 + 1.545 + return NS_OK; 1.546 +} 1.547 + 1.548 + 1.549 +/** 1.550 + * The async version of OpenContainer. 1.551 + */ 1.552 +nsresult 1.553 +nsNavHistoryContainerResultNode::OpenContainerAsync() 1.554 +{ 1.555 + return NS_ERROR_NOT_IMPLEMENTED; 1.556 +} 1.557 + 1.558 + 1.559 +/** 1.560 + * Cancels the pending asynchronous Storage execution triggered by 1.561 + * FillChildrenAsync, if it exists. This method doesn't do much, because after 1.562 + * cancelation Storage will call this node's HandleCompletion callback, where 1.563 + * the real work is done. 1.564 + * 1.565 + * @param aRestart 1.566 + * If true, async execution will be restarted by HandleCompletion. 1.567 + */ 1.568 +void 1.569 +nsNavHistoryContainerResultNode::CancelAsyncOpen(bool aRestart) 1.570 +{ 1.571 + NS_ASSERTION(mAsyncPendingStmt, "Async execution canceled but not pending"); 1.572 + 1.573 + mAsyncCanceledState = aRestart ? CANCELED_RESTART_NEEDED : CANCELED; 1.574 + 1.575 + // Cancel will fail if the pending statement has already been canceled. 1.576 + // That's OK since this method may be called multiple times, and multiple 1.577 + // cancels don't harm anything. 1.578 + (void)mAsyncPendingStmt->Cancel(); 1.579 +} 1.580 + 1.581 + 1.582 +/** 1.583 + * This builds up tree statistics from the bottom up. Call with a container 1.584 + * and the indent level of that container. To init the full tree, call with 1.585 + * the root container. The default indent level is -1, which is appropriate 1.586 + * for the root level. 1.587 + * 1.588 + * CALL THIS AFTER FILLING ANY CONTAINER to update the parent and result node 1.589 + * pointers, even if you don't care about visit counts and last visit dates. 1.590 + */ 1.591 +void 1.592 +nsNavHistoryContainerResultNode::FillStats() 1.593 +{ 1.594 + uint32_t accessCount = 0; 1.595 + PRTime newTime = 0; 1.596 + 1.597 + for (int32_t i = 0; i < mChildren.Count(); ++i) { 1.598 + nsNavHistoryResultNode* node = mChildren[i]; 1.599 + node->mParent = this; 1.600 + node->mIndentLevel = mIndentLevel + 1; 1.601 + if (node->IsContainer()) { 1.602 + nsNavHistoryContainerResultNode* container = node->GetAsContainer(); 1.603 + container->mResult = mResult; 1.604 + container->FillStats(); 1.605 + } 1.606 + accessCount += node->mAccessCount; 1.607 + // this is how container nodes get sorted by date 1.608 + // The container gets the most recent time of the child nodes. 1.609 + if (node->mTime > newTime) 1.610 + newTime = node->mTime; 1.611 + } 1.612 + 1.613 + if (mExpanded) { 1.614 + mAccessCount = accessCount; 1.615 + if (!IsQuery() || newTime > mTime) 1.616 + mTime = newTime; 1.617 + } 1.618 +} 1.619 + 1.620 + 1.621 +/** 1.622 + * This is used when one container changes to do a minimal update of the tree 1.623 + * structure. When something changes, you want to call FillStats if necessary 1.624 + * and update this container completely. Then call this function which will 1.625 + * walk up the tree and fill in the previous containers. 1.626 + * 1.627 + * Note that you have to tell us by how much our access count changed. Our 1.628 + * access count should already be set to the new value; this is used tochange 1.629 + * the parents without having to re-count all their children. 1.630 + * 1.631 + * This does NOT update the last visit date downward. Therefore, if you are 1.632 + * deleting a node that has the most recent last visit date, the parents will 1.633 + * not get their last visit dates downshifted accordingly. This is a rather 1.634 + * unusual case: we don't often delete things, and we usually don't even show 1.635 + * the last visit date for folders. Updating would be slower because we would 1.636 + * have to recompute it from scratch. 1.637 + */ 1.638 +nsresult 1.639 +nsNavHistoryContainerResultNode::ReverseUpdateStats(int32_t aAccessCountChange) 1.640 +{ 1.641 + if (mParent) { 1.642 + nsNavHistoryResult* result = GetResult(); 1.643 + bool shouldNotify = result && mParent->mParent && 1.644 + mParent->mParent->AreChildrenVisible(); 1.645 + 1.646 + mParent->mAccessCount += aAccessCountChange; 1.647 + bool timeChanged = false; 1.648 + if (mTime > mParent->mTime) { 1.649 + timeChanged = true; 1.650 + mParent->mTime = mTime; 1.651 + } 1.652 + 1.653 + if (shouldNotify) { 1.654 + NOTIFY_RESULT_OBSERVERS(result, 1.655 + NodeHistoryDetailsChanged(TO_ICONTAINER(mParent), 1.656 + mParent->mTime, 1.657 + mParent->mAccessCount)); 1.658 + } 1.659 + 1.660 + // check sorting, the stats may have caused this node to move if the 1.661 + // sorting depended on something we are changing. 1.662 + uint16_t sortMode = mParent->GetSortType(); 1.663 + bool sortingByVisitCount = 1.664 + sortMode == nsINavHistoryQueryOptions::SORT_BY_VISITCOUNT_ASCENDING || 1.665 + sortMode == nsINavHistoryQueryOptions::SORT_BY_VISITCOUNT_DESCENDING; 1.666 + bool sortingByTime = 1.667 + sortMode == nsINavHistoryQueryOptions::SORT_BY_DATE_ASCENDING || 1.668 + sortMode == nsINavHistoryQueryOptions::SORT_BY_DATE_DESCENDING; 1.669 + 1.670 + if ((sortingByVisitCount && aAccessCountChange != 0) || 1.671 + (sortingByTime && timeChanged)) { 1.672 + int32_t ourIndex = mParent->FindChild(this); 1.673 + NS_ASSERTION(ourIndex >= 0, "Could not find self in parent"); 1.674 + if (ourIndex >= 0) 1.675 + EnsureItemPosition(static_cast<uint32_t>(ourIndex)); 1.676 + } 1.677 + 1.678 + nsresult rv = mParent->ReverseUpdateStats(aAccessCountChange); 1.679 + NS_ENSURE_SUCCESS(rv, rv); 1.680 + } 1.681 + 1.682 + return NS_OK; 1.683 +} 1.684 + 1.685 + 1.686 +/** 1.687 + * This walks up the tree until we find a query result node or the root to get 1.688 + * the sorting type. 1.689 + */ 1.690 +uint16_t 1.691 +nsNavHistoryContainerResultNode::GetSortType() 1.692 +{ 1.693 + if (mParent) 1.694 + return mParent->GetSortType(); 1.695 + if (mResult) 1.696 + return mResult->mSortingMode; 1.697 + 1.698 + // This is a detached container, just use natural order. 1.699 + return nsINavHistoryQueryOptions::SORT_BY_NONE; 1.700 +} 1.701 + 1.702 + 1.703 +nsresult nsNavHistoryContainerResultNode::Refresh() { 1.704 + NS_WARNING("Refresh() is supported by queries or folders, not generic containers."); 1.705 + return NS_OK; 1.706 +} 1.707 + 1.708 +void 1.709 +nsNavHistoryContainerResultNode::GetSortingAnnotation(nsACString& aAnnotation) 1.710 +{ 1.711 + if (mParent) 1.712 + mParent->GetSortingAnnotation(aAnnotation); 1.713 + else if (mResult) 1.714 + aAnnotation.Assign(mResult->mSortingAnnotation); 1.715 +} 1.716 + 1.717 +/** 1.718 + * @return the sorting comparator function for the give sort type, or null if 1.719 + * there is no comparator. 1.720 + */ 1.721 +nsNavHistoryContainerResultNode::SortComparator 1.722 +nsNavHistoryContainerResultNode::GetSortingComparator(uint16_t aSortType) 1.723 +{ 1.724 + switch (aSortType) 1.725 + { 1.726 + case nsINavHistoryQueryOptions::SORT_BY_NONE: 1.727 + return &SortComparison_Bookmark; 1.728 + case nsINavHistoryQueryOptions::SORT_BY_TITLE_ASCENDING: 1.729 + return &SortComparison_TitleLess; 1.730 + case nsINavHistoryQueryOptions::SORT_BY_TITLE_DESCENDING: 1.731 + return &SortComparison_TitleGreater; 1.732 + case nsINavHistoryQueryOptions::SORT_BY_DATE_ASCENDING: 1.733 + return &SortComparison_DateLess; 1.734 + case nsINavHistoryQueryOptions::SORT_BY_DATE_DESCENDING: 1.735 + return &SortComparison_DateGreater; 1.736 + case nsINavHistoryQueryOptions::SORT_BY_URI_ASCENDING: 1.737 + return &SortComparison_URILess; 1.738 + case nsINavHistoryQueryOptions::SORT_BY_URI_DESCENDING: 1.739 + return &SortComparison_URIGreater; 1.740 + case nsINavHistoryQueryOptions::SORT_BY_VISITCOUNT_ASCENDING: 1.741 + return &SortComparison_VisitCountLess; 1.742 + case nsINavHistoryQueryOptions::SORT_BY_VISITCOUNT_DESCENDING: 1.743 + return &SortComparison_VisitCountGreater; 1.744 + case nsINavHistoryQueryOptions::SORT_BY_KEYWORD_ASCENDING: 1.745 + return &SortComparison_KeywordLess; 1.746 + case nsINavHistoryQueryOptions::SORT_BY_KEYWORD_DESCENDING: 1.747 + return &SortComparison_KeywordGreater; 1.748 + case nsINavHistoryQueryOptions::SORT_BY_ANNOTATION_ASCENDING: 1.749 + return &SortComparison_AnnotationLess; 1.750 + case nsINavHistoryQueryOptions::SORT_BY_ANNOTATION_DESCENDING: 1.751 + return &SortComparison_AnnotationGreater; 1.752 + case nsINavHistoryQueryOptions::SORT_BY_DATEADDED_ASCENDING: 1.753 + return &SortComparison_DateAddedLess; 1.754 + case nsINavHistoryQueryOptions::SORT_BY_DATEADDED_DESCENDING: 1.755 + return &SortComparison_DateAddedGreater; 1.756 + case nsINavHistoryQueryOptions::SORT_BY_LASTMODIFIED_ASCENDING: 1.757 + return &SortComparison_LastModifiedLess; 1.758 + case nsINavHistoryQueryOptions::SORT_BY_LASTMODIFIED_DESCENDING: 1.759 + return &SortComparison_LastModifiedGreater; 1.760 + case nsINavHistoryQueryOptions::SORT_BY_TAGS_ASCENDING: 1.761 + return &SortComparison_TagsLess; 1.762 + case nsINavHistoryQueryOptions::SORT_BY_TAGS_DESCENDING: 1.763 + return &SortComparison_TagsGreater; 1.764 + case nsINavHistoryQueryOptions::SORT_BY_FRECENCY_ASCENDING: 1.765 + return &SortComparison_FrecencyLess; 1.766 + case nsINavHistoryQueryOptions::SORT_BY_FRECENCY_DESCENDING: 1.767 + return &SortComparison_FrecencyGreater; 1.768 + default: 1.769 + NS_NOTREACHED("Bad sorting type"); 1.770 + return nullptr; 1.771 + } 1.772 +} 1.773 + 1.774 + 1.775 +/** 1.776 + * This is used by Result::SetSortingMode and QueryResultNode::FillChildren to 1.777 + * sort the child list. 1.778 + * 1.779 + * This does NOT update any visibility or tree information. The caller will 1.780 + * have to completely rebuild the visible list after this. 1.781 + */ 1.782 +void 1.783 +nsNavHistoryContainerResultNode::RecursiveSort( 1.784 + const char* aData, SortComparator aComparator) 1.785 +{ 1.786 + void* data = const_cast<void*>(static_cast<const void*>(aData)); 1.787 + 1.788 + mChildren.Sort(aComparator, data); 1.789 + for (int32_t i = 0; i < mChildren.Count(); ++i) { 1.790 + if (mChildren[i]->IsContainer()) 1.791 + mChildren[i]->GetAsContainer()->RecursiveSort(aData, aComparator); 1.792 + } 1.793 +} 1.794 + 1.795 + 1.796 +/** 1.797 + * @return the index that the given item would fall on if it were to be 1.798 + * inserted using the given sorting. 1.799 + */ 1.800 +uint32_t 1.801 +nsNavHistoryContainerResultNode::FindInsertionPoint( 1.802 + nsNavHistoryResultNode* aNode, SortComparator aComparator, 1.803 + const char* aData, bool* aItemExists) 1.804 +{ 1.805 + if (aItemExists) 1.806 + (*aItemExists) = false; 1.807 + 1.808 + if (mChildren.Count() == 0) 1.809 + return 0; 1.810 + 1.811 + void* data = const_cast<void*>(static_cast<const void*>(aData)); 1.812 + 1.813 + // The common case is the beginning or the end because this is used to insert 1.814 + // new items that are added to history, which is usually sorted by date. 1.815 + int32_t res; 1.816 + res = aComparator(aNode, mChildren[0], data); 1.817 + if (res <= 0) { 1.818 + if (aItemExists && res == 0) 1.819 + (*aItemExists) = true; 1.820 + return 0; 1.821 + } 1.822 + res = aComparator(aNode, mChildren[mChildren.Count() - 1], data); 1.823 + if (res >= 0) { 1.824 + if (aItemExists && res == 0) 1.825 + (*aItemExists) = true; 1.826 + return mChildren.Count(); 1.827 + } 1.828 + 1.829 + uint32_t beginRange = 0; // inclusive 1.830 + uint32_t endRange = mChildren.Count(); // exclusive 1.831 + while (1) { 1.832 + if (beginRange == endRange) 1.833 + return endRange; 1.834 + uint32_t center = beginRange + (endRange - beginRange) / 2; 1.835 + int32_t res = aComparator(aNode, mChildren[center], data); 1.836 + if (res <= 0) { 1.837 + endRange = center; // left side 1.838 + if (aItemExists && res == 0) 1.839 + (*aItemExists) = true; 1.840 + } 1.841 + else { 1.842 + beginRange = center + 1; // right site 1.843 + } 1.844 + } 1.845 +} 1.846 + 1.847 + 1.848 +/** 1.849 + * This checks the child node at the given index to see if its sorting is 1.850 + * correct. This is called when nodes are updated and we need to see whether 1.851 + * we need to move it. 1.852 + * 1.853 + * @returns true if not and it should be resorted. 1.854 +*/ 1.855 +bool 1.856 +nsNavHistoryContainerResultNode::DoesChildNeedResorting(uint32_t aIndex, 1.857 + SortComparator aComparator, const char* aData) 1.858 +{ 1.859 + NS_ASSERTION(aIndex < uint32_t(mChildren.Count()), 1.860 + "Input index out of range"); 1.861 + if (mChildren.Count() == 1) 1.862 + return false; 1.863 + 1.864 + void* data = const_cast<void*>(static_cast<const void*>(aData)); 1.865 + 1.866 + if (aIndex > 0) { 1.867 + // compare to previous item 1.868 + if (aComparator(mChildren[aIndex - 1], mChildren[aIndex], data) > 0) 1.869 + return true; 1.870 + } 1.871 + if (aIndex < uint32_t(mChildren.Count()) - 1) { 1.872 + // compare to next item 1.873 + if (aComparator(mChildren[aIndex], mChildren[aIndex + 1], data) > 0) 1.874 + return true; 1.875 + } 1.876 + return false; 1.877 +} 1.878 + 1.879 + 1.880 +/* static */ 1.881 +int32_t nsNavHistoryContainerResultNode::SortComparison_StringLess( 1.882 + const nsAString& a, const nsAString& b) { 1.883 + 1.884 + nsNavHistory* history = nsNavHistory::GetHistoryService(); 1.885 + NS_ENSURE_TRUE(history, 0); 1.886 + nsICollation* collation = history->GetCollation(); 1.887 + NS_ENSURE_TRUE(collation, 0); 1.888 + 1.889 + int32_t res = 0; 1.890 + collation->CompareString(nsICollation::kCollationCaseInSensitive, a, b, &res); 1.891 + return res; 1.892 +} 1.893 + 1.894 + 1.895 +/** 1.896 + * When there are bookmark indices, we should never have ties, so we don't 1.897 + * need to worry about tiebreaking. When there are no bookmark indices, 1.898 + * everything will be -1 and we don't worry about sorting. 1.899 + */ 1.900 +int32_t nsNavHistoryContainerResultNode::SortComparison_Bookmark( 1.901 + nsNavHistoryResultNode* a, nsNavHistoryResultNode* b, void* closure) 1.902 +{ 1.903 + return a->mBookmarkIndex - b->mBookmarkIndex; 1.904 +} 1.905 + 1.906 +/** 1.907 + * These are a little more complicated because they do a localization 1.908 + * conversion. If this is too slow, we can compute the sort keys once in 1.909 + * advance, sort that array, and then reorder the real array accordingly. 1.910 + * This would save some key generations. 1.911 + * 1.912 + * The collation object must be allocated before sorting on title! 1.913 + */ 1.914 +int32_t nsNavHistoryContainerResultNode::SortComparison_TitleLess( 1.915 + nsNavHistoryResultNode* a, nsNavHistoryResultNode* b, void* closure) 1.916 +{ 1.917 + uint32_t aType; 1.918 + a->GetType(&aType); 1.919 + 1.920 + int32_t value = SortComparison_StringLess(NS_ConvertUTF8toUTF16(a->mTitle), 1.921 + NS_ConvertUTF8toUTF16(b->mTitle)); 1.922 + if (value == 0) { 1.923 + // resolve by URI 1.924 + if (a->IsURI()) { 1.925 + value = a->mURI.Compare(b->mURI.get()); 1.926 + } 1.927 + if (value == 0) { 1.928 + // resolve by date 1.929 + value = ComparePRTime(a->mTime, b->mTime); 1.930 + if (value == 0) 1.931 + value = nsNavHistoryContainerResultNode::SortComparison_Bookmark(a, b, closure); 1.932 + } 1.933 + } 1.934 + return value; 1.935 +} 1.936 +int32_t nsNavHistoryContainerResultNode::SortComparison_TitleGreater( 1.937 + nsNavHistoryResultNode* a, nsNavHistoryResultNode* b, void* closure) 1.938 +{ 1.939 + return -SortComparison_TitleLess(a, b, closure); 1.940 +} 1.941 + 1.942 +/** 1.943 + * Equal times will be very unusual, but it is important that there is some 1.944 + * deterministic ordering of the results so they don't move around. 1.945 + */ 1.946 +int32_t nsNavHistoryContainerResultNode::SortComparison_DateLess( 1.947 + nsNavHistoryResultNode* a, nsNavHistoryResultNode* b, void* closure) 1.948 +{ 1.949 + int32_t value = ComparePRTime(a->mTime, b->mTime); 1.950 + if (value == 0) { 1.951 + value = SortComparison_StringLess(NS_ConvertUTF8toUTF16(a->mTitle), 1.952 + NS_ConvertUTF8toUTF16(b->mTitle)); 1.953 + if (value == 0) 1.954 + value = nsNavHistoryContainerResultNode::SortComparison_Bookmark(a, b, closure); 1.955 + } 1.956 + return value; 1.957 +} 1.958 +int32_t nsNavHistoryContainerResultNode::SortComparison_DateGreater( 1.959 + nsNavHistoryResultNode* a, nsNavHistoryResultNode* b, void* closure) 1.960 +{ 1.961 + return -nsNavHistoryContainerResultNode::SortComparison_DateLess(a, b, closure); 1.962 +} 1.963 + 1.964 + 1.965 +int32_t nsNavHistoryContainerResultNode::SortComparison_DateAddedLess( 1.966 + nsNavHistoryResultNode* a, nsNavHistoryResultNode* b, void* closure) 1.967 +{ 1.968 + int32_t value = ComparePRTime(a->mDateAdded, b->mDateAdded); 1.969 + if (value == 0) { 1.970 + value = SortComparison_StringLess(NS_ConvertUTF8toUTF16(a->mTitle), 1.971 + NS_ConvertUTF8toUTF16(b->mTitle)); 1.972 + if (value == 0) 1.973 + value = nsNavHistoryContainerResultNode::SortComparison_Bookmark(a, b, closure); 1.974 + } 1.975 + return value; 1.976 +} 1.977 +int32_t nsNavHistoryContainerResultNode::SortComparison_DateAddedGreater( 1.978 + nsNavHistoryResultNode* a, nsNavHistoryResultNode* b, void* closure) 1.979 +{ 1.980 + return -nsNavHistoryContainerResultNode::SortComparison_DateAddedLess(a, b, closure); 1.981 +} 1.982 + 1.983 + 1.984 +int32_t nsNavHistoryContainerResultNode::SortComparison_LastModifiedLess( 1.985 + nsNavHistoryResultNode* a, nsNavHistoryResultNode* b, void* closure) 1.986 +{ 1.987 + int32_t value = ComparePRTime(a->mLastModified, b->mLastModified); 1.988 + if (value == 0) { 1.989 + value = SortComparison_StringLess(NS_ConvertUTF8toUTF16(a->mTitle), 1.990 + NS_ConvertUTF8toUTF16(b->mTitle)); 1.991 + if (value == 0) 1.992 + value = nsNavHistoryContainerResultNode::SortComparison_Bookmark(a, b, closure); 1.993 + } 1.994 + return value; 1.995 +} 1.996 +int32_t nsNavHistoryContainerResultNode::SortComparison_LastModifiedGreater( 1.997 + nsNavHistoryResultNode* a, nsNavHistoryResultNode* b, void* closure) 1.998 +{ 1.999 + return -nsNavHistoryContainerResultNode::SortComparison_LastModifiedLess(a, b, closure); 1.1000 +} 1.1001 + 1.1002 + 1.1003 +/** 1.1004 + * Certain types of parent nodes are treated specially because URIs are not 1.1005 + * valid (like days or hosts). 1.1006 + */ 1.1007 +int32_t nsNavHistoryContainerResultNode::SortComparison_URILess( 1.1008 + nsNavHistoryResultNode* a, nsNavHistoryResultNode* b, void* closure) 1.1009 +{ 1.1010 + int32_t value; 1.1011 + if (a->IsURI() && b->IsURI()) { 1.1012 + // normal URI or visit 1.1013 + value = a->mURI.Compare(b->mURI.get()); 1.1014 + } else { 1.1015 + // for everything else, use title (= host name) 1.1016 + value = SortComparison_StringLess(NS_ConvertUTF8toUTF16(a->mTitle), 1.1017 + NS_ConvertUTF8toUTF16(b->mTitle)); 1.1018 + } 1.1019 + 1.1020 + if (value == 0) { 1.1021 + value = ComparePRTime(a->mTime, b->mTime); 1.1022 + if (value == 0) 1.1023 + value = nsNavHistoryContainerResultNode::SortComparison_Bookmark(a, b, closure); 1.1024 + } 1.1025 + return value; 1.1026 +} 1.1027 +int32_t nsNavHistoryContainerResultNode::SortComparison_URIGreater( 1.1028 + nsNavHistoryResultNode* a, nsNavHistoryResultNode* b, void* closure) 1.1029 +{ 1.1030 + return -SortComparison_URILess(a, b, closure); 1.1031 +} 1.1032 + 1.1033 + 1.1034 +int32_t nsNavHistoryContainerResultNode::SortComparison_KeywordLess( 1.1035 + nsNavHistoryResultNode* a, nsNavHistoryResultNode* b, void* closure) 1.1036 +{ 1.1037 + int32_t value = 0; 1.1038 + if (a->mItemId != -1 || b->mItemId != -1) { 1.1039 + // compare the keywords 1.1040 + nsAutoString keywordA, keywordB; 1.1041 + nsNavBookmarks* bookmarks = nsNavBookmarks::GetBookmarksService(); 1.1042 + NS_ENSURE_TRUE(bookmarks, 0); 1.1043 + 1.1044 + nsresult rv; 1.1045 + if (a->mItemId != -1) { 1.1046 + rv = bookmarks->GetKeywordForBookmark(a->mItemId, keywordA); 1.1047 + NS_ENSURE_SUCCESS(rv, 0); 1.1048 + } 1.1049 + if (b->mItemId != -1) { 1.1050 + rv = bookmarks->GetKeywordForBookmark(b->mItemId, keywordB); 1.1051 + NS_ENSURE_SUCCESS(rv, 0); 1.1052 + } 1.1053 + 1.1054 + value = SortComparison_StringLess(keywordA, keywordB); 1.1055 + } 1.1056 + 1.1057 + // Fall back to title sorting. 1.1058 + if (value == 0) 1.1059 + value = SortComparison_TitleLess(a, b, closure); 1.1060 + 1.1061 + return value; 1.1062 +} 1.1063 + 1.1064 +int32_t nsNavHistoryContainerResultNode::SortComparison_KeywordGreater( 1.1065 + nsNavHistoryResultNode* a, nsNavHistoryResultNode* b, void* closure) 1.1066 +{ 1.1067 + return -SortComparison_KeywordLess(a, b, closure); 1.1068 +} 1.1069 + 1.1070 +int32_t nsNavHistoryContainerResultNode::SortComparison_AnnotationLess( 1.1071 + nsNavHistoryResultNode* a, nsNavHistoryResultNode* b, void* closure) 1.1072 +{ 1.1073 + nsAutoCString annoName(static_cast<char*>(closure)); 1.1074 + NS_ENSURE_TRUE(!annoName.IsEmpty(), 0); 1.1075 + 1.1076 + bool a_itemAnno = false; 1.1077 + bool b_itemAnno = false; 1.1078 + 1.1079 + // Not used for item annos 1.1080 + nsCOMPtr<nsIURI> a_uri, b_uri; 1.1081 + if (a->mItemId != -1) { 1.1082 + a_itemAnno = true; 1.1083 + } else { 1.1084 + nsAutoCString spec; 1.1085 + if (NS_SUCCEEDED(a->GetUri(spec))) 1.1086 + NS_NewURI(getter_AddRefs(a_uri), spec); 1.1087 + NS_ENSURE_TRUE(a_uri, 0); 1.1088 + } 1.1089 + 1.1090 + if (b->mItemId != -1) { 1.1091 + b_itemAnno = true; 1.1092 + } else { 1.1093 + nsAutoCString spec; 1.1094 + if (NS_SUCCEEDED(b->GetUri(spec))) 1.1095 + NS_NewURI(getter_AddRefs(b_uri), spec); 1.1096 + NS_ENSURE_TRUE(b_uri, 0); 1.1097 + } 1.1098 + 1.1099 + nsAnnotationService* annosvc = nsAnnotationService::GetAnnotationService(); 1.1100 + NS_ENSURE_TRUE(annosvc, 0); 1.1101 + 1.1102 + bool a_hasAnno, b_hasAnno; 1.1103 + if (a_itemAnno) { 1.1104 + NS_ENSURE_SUCCESS(annosvc->ItemHasAnnotation(a->mItemId, annoName, 1.1105 + &a_hasAnno), 0); 1.1106 + } else { 1.1107 + NS_ENSURE_SUCCESS(annosvc->PageHasAnnotation(a_uri, annoName, 1.1108 + &a_hasAnno), 0); 1.1109 + } 1.1110 + if (b_itemAnno) { 1.1111 + NS_ENSURE_SUCCESS(annosvc->ItemHasAnnotation(b->mItemId, annoName, 1.1112 + &b_hasAnno), 0); 1.1113 + } else { 1.1114 + NS_ENSURE_SUCCESS(annosvc->PageHasAnnotation(b_uri, annoName, 1.1115 + &b_hasAnno), 0); 1.1116 + } 1.1117 + 1.1118 + int32_t value = 0; 1.1119 + if (a_hasAnno || b_hasAnno) { 1.1120 + uint16_t annoType; 1.1121 + if (a_hasAnno) { 1.1122 + if (a_itemAnno) { 1.1123 + NS_ENSURE_SUCCESS(annosvc->GetItemAnnotationType(a->mItemId, 1.1124 + annoName, 1.1125 + &annoType), 0); 1.1126 + } else { 1.1127 + NS_ENSURE_SUCCESS(annosvc->GetPageAnnotationType(a_uri, annoName, 1.1128 + &annoType), 0); 1.1129 + } 1.1130 + } 1.1131 + if (b_hasAnno) { 1.1132 + uint16_t b_type; 1.1133 + if (b_itemAnno) { 1.1134 + NS_ENSURE_SUCCESS(annosvc->GetItemAnnotationType(b->mItemId, 1.1135 + annoName, 1.1136 + &b_type), 0); 1.1137 + } else { 1.1138 + NS_ENSURE_SUCCESS(annosvc->GetPageAnnotationType(b_uri, annoName, 1.1139 + &b_type), 0); 1.1140 + } 1.1141 + // We better make the API not support this state, really 1.1142 + // XXXmano: this is actually wrong for double<->int and int64_t<->int32_t 1.1143 + if (a_hasAnno && b_type != annoType) 1.1144 + return 0; 1.1145 + annoType = b_type; 1.1146 + } 1.1147 + 1.1148 +#define GET_ANNOTATIONS_VALUES(METHOD_ITEM, METHOD_PAGE, A_VAL, B_VAL) \ 1.1149 + if (a_hasAnno) { \ 1.1150 + if (a_itemAnno) { \ 1.1151 + NS_ENSURE_SUCCESS(annosvc->METHOD_ITEM(a->mItemId, annoName, \ 1.1152 + A_VAL), 0); \ 1.1153 + } else { \ 1.1154 + NS_ENSURE_SUCCESS(annosvc->METHOD_PAGE(a_uri, annoName, \ 1.1155 + A_VAL), 0); \ 1.1156 + } \ 1.1157 + } \ 1.1158 + if (b_hasAnno) { \ 1.1159 + if (b_itemAnno) { \ 1.1160 + NS_ENSURE_SUCCESS(annosvc->METHOD_ITEM(b->mItemId, annoName, \ 1.1161 + B_VAL), 0); \ 1.1162 + } else { \ 1.1163 + NS_ENSURE_SUCCESS(annosvc->METHOD_PAGE(b_uri, annoName, \ 1.1164 + B_VAL), 0); \ 1.1165 + } \ 1.1166 + } 1.1167 + 1.1168 + if (annoType == nsIAnnotationService::TYPE_STRING) { 1.1169 + nsAutoString a_val, b_val; 1.1170 + GET_ANNOTATIONS_VALUES(GetItemAnnotationString, 1.1171 + GetPageAnnotationString, a_val, b_val); 1.1172 + value = SortComparison_StringLess(a_val, b_val); 1.1173 + } 1.1174 + else if (annoType == nsIAnnotationService::TYPE_INT32) { 1.1175 + int32_t a_val = 0, b_val = 0; 1.1176 + GET_ANNOTATIONS_VALUES(GetItemAnnotationInt32, 1.1177 + GetPageAnnotationInt32, &a_val, &b_val); 1.1178 + value = (a_val < b_val) ? -1 : (a_val > b_val) ? 1 : 0; 1.1179 + } 1.1180 + else if (annoType == nsIAnnotationService::TYPE_INT64) { 1.1181 + int64_t a_val = 0, b_val = 0; 1.1182 + GET_ANNOTATIONS_VALUES(GetItemAnnotationInt64, 1.1183 + GetPageAnnotationInt64, &a_val, &b_val); 1.1184 + value = (a_val < b_val) ? -1 : (a_val > b_val) ? 1 : 0; 1.1185 + } 1.1186 + else if (annoType == nsIAnnotationService::TYPE_DOUBLE) { 1.1187 + double a_val = 0, b_val = 0; 1.1188 + GET_ANNOTATIONS_VALUES(GetItemAnnotationDouble, 1.1189 + GetPageAnnotationDouble, &a_val, &b_val); 1.1190 + value = (a_val < b_val) ? -1 : (a_val > b_val) ? 1 : 0; 1.1191 + } 1.1192 + } 1.1193 + 1.1194 + // Note we also fall back to the title-sorting route one of the items didn't 1.1195 + // have the annotation set or if both had it set but in a different storage 1.1196 + // type 1.1197 + if (value == 0) 1.1198 + return SortComparison_TitleLess(a, b, nullptr); 1.1199 + 1.1200 + return value; 1.1201 +} 1.1202 +int32_t nsNavHistoryContainerResultNode::SortComparison_AnnotationGreater( 1.1203 + nsNavHistoryResultNode* a, nsNavHistoryResultNode* b, void* closure) 1.1204 +{ 1.1205 + return -SortComparison_AnnotationLess(a, b, closure); 1.1206 +} 1.1207 + 1.1208 +/** 1.1209 + * Fall back on dates for conflict resolution 1.1210 + */ 1.1211 +int32_t nsNavHistoryContainerResultNode::SortComparison_VisitCountLess( 1.1212 + nsNavHistoryResultNode* a, nsNavHistoryResultNode* b, void* closure) 1.1213 +{ 1.1214 + int32_t value = CompareIntegers(a->mAccessCount, b->mAccessCount); 1.1215 + if (value == 0) { 1.1216 + value = ComparePRTime(a->mTime, b->mTime); 1.1217 + if (value == 0) 1.1218 + value = nsNavHistoryContainerResultNode::SortComparison_Bookmark(a, b, closure); 1.1219 + } 1.1220 + return value; 1.1221 +} 1.1222 +int32_t nsNavHistoryContainerResultNode::SortComparison_VisitCountGreater( 1.1223 + nsNavHistoryResultNode* a, nsNavHistoryResultNode* b, void* closure) 1.1224 +{ 1.1225 + return -nsNavHistoryContainerResultNode::SortComparison_VisitCountLess(a, b, closure); 1.1226 +} 1.1227 + 1.1228 + 1.1229 +int32_t nsNavHistoryContainerResultNode::SortComparison_TagsLess( 1.1230 + nsNavHistoryResultNode* a, nsNavHistoryResultNode* b, void* closure) 1.1231 +{ 1.1232 + int32_t value = 0; 1.1233 + nsAutoString aTags, bTags; 1.1234 + 1.1235 + nsresult rv = a->GetTags(aTags); 1.1236 + NS_ENSURE_SUCCESS(rv, 0); 1.1237 + 1.1238 + rv = b->GetTags(bTags); 1.1239 + NS_ENSURE_SUCCESS(rv, 0); 1.1240 + 1.1241 + value = SortComparison_StringLess(aTags, bTags); 1.1242 + 1.1243 + // fall back to title sorting 1.1244 + if (value == 0) 1.1245 + value = SortComparison_TitleLess(a, b, closure); 1.1246 + 1.1247 + return value; 1.1248 +} 1.1249 + 1.1250 +int32_t nsNavHistoryContainerResultNode::SortComparison_TagsGreater( 1.1251 + nsNavHistoryResultNode* a, nsNavHistoryResultNode* b, void* closure) 1.1252 +{ 1.1253 + return -SortComparison_TagsLess(a, b, closure); 1.1254 +} 1.1255 + 1.1256 +/** 1.1257 + * Fall back on date and bookmarked status, for conflict resolution. 1.1258 + */ 1.1259 +int32_t 1.1260 +nsNavHistoryContainerResultNode::SortComparison_FrecencyLess( 1.1261 + nsNavHistoryResultNode* a, nsNavHistoryResultNode* b, void* closure 1.1262 +) 1.1263 +{ 1.1264 + int32_t value = CompareIntegers(a->mFrecency, b->mFrecency); 1.1265 + if (value == 0) { 1.1266 + value = ComparePRTime(a->mTime, b->mTime); 1.1267 + if (value == 0) { 1.1268 + value = nsNavHistoryContainerResultNode::SortComparison_Bookmark(a, b, closure); 1.1269 + } 1.1270 + } 1.1271 + return value; 1.1272 +} 1.1273 +int32_t 1.1274 +nsNavHistoryContainerResultNode::SortComparison_FrecencyGreater( 1.1275 + nsNavHistoryResultNode* a, nsNavHistoryResultNode* b, void* closure 1.1276 +) 1.1277 +{ 1.1278 + return -nsNavHistoryContainerResultNode::SortComparison_FrecencyLess(a, b, closure); 1.1279 +} 1.1280 + 1.1281 +/** 1.1282 + * Searches this folder for a node with the given URI. Returns null if not 1.1283 + * found. 1.1284 + * 1.1285 + * @note Does not addref the node! 1.1286 + */ 1.1287 +nsNavHistoryResultNode* 1.1288 +nsNavHistoryContainerResultNode::FindChildURI(const nsACString& aSpec, 1.1289 + uint32_t* aNodeIndex) 1.1290 +{ 1.1291 + for (int32_t i = 0; i < mChildren.Count(); ++i) { 1.1292 + if (mChildren[i]->IsURI()) { 1.1293 + if (aSpec.Equals(mChildren[i]->mURI)) { 1.1294 + *aNodeIndex = i; 1.1295 + return mChildren[i]; 1.1296 + } 1.1297 + } 1.1298 + } 1.1299 + return nullptr; 1.1300 +} 1.1301 + 1.1302 +/** 1.1303 + * This does the work of adding a child to the container. The child can be 1.1304 + * either a container or or a single item that may even be collapsed with the 1.1305 + * adjacent ones. 1.1306 + * 1.1307 + * Some inserts are "temporary" meaning that they are happening immediately 1.1308 + * after a temporary remove. We do this when movings elements when they 1.1309 + * change to keep them in the proper sorting position. In these cases, we 1.1310 + * don't need to recompute any statistics. 1.1311 + */ 1.1312 +nsresult 1.1313 +nsNavHistoryContainerResultNode::InsertChildAt(nsNavHistoryResultNode* aNode, 1.1314 + int32_t aIndex, 1.1315 + bool aIsTemporary) 1.1316 +{ 1.1317 + nsNavHistoryResult* result = GetResult(); 1.1318 + NS_ENSURE_STATE(result); 1.1319 + 1.1320 + aNode->mParent = this; 1.1321 + aNode->mIndentLevel = mIndentLevel + 1; 1.1322 + if (!aIsTemporary && aNode->IsContainer()) { 1.1323 + // need to update all the new item's children 1.1324 + nsNavHistoryContainerResultNode* container = aNode->GetAsContainer(); 1.1325 + container->mResult = result; 1.1326 + container->FillStats(); 1.1327 + } 1.1328 + 1.1329 + if (!mChildren.InsertObjectAt(aNode, aIndex)) 1.1330 + return NS_ERROR_OUT_OF_MEMORY; 1.1331 + 1.1332 + // Update our stats and notify the result's observers. 1.1333 + if (!aIsTemporary) { 1.1334 + mAccessCount += aNode->mAccessCount; 1.1335 + if (mTime < aNode->mTime) 1.1336 + mTime = aNode->mTime; 1.1337 + if (!mParent || mParent->AreChildrenVisible()) { 1.1338 + NOTIFY_RESULT_OBSERVERS(result, 1.1339 + NodeHistoryDetailsChanged(TO_ICONTAINER(this), 1.1340 + mTime, 1.1341 + mAccessCount)); 1.1342 + } 1.1343 + 1.1344 + nsresult rv = ReverseUpdateStats(aNode->mAccessCount); 1.1345 + NS_ENSURE_SUCCESS(rv, rv); 1.1346 + } 1.1347 + 1.1348 + // Update tree if we are visible. Note that we could be here and not 1.1349 + // expanded, like when there is a bookmark folder being updated because its 1.1350 + // parent is visible. 1.1351 + if (AreChildrenVisible()) 1.1352 + NOTIFY_RESULT_OBSERVERS(result, NodeInserted(this, aNode, aIndex)); 1.1353 + 1.1354 + return NS_OK; 1.1355 +} 1.1356 + 1.1357 + 1.1358 +/** 1.1359 + * This locates the proper place for insertion according to the current sort 1.1360 + * and calls InsertChildAt 1.1361 + */ 1.1362 +nsresult 1.1363 +nsNavHistoryContainerResultNode::InsertSortedChild( 1.1364 + nsNavHistoryResultNode* aNode, 1.1365 + bool aIsTemporary, bool aIgnoreDuplicates) 1.1366 +{ 1.1367 + 1.1368 + if (mChildren.Count() == 0) 1.1369 + return InsertChildAt(aNode, 0, aIsTemporary); 1.1370 + 1.1371 + SortComparator comparator = GetSortingComparator(GetSortType()); 1.1372 + if (comparator) { 1.1373 + // When inserting a new node, it must have proper statistics because we use 1.1374 + // them to find the correct insertion point. The insert function will then 1.1375 + // recompute these statistics and fill in the proper parents and hierarchy 1.1376 + // level. Doing this twice shouldn't be a large performance penalty because 1.1377 + // when we are inserting new containers, they typically contain only one 1.1378 + // item (because we've browsed a new page). 1.1379 + if (!aIsTemporary && aNode->IsContainer()) { 1.1380 + // need to update all the new item's children 1.1381 + nsNavHistoryContainerResultNode* container = aNode->GetAsContainer(); 1.1382 + container->mResult = mResult; 1.1383 + container->FillStats(); 1.1384 + } 1.1385 + 1.1386 + nsAutoCString sortingAnnotation; 1.1387 + GetSortingAnnotation(sortingAnnotation); 1.1388 + bool itemExists; 1.1389 + uint32_t position = FindInsertionPoint(aNode, comparator, 1.1390 + sortingAnnotation.get(), 1.1391 + &itemExists); 1.1392 + if (aIgnoreDuplicates && itemExists) 1.1393 + return NS_OK; 1.1394 + 1.1395 + return InsertChildAt(aNode, position, aIsTemporary); 1.1396 + } 1.1397 + return InsertChildAt(aNode, mChildren.Count(), aIsTemporary); 1.1398 +} 1.1399 + 1.1400 +/** 1.1401 + * This checks if the item at aIndex is located correctly given the sorting 1.1402 + * move. If it's not, the item is moved, and the result's observers are 1.1403 + * notified. 1.1404 + * 1.1405 + * @return true if the item position has been changed, false otherwise. 1.1406 + */ 1.1407 +bool 1.1408 +nsNavHistoryContainerResultNode::EnsureItemPosition(uint32_t aIndex) { 1.1409 + NS_ASSERTION(aIndex < (uint32_t)mChildren.Count(), "Invalid index"); 1.1410 + if (aIndex >= (uint32_t)mChildren.Count()) 1.1411 + return false; 1.1412 + 1.1413 + SortComparator comparator = GetSortingComparator(GetSortType()); 1.1414 + if (!comparator) 1.1415 + return false; 1.1416 + 1.1417 + nsAutoCString sortAnno; 1.1418 + GetSortingAnnotation(sortAnno); 1.1419 + if (!DoesChildNeedResorting(aIndex, comparator, sortAnno.get())) 1.1420 + return false; 1.1421 + 1.1422 + nsRefPtr<nsNavHistoryResultNode> node(mChildren[aIndex]); 1.1423 + mChildren.RemoveObjectAt(aIndex); 1.1424 + 1.1425 + uint32_t newIndex = FindInsertionPoint( 1.1426 + node, comparator,sortAnno.get(), nullptr); 1.1427 + mChildren.InsertObjectAt(node.get(), newIndex); 1.1428 + 1.1429 + if (AreChildrenVisible()) { 1.1430 + nsNavHistoryResult* result = GetResult(); 1.1431 + NOTIFY_RESULT_OBSERVERS_RET(result, 1.1432 + NodeMoved(node, this, aIndex, this, newIndex), 1.1433 + false); 1.1434 + } 1.1435 + 1.1436 + return true; 1.1437 +} 1.1438 + 1.1439 +/** 1.1440 + * This does all the work of removing a child from this container, including 1.1441 + * updating the tree if necessary. Note that we do not need to be open for 1.1442 + * this to work. 1.1443 + * 1.1444 + * Some removes are "temporary" meaning that they'll just get inserted again. 1.1445 + * We do this for resorting. In these cases, we don't need to recompute any 1.1446 + * statistics, and we shouldn't notify those container that they are being 1.1447 + * removed. 1.1448 + */ 1.1449 +nsresult 1.1450 +nsNavHistoryContainerResultNode::RemoveChildAt(int32_t aIndex, 1.1451 + bool aIsTemporary) 1.1452 +{ 1.1453 + NS_ASSERTION(aIndex >= 0 && aIndex < mChildren.Count(), "Invalid index"); 1.1454 + 1.1455 + // Hold an owning reference to keep from expiring while we work with it. 1.1456 + nsRefPtr<nsNavHistoryResultNode> oldNode = mChildren[aIndex]; 1.1457 + 1.1458 + // Update stats. 1.1459 + uint32_t oldAccessCount = 0; 1.1460 + if (!aIsTemporary) { 1.1461 + oldAccessCount = mAccessCount; 1.1462 + mAccessCount -= mChildren[aIndex]->mAccessCount; 1.1463 + NS_ASSERTION(mAccessCount >= 0, "Invalid access count while updating!"); 1.1464 + } 1.1465 + 1.1466 + // Remove it from our list and notify the result's observers. 1.1467 + mChildren.RemoveObjectAt(aIndex); 1.1468 + if (AreChildrenVisible()) { 1.1469 + nsNavHistoryResult* result = GetResult(); 1.1470 + NOTIFY_RESULT_OBSERVERS(result, 1.1471 + NodeRemoved(this, oldNode, aIndex)); 1.1472 + } 1.1473 + 1.1474 + if (!aIsTemporary) { 1.1475 + nsresult rv = ReverseUpdateStats(mAccessCount - oldAccessCount); 1.1476 + NS_ENSURE_SUCCESS(rv, rv); 1.1477 + oldNode->OnRemoving(); 1.1478 + } 1.1479 + return NS_OK; 1.1480 +} 1.1481 + 1.1482 + 1.1483 +/** 1.1484 + * Searches for matches for the given URI. If aOnlyOne is set, it will 1.1485 + * terminate as soon as it finds a single match. This would be used when there 1.1486 + * are URI results so there will only ever be one copy of any URI. 1.1487 + * 1.1488 + * When aOnlyOne is false, it will check all elements. This is for visit 1.1489 + * style results that may have multiple copies of any given URI. 1.1490 + */ 1.1491 +void 1.1492 +nsNavHistoryContainerResultNode::RecursiveFindURIs(bool aOnlyOne, 1.1493 + nsNavHistoryContainerResultNode* aContainer, const nsCString& aSpec, 1.1494 + nsCOMArray<nsNavHistoryResultNode>* aMatches) 1.1495 +{ 1.1496 + for (int32_t child = 0; child < aContainer->mChildren.Count(); ++child) { 1.1497 + uint32_t type; 1.1498 + aContainer->mChildren[child]->GetType(&type); 1.1499 + if (nsNavHistoryResultNode::IsTypeURI(type)) { 1.1500 + // compare URIs 1.1501 + nsNavHistoryResultNode* uriNode = aContainer->mChildren[child]; 1.1502 + if (uriNode->mURI.Equals(aSpec)) { 1.1503 + // found 1.1504 + aMatches->AppendObject(uriNode); 1.1505 + if (aOnlyOne) 1.1506 + return; 1.1507 + } 1.1508 + } 1.1509 + } 1.1510 +} 1.1511 + 1.1512 + 1.1513 +/** 1.1514 + * If aUpdateSort is true, we will also update the sorting of this item. 1.1515 + * Normally you want this to be true, but it can be false if the thing you are 1.1516 + * changing can not affect sorting (like favicons). 1.1517 + * 1.1518 + * You should NOT change any child lists as part of the callback function. 1.1519 + */ 1.1520 +bool 1.1521 +nsNavHistoryContainerResultNode::UpdateURIs(bool aRecursive, bool aOnlyOne, 1.1522 + bool aUpdateSort, const nsCString& aSpec, 1.1523 + nsresult (*aCallback)(nsNavHistoryResultNode*, const void*, const nsNavHistoryResult*), 1.1524 + const void* aClosure) 1.1525 +{ 1.1526 + const nsNavHistoryResult* result = GetResult(); 1.1527 + if (!result) { 1.1528 + MOZ_ASSERT(false, "Should have a result"); 1.1529 + return false; 1.1530 + } 1.1531 + 1.1532 + // this needs to be owning since sometimes we remove and re-insert nodes 1.1533 + // in their parents and we don't want them to go away. 1.1534 + nsCOMArray<nsNavHistoryResultNode> matches; 1.1535 + 1.1536 + if (aRecursive) { 1.1537 + RecursiveFindURIs(aOnlyOne, this, aSpec, &matches); 1.1538 + } else if (aOnlyOne) { 1.1539 + uint32_t nodeIndex; 1.1540 + nsNavHistoryResultNode* node = FindChildURI(aSpec, &nodeIndex); 1.1541 + if (node) 1.1542 + matches.AppendObject(node); 1.1543 + } else { 1.1544 + MOZ_ASSERT(false, 1.1545 + "UpdateURIs does not handle nonrecursive updates of multiple items."); 1.1546 + // this case easy to add if you need it, just find all the matching URIs 1.1547 + // at this level. However, this isn't currently used. History uses 1.1548 + // recursive, Bookmarks uses one level and knows that the match is unique. 1.1549 + return false; 1.1550 + } 1.1551 + 1.1552 + if (matches.Count() == 0) 1.1553 + return false; 1.1554 + 1.1555 + // PERFORMANCE: This updates each container for each child in it that 1.1556 + // changes. In some cases, many elements have changed inside the same 1.1557 + // container. It would be better to compose a list of containers, and 1.1558 + // update each one only once for all the items that have changed in it. 1.1559 + for (int32_t i = 0; i < matches.Count(); ++i) 1.1560 + { 1.1561 + nsNavHistoryResultNode* node = matches[i]; 1.1562 + nsNavHistoryContainerResultNode* parent = node->mParent; 1.1563 + if (!parent) { 1.1564 + MOZ_ASSERT(false, "All URI nodes being updated must have parents"); 1.1565 + continue; 1.1566 + } 1.1567 + 1.1568 + uint32_t oldAccessCount = node->mAccessCount; 1.1569 + PRTime oldTime = node->mTime; 1.1570 + aCallback(node, aClosure, result); 1.1571 + 1.1572 + if (oldAccessCount != node->mAccessCount || oldTime != node->mTime) { 1.1573 + parent->mAccessCount += node->mAccessCount - oldAccessCount; 1.1574 + if (node->mTime > parent->mTime) 1.1575 + parent->mTime = node->mTime; 1.1576 + if (parent->AreChildrenVisible()) { 1.1577 + NOTIFY_RESULT_OBSERVERS_RET(result, 1.1578 + NodeHistoryDetailsChanged( 1.1579 + TO_ICONTAINER(parent), 1.1580 + parent->mTime, 1.1581 + parent->mAccessCount), 1.1582 + true); 1.1583 + } 1.1584 + DebugOnly<nsresult> rv = parent->ReverseUpdateStats(node->mAccessCount - oldAccessCount); 1.1585 + MOZ_ASSERT(NS_SUCCEEDED(rv), "should be able to ReverseUpdateStats"); 1.1586 + } 1.1587 + 1.1588 + if (aUpdateSort) { 1.1589 + int32_t childIndex = parent->FindChild(node); 1.1590 + MOZ_ASSERT(childIndex >= 0, "Could not find child we just got a reference to"); 1.1591 + if (childIndex >= 0) 1.1592 + parent->EnsureItemPosition(childIndex); 1.1593 + } 1.1594 + } 1.1595 + 1.1596 + return true; 1.1597 +} 1.1598 + 1.1599 + 1.1600 +/** 1.1601 + * This is used to update the titles in the tree. This is called from both 1.1602 + * query and bookmark folder containers to update the tree. Bookmark folders 1.1603 + * should be sure to set recursive to false, since child folders will have 1.1604 + * their own callbacks registered. 1.1605 + */ 1.1606 +static nsresult setTitleCallback(nsNavHistoryResultNode* aNode, 1.1607 + const void* aClosure, 1.1608 + const nsNavHistoryResult* aResult) 1.1609 +{ 1.1610 + const nsACString* newTitle = static_cast<const nsACString*>(aClosure); 1.1611 + aNode->mTitle = *newTitle; 1.1612 + 1.1613 + if (aResult && (!aNode->mParent || aNode->mParent->AreChildrenVisible())) 1.1614 + NOTIFY_RESULT_OBSERVERS(aResult, NodeTitleChanged(aNode, *newTitle)); 1.1615 + 1.1616 + return NS_OK; 1.1617 +} 1.1618 +nsresult 1.1619 +nsNavHistoryContainerResultNode::ChangeTitles(nsIURI* aURI, 1.1620 + const nsACString& aNewTitle, 1.1621 + bool aRecursive, 1.1622 + bool aOnlyOne) 1.1623 +{ 1.1624 + // uri string 1.1625 + nsAutoCString uriString; 1.1626 + nsresult rv = aURI->GetSpec(uriString); 1.1627 + NS_ENSURE_SUCCESS(rv, rv); 1.1628 + 1.1629 + // The recursive function will update the result's tree nodes, but only if we 1.1630 + // give it a non-null pointer. So if there isn't a tree, just pass nullptr 1.1631 + // so it doesn't bother trying to call the result. 1.1632 + nsNavHistoryResult* result = GetResult(); 1.1633 + NS_ENSURE_STATE(result); 1.1634 + 1.1635 + uint16_t sortType = GetSortType(); 1.1636 + bool updateSorting = 1.1637 + (sortType == nsINavHistoryQueryOptions::SORT_BY_TITLE_ASCENDING || 1.1638 + sortType == nsINavHistoryQueryOptions::SORT_BY_TITLE_DESCENDING); 1.1639 + 1.1640 + UpdateURIs(aRecursive, aOnlyOne, updateSorting, uriString, 1.1641 + setTitleCallback, 1.1642 + static_cast<const void*>(&aNewTitle)); 1.1643 + 1.1644 + return NS_OK; 1.1645 +} 1.1646 + 1.1647 + 1.1648 +/** 1.1649 + * Complex containers (folders and queries) will override this. Here, we 1.1650 + * handle the case of simple containers (like host groups) where the children 1.1651 + * are always stored. 1.1652 + */ 1.1653 +NS_IMETHODIMP 1.1654 +nsNavHistoryContainerResultNode::GetHasChildren(bool *aHasChildren) 1.1655 +{ 1.1656 + *aHasChildren = (mChildren.Count() > 0); 1.1657 + return NS_OK; 1.1658 +} 1.1659 + 1.1660 + 1.1661 +/** 1.1662 + * @throws if this node is closed. 1.1663 + */ 1.1664 +NS_IMETHODIMP 1.1665 +nsNavHistoryContainerResultNode::GetChildCount(uint32_t* aChildCount) 1.1666 +{ 1.1667 + if (!mExpanded) 1.1668 + return NS_ERROR_NOT_AVAILABLE; 1.1669 + *aChildCount = mChildren.Count(); 1.1670 + return NS_OK; 1.1671 +} 1.1672 + 1.1673 + 1.1674 +NS_IMETHODIMP 1.1675 +nsNavHistoryContainerResultNode::GetChild(uint32_t aIndex, 1.1676 + nsINavHistoryResultNode** _retval) 1.1677 +{ 1.1678 + if (!mExpanded) 1.1679 + return NS_ERROR_NOT_AVAILABLE; 1.1680 + if (aIndex >= uint32_t(mChildren.Count())) 1.1681 + return NS_ERROR_INVALID_ARG; 1.1682 + NS_ADDREF(*_retval = mChildren[aIndex]); 1.1683 + return NS_OK; 1.1684 +} 1.1685 + 1.1686 + 1.1687 +NS_IMETHODIMP 1.1688 +nsNavHistoryContainerResultNode::GetChildIndex(nsINavHistoryResultNode* aNode, 1.1689 + uint32_t* _retval) 1.1690 +{ 1.1691 + if (!mExpanded) 1.1692 + return NS_ERROR_NOT_AVAILABLE; 1.1693 + 1.1694 + int32_t nodeIndex = FindChild(static_cast<nsNavHistoryResultNode*>(aNode)); 1.1695 + if (nodeIndex == -1) 1.1696 + return NS_ERROR_INVALID_ARG; 1.1697 + 1.1698 + *_retval = nodeIndex; 1.1699 + return NS_OK; 1.1700 +} 1.1701 + 1.1702 + 1.1703 +NS_IMETHODIMP 1.1704 +nsNavHistoryContainerResultNode::FindNodeByDetails(const nsACString& aURIString, 1.1705 + PRTime aTime, 1.1706 + int64_t aItemId, 1.1707 + bool aRecursive, 1.1708 + nsINavHistoryResultNode** _retval) { 1.1709 + if (!mExpanded) 1.1710 + return NS_ERROR_NOT_AVAILABLE; 1.1711 + 1.1712 + *_retval = nullptr; 1.1713 + for (int32_t i = 0; i < mChildren.Count(); ++i) { 1.1714 + if (mChildren[i]->mURI.Equals(aURIString) && 1.1715 + mChildren[i]->mTime == aTime && 1.1716 + mChildren[i]->mItemId == aItemId) { 1.1717 + *_retval = mChildren[i]; 1.1718 + break; 1.1719 + } 1.1720 + 1.1721 + if (aRecursive && mChildren[i]->IsContainer()) { 1.1722 + nsNavHistoryContainerResultNode* asContainer = 1.1723 + mChildren[i]->GetAsContainer(); 1.1724 + if (asContainer->mExpanded) { 1.1725 + nsresult rv = asContainer->FindNodeByDetails(aURIString, aTime, 1.1726 + aItemId, 1.1727 + aRecursive, 1.1728 + _retval); 1.1729 + 1.1730 + if (NS_SUCCEEDED(rv) && _retval) 1.1731 + break; 1.1732 + } 1.1733 + } 1.1734 + } 1.1735 + NS_IF_ADDREF(*_retval); 1.1736 + return NS_OK; 1.1737 +} 1.1738 + 1.1739 +/** 1.1740 + * @note Overridden for folders to query the bookmarks service directly. 1.1741 + */ 1.1742 +NS_IMETHODIMP 1.1743 +nsNavHistoryContainerResultNode::GetChildrenReadOnly(bool *aChildrenReadOnly) 1.1744 +{ 1.1745 + *aChildrenReadOnly = mChildrenReadOnly; 1.1746 + return NS_OK; 1.1747 +} 1.1748 + 1.1749 +/** 1.1750 + * HOW QUERY UPDATING WORKS 1.1751 + * 1.1752 + * Queries are different than bookmark folders in that we can not always do 1.1753 + * dynamic updates (easily) and updates are more expensive. Therefore, we do 1.1754 + * NOT query if we are not open and want to see if we have any children (for 1.1755 + * drawing a twisty) and always assume we will. 1.1756 + * 1.1757 + * When the container is opened, we execute the query and register the 1.1758 + * listeners. Like bookmark folders, we stay registered even when closed, and 1.1759 + * clear ourselves as soon as a message comes in. This lets us respond quickly 1.1760 + * if the user closes and reopens the container. 1.1761 + * 1.1762 + * We try to handle the most common notifications for the most common query 1.1763 + * types dynamically, that is, figuring out what should happen in response to 1.1764 + * a message without doing a requery. For complex changes or complex queries, 1.1765 + * we give up and requery. 1.1766 + */ 1.1767 +NS_IMPL_ISUPPORTS_INHERITED(nsNavHistoryQueryResultNode, 1.1768 + nsNavHistoryContainerResultNode, 1.1769 + nsINavHistoryQueryResultNode) 1.1770 + 1.1771 +nsNavHistoryQueryResultNode::nsNavHistoryQueryResultNode( 1.1772 + const nsACString& aTitle, const nsACString& aIconURI, 1.1773 + const nsACString& aQueryURI) : 1.1774 + nsNavHistoryContainerResultNode(aQueryURI, aTitle, aIconURI, 1.1775 + nsNavHistoryResultNode::RESULT_TYPE_QUERY, 1.1776 + true, nullptr), 1.1777 + mLiveUpdate(QUERYUPDATE_COMPLEX_WITH_BOOKMARKS), 1.1778 + mHasSearchTerms(false), 1.1779 + mContentsValid(false), 1.1780 + mBatchChanges(0) 1.1781 +{ 1.1782 +} 1.1783 + 1.1784 +nsNavHistoryQueryResultNode::nsNavHistoryQueryResultNode( 1.1785 + const nsACString& aTitle, const nsACString& aIconURI, 1.1786 + const nsCOMArray<nsNavHistoryQuery>& aQueries, 1.1787 + nsNavHistoryQueryOptions* aOptions) : 1.1788 + nsNavHistoryContainerResultNode(EmptyCString(), aTitle, aIconURI, 1.1789 + nsNavHistoryResultNode::RESULT_TYPE_QUERY, 1.1790 + true, aOptions), 1.1791 + mQueries(aQueries), 1.1792 + mContentsValid(false), 1.1793 + mBatchChanges(0), 1.1794 + mTransitions(mQueries[0]->Transitions()) 1.1795 +{ 1.1796 + NS_ASSERTION(aQueries.Count() > 0, "Must have at least one query"); 1.1797 + 1.1798 + nsNavHistory* history = nsNavHistory::GetHistoryService(); 1.1799 + NS_ASSERTION(history, "History service missing"); 1.1800 + if (history) { 1.1801 + mLiveUpdate = history->GetUpdateRequirements(mQueries, mOptions, 1.1802 + &mHasSearchTerms); 1.1803 + } 1.1804 + 1.1805 + // Collect transitions shared by all queries. 1.1806 + for (int32_t i = 1; i < mQueries.Count(); ++i) { 1.1807 + const nsTArray<uint32_t>& queryTransitions = mQueries[i]->Transitions(); 1.1808 + for (uint32_t j = 0; j < mTransitions.Length() ; ++j) { 1.1809 + uint32_t transition = mTransitions.SafeElementAt(j, 0); 1.1810 + if (transition && !queryTransitions.Contains(transition)) 1.1811 + mTransitions.RemoveElement(transition); 1.1812 + } 1.1813 + } 1.1814 +} 1.1815 + 1.1816 +nsNavHistoryQueryResultNode::nsNavHistoryQueryResultNode( 1.1817 + const nsACString& aTitle, const nsACString& aIconURI, 1.1818 + PRTime aTime, 1.1819 + const nsCOMArray<nsNavHistoryQuery>& aQueries, 1.1820 + nsNavHistoryQueryOptions* aOptions) : 1.1821 + nsNavHistoryContainerResultNode(EmptyCString(), aTitle, aTime, aIconURI, 1.1822 + nsNavHistoryResultNode::RESULT_TYPE_QUERY, 1.1823 + true, aOptions), 1.1824 + mQueries(aQueries), 1.1825 + mContentsValid(false), 1.1826 + mBatchChanges(0), 1.1827 + mTransitions(mQueries[0]->Transitions()) 1.1828 +{ 1.1829 + NS_ASSERTION(aQueries.Count() > 0, "Must have at least one query"); 1.1830 + 1.1831 + nsNavHistory* history = nsNavHistory::GetHistoryService(); 1.1832 + NS_ASSERTION(history, "History service missing"); 1.1833 + if (history) { 1.1834 + mLiveUpdate = history->GetUpdateRequirements(mQueries, mOptions, 1.1835 + &mHasSearchTerms); 1.1836 + } 1.1837 + 1.1838 + // Collect transitions shared by all queries. 1.1839 + for (int32_t i = 1; i < mQueries.Count(); ++i) { 1.1840 + const nsTArray<uint32_t>& queryTransitions = mQueries[i]->Transitions(); 1.1841 + for (uint32_t j = 0; j < mTransitions.Length() ; ++j) { 1.1842 + uint32_t transition = mTransitions.SafeElementAt(j, 0); 1.1843 + if (transition && !queryTransitions.Contains(transition)) 1.1844 + mTransitions.RemoveElement(transition); 1.1845 + } 1.1846 + } 1.1847 +} 1.1848 + 1.1849 +nsNavHistoryQueryResultNode::~nsNavHistoryQueryResultNode() { 1.1850 + // Remove this node from result's observers. We don't need to be notified 1.1851 + // anymore. 1.1852 + if (mResult && mResult->mAllBookmarksObservers.IndexOf(this) != 1.1853 + mResult->mAllBookmarksObservers.NoIndex) 1.1854 + mResult->RemoveAllBookmarksObserver(this); 1.1855 + if (mResult && mResult->mHistoryObservers.IndexOf(this) != 1.1856 + mResult->mHistoryObservers.NoIndex) 1.1857 + mResult->RemoveHistoryObserver(this); 1.1858 +} 1.1859 + 1.1860 +/** 1.1861 + * Whoever made us may want non-expanding queries. However, we always expand 1.1862 + * when we are the root node, or else asking for non-expanding queries would be 1.1863 + * useless. A query node is not expandable if excludeItems is set or if 1.1864 + * expandQueries is unset. 1.1865 + */ 1.1866 +bool 1.1867 +nsNavHistoryQueryResultNode::CanExpand() 1.1868 +{ 1.1869 + if (IsContainersQuery()) 1.1870 + return true; 1.1871 + 1.1872 + // If ExcludeItems is set on the root or on the node itself, don't expand. 1.1873 + if ((mResult && mResult->mRootNode->mOptions->ExcludeItems()) || 1.1874 + Options()->ExcludeItems()) 1.1875 + return false; 1.1876 + 1.1877 + // Check the ancestor container. 1.1878 + nsNavHistoryQueryOptions* options = GetGeneratingOptions(); 1.1879 + if (options) { 1.1880 + if (options->ExcludeItems()) 1.1881 + return false; 1.1882 + if (options->ExpandQueries()) 1.1883 + return true; 1.1884 + } 1.1885 + 1.1886 + if (mResult && mResult->mRootNode == this) 1.1887 + return true; 1.1888 + 1.1889 + return false; 1.1890 +} 1.1891 + 1.1892 + 1.1893 +/** 1.1894 + * Some query with a particular result type can contain other queries. They 1.1895 + * must be always expandable 1.1896 + */ 1.1897 +bool 1.1898 +nsNavHistoryQueryResultNode::IsContainersQuery() 1.1899 +{ 1.1900 + uint16_t resultType = Options()->ResultType(); 1.1901 + return resultType == nsINavHistoryQueryOptions::RESULTS_AS_DATE_QUERY || 1.1902 + resultType == nsINavHistoryQueryOptions::RESULTS_AS_DATE_SITE_QUERY || 1.1903 + resultType == nsINavHistoryQueryOptions::RESULTS_AS_TAG_QUERY || 1.1904 + resultType == nsINavHistoryQueryOptions::RESULTS_AS_SITE_QUERY; 1.1905 +} 1.1906 + 1.1907 + 1.1908 +/** 1.1909 + * Here we do not want to call ContainerResultNode::OnRemoving since our own 1.1910 + * ClearChildren will do the same thing and more (unregister the observers). 1.1911 + * The base ResultNode::OnRemoving will clear some regular node stats, so it 1.1912 + * is OK. 1.1913 + */ 1.1914 +void 1.1915 +nsNavHistoryQueryResultNode::OnRemoving() 1.1916 +{ 1.1917 + nsNavHistoryResultNode::OnRemoving(); 1.1918 + ClearChildren(true); 1.1919 + mResult = nullptr; 1.1920 +} 1.1921 + 1.1922 + 1.1923 +/** 1.1924 + * Marks the container as open, rebuilding results if they are invalid. We 1.1925 + * may still have valid results if the container was previously open and 1.1926 + * nothing happened since closing it. 1.1927 + * 1.1928 + * We do not handle CloseContainer specially. The default one just marks the 1.1929 + * container as closed, but doesn't actually mark the results as invalid. 1.1930 + * The results will be invalidated by the next history or bookmark 1.1931 + * notification that comes in. This means if you open and close the item 1.1932 + * without anything happening in between, it will be fast (this actually 1.1933 + * happens when results are used as menus). 1.1934 + */ 1.1935 +nsresult 1.1936 +nsNavHistoryQueryResultNode::OpenContainer() 1.1937 +{ 1.1938 + NS_ASSERTION(!mExpanded, "Container must be closed to open it"); 1.1939 + mExpanded = true; 1.1940 + 1.1941 + nsresult rv; 1.1942 + 1.1943 + if (!CanExpand()) 1.1944 + return NS_OK; 1.1945 + if (!mContentsValid) { 1.1946 + rv = FillChildren(); 1.1947 + NS_ENSURE_SUCCESS(rv, rv); 1.1948 + } 1.1949 + 1.1950 + rv = NotifyOnStateChange(STATE_CLOSED); 1.1951 + NS_ENSURE_SUCCESS(rv, rv); 1.1952 + 1.1953 + return NS_OK; 1.1954 +} 1.1955 + 1.1956 + 1.1957 +/** 1.1958 + * When we have valid results we can always give an exact answer. When we 1.1959 + * don't we just assume we'll have results, since actually doing the query 1.1960 + * might be hard. This is used to draw twisties on the tree, so precise results 1.1961 + * don't matter. 1.1962 + */ 1.1963 +NS_IMETHODIMP 1.1964 +nsNavHistoryQueryResultNode::GetHasChildren(bool* aHasChildren) 1.1965 +{ 1.1966 + *aHasChildren = false; 1.1967 + 1.1968 + if (!CanExpand()) { 1.1969 + return NS_OK; 1.1970 + } 1.1971 + 1.1972 + uint16_t resultType = mOptions->ResultType(); 1.1973 + 1.1974 + // Tags are always populated, otherwise they are removed. 1.1975 + if (resultType == nsINavHistoryQueryOptions::RESULTS_AS_TAG_CONTENTS) { 1.1976 + *aHasChildren = true; 1.1977 + return NS_OK; 1.1978 + } 1.1979 + 1.1980 + // For tag containers query we must check if we have any tag 1.1981 + if (resultType == nsINavHistoryQueryOptions::RESULTS_AS_TAG_QUERY) { 1.1982 + nsCOMPtr<nsITaggingService> tagging = 1.1983 + do_GetService(NS_TAGGINGSERVICE_CONTRACTID); 1.1984 + if (tagging) { 1.1985 + bool hasTags; 1.1986 + *aHasChildren = NS_SUCCEEDED(tagging->GetHasTags(&hasTags)) && hasTags; 1.1987 + } 1.1988 + return NS_OK; 1.1989 + } 1.1990 + 1.1991 + // For history containers query we must check if we have any history 1.1992 + if (resultType == nsINavHistoryQueryOptions::RESULTS_AS_DATE_QUERY || 1.1993 + resultType == nsINavHistoryQueryOptions::RESULTS_AS_DATE_SITE_QUERY || 1.1994 + resultType == nsINavHistoryQueryOptions::RESULTS_AS_SITE_QUERY) { 1.1995 + nsNavHistory* history = nsNavHistory::GetHistoryService(); 1.1996 + NS_ENSURE_TRUE(history, NS_ERROR_OUT_OF_MEMORY); 1.1997 + return history->GetHasHistoryEntries(aHasChildren); 1.1998 + } 1.1999 + 1.2000 + //XXX: For other containers queries we must: 1.2001 + // 1. If it's open, just check mChildren for containers 1.2002 + // 2. Else null the view (keep it in a var), open container, check mChildren 1.2003 + // for containers, close container, reset the view 1.2004 + 1.2005 + if (mContentsValid) { 1.2006 + *aHasChildren = (mChildren.Count() > 0); 1.2007 + return NS_OK; 1.2008 + } 1.2009 + *aHasChildren = true; 1.2010 + return NS_OK; 1.2011 +} 1.2012 + 1.2013 + 1.2014 +/** 1.2015 + * This doesn't just return mURI because in the case of queries that may 1.2016 + * be lazily constructed from the query objects. 1.2017 + */ 1.2018 +NS_IMETHODIMP 1.2019 +nsNavHistoryQueryResultNode::GetUri(nsACString& aURI) 1.2020 +{ 1.2021 + nsresult rv = VerifyQueriesSerialized(); 1.2022 + NS_ENSURE_SUCCESS(rv, rv); 1.2023 + aURI = mURI; 1.2024 + return NS_OK; 1.2025 +} 1.2026 + 1.2027 + 1.2028 +NS_IMETHODIMP 1.2029 +nsNavHistoryQueryResultNode::GetFolderItemId(int64_t* aItemId) 1.2030 +{ 1.2031 + *aItemId = mItemId; 1.2032 + return NS_OK; 1.2033 +} 1.2034 + 1.2035 + 1.2036 +NS_IMETHODIMP 1.2037 +nsNavHistoryQueryResultNode::GetQueries(uint32_t* queryCount, 1.2038 + nsINavHistoryQuery*** queries) 1.2039 +{ 1.2040 + nsresult rv = VerifyQueriesParsed(); 1.2041 + NS_ENSURE_SUCCESS(rv, rv); 1.2042 + NS_ASSERTION(mQueries.Count() > 0, "Must have >= 1 query"); 1.2043 + 1.2044 + *queries = static_cast<nsINavHistoryQuery**> 1.2045 + (nsMemory::Alloc(mQueries.Count() * sizeof(nsINavHistoryQuery*))); 1.2046 + NS_ENSURE_TRUE(*queries, NS_ERROR_OUT_OF_MEMORY); 1.2047 + 1.2048 + for (int32_t i = 0; i < mQueries.Count(); ++i) 1.2049 + NS_ADDREF((*queries)[i] = mQueries[i]); 1.2050 + *queryCount = mQueries.Count(); 1.2051 + return NS_OK; 1.2052 +} 1.2053 + 1.2054 + 1.2055 +NS_IMETHODIMP 1.2056 +nsNavHistoryQueryResultNode::GetQueryOptions( 1.2057 + nsINavHistoryQueryOptions** aQueryOptions) 1.2058 +{ 1.2059 + *aQueryOptions = Options(); 1.2060 + NS_ADDREF(*aQueryOptions); 1.2061 + return NS_OK; 1.2062 +} 1.2063 + 1.2064 +/** 1.2065 + * Safe options getter, ensures queries are parsed first. 1.2066 + */ 1.2067 +nsNavHistoryQueryOptions* 1.2068 +nsNavHistoryQueryResultNode::Options() 1.2069 +{ 1.2070 + nsresult rv = VerifyQueriesParsed(); 1.2071 + if (NS_FAILED(rv)) 1.2072 + return nullptr; 1.2073 + NS_ASSERTION(mOptions, "Options invalid, cannot generate from URI"); 1.2074 + return mOptions; 1.2075 +} 1.2076 + 1.2077 + 1.2078 +nsresult 1.2079 +nsNavHistoryQueryResultNode::VerifyQueriesParsed() 1.2080 +{ 1.2081 + if (mQueries.Count() > 0) { 1.2082 + NS_ASSERTION(mOptions, "If a result has queries, it also needs options"); 1.2083 + return NS_OK; 1.2084 + } 1.2085 + NS_ASSERTION(!mURI.IsEmpty(), 1.2086 + "Query nodes must have either a URI or query/options"); 1.2087 + 1.2088 + nsNavHistory* history = nsNavHistory::GetHistoryService(); 1.2089 + NS_ENSURE_TRUE(history, NS_ERROR_OUT_OF_MEMORY); 1.2090 + 1.2091 + nsresult rv = history->QueryStringToQueryArray(mURI, &mQueries, 1.2092 + getter_AddRefs(mOptions)); 1.2093 + NS_ENSURE_SUCCESS(rv, rv); 1.2094 + 1.2095 + mLiveUpdate = history->GetUpdateRequirements(mQueries, mOptions, 1.2096 + &mHasSearchTerms); 1.2097 + return NS_OK; 1.2098 +} 1.2099 + 1.2100 + 1.2101 +nsresult 1.2102 +nsNavHistoryQueryResultNode::VerifyQueriesSerialized() 1.2103 +{ 1.2104 + if (!mURI.IsEmpty()) { 1.2105 + return NS_OK; 1.2106 + } 1.2107 + NS_ASSERTION(mQueries.Count() > 0 && mOptions, 1.2108 + "Query nodes must have either a URI or query/options"); 1.2109 + 1.2110 + nsTArray<nsINavHistoryQuery*> flatQueries; 1.2111 + flatQueries.SetCapacity(mQueries.Count()); 1.2112 + for (int32_t i = 0; i < mQueries.Count(); ++i) 1.2113 + flatQueries.AppendElement(static_cast<nsINavHistoryQuery*> 1.2114 + (mQueries.ObjectAt(i))); 1.2115 + 1.2116 + nsNavHistory* history = nsNavHistory::GetHistoryService(); 1.2117 + NS_ENSURE_TRUE(history, NS_ERROR_OUT_OF_MEMORY); 1.2118 + 1.2119 + nsresult rv = history->QueriesToQueryString(flatQueries.Elements(), 1.2120 + flatQueries.Length(), 1.2121 + mOptions, mURI); 1.2122 + NS_ENSURE_SUCCESS(rv, rv); 1.2123 + NS_ENSURE_STATE(!mURI.IsEmpty()); 1.2124 + return NS_OK; 1.2125 +} 1.2126 + 1.2127 + 1.2128 +nsresult 1.2129 +nsNavHistoryQueryResultNode::FillChildren() 1.2130 +{ 1.2131 + NS_ASSERTION(!mContentsValid, 1.2132 + "Don't call FillChildren when contents are valid"); 1.2133 + NS_ASSERTION(mChildren.Count() == 0, 1.2134 + "We are trying to fill children when there already are some"); 1.2135 + 1.2136 + nsNavHistory* history = nsNavHistory::GetHistoryService(); 1.2137 + NS_ENSURE_TRUE(history, NS_ERROR_OUT_OF_MEMORY); 1.2138 + 1.2139 + // get the results from the history service 1.2140 + nsresult rv = VerifyQueriesParsed(); 1.2141 + NS_ENSURE_SUCCESS(rv, rv); 1.2142 + rv = history->GetQueryResults(this, mQueries, mOptions, &mChildren); 1.2143 + NS_ENSURE_SUCCESS(rv, rv); 1.2144 + 1.2145 + // it is important to call FillStats to fill in the parents on all 1.2146 + // nodes and the result node pointers on the containers 1.2147 + FillStats(); 1.2148 + 1.2149 + uint16_t sortType = GetSortType(); 1.2150 + 1.2151 + if (mResult && mResult->mNeedsToApplySortingMode) { 1.2152 + // We should repopulate container and then apply sortingMode. To avoid 1.2153 + // sorting 2 times we simply do that here. 1.2154 + mResult->SetSortingMode(mResult->mSortingMode); 1.2155 + } 1.2156 + else if (mOptions->QueryType() != nsINavHistoryQueryOptions::QUERY_TYPE_HISTORY || 1.2157 + sortType != nsINavHistoryQueryOptions::SORT_BY_NONE) { 1.2158 + // The default SORT_BY_NONE sorts by the bookmark index (position), 1.2159 + // which we do not have for history queries. 1.2160 + // Once we've computed all tree stats, we can sort, because containers will 1.2161 + // then have proper visit counts and dates. 1.2162 + SortComparator comparator = GetSortingComparator(GetSortType()); 1.2163 + if (comparator) { 1.2164 + nsAutoCString sortingAnnotation; 1.2165 + GetSortingAnnotation(sortingAnnotation); 1.2166 + // Usually containers queries results comes already sorted from the 1.2167 + // database, but some locales could have special rules to sort by title. 1.2168 + // RecursiveSort won't apply these rules to containers in containers 1.2169 + // queries because when setting sortingMode on the result we want to sort 1.2170 + // contained items (bug 473157). 1.2171 + // Base container RecursiveSort will sort both our children and all 1.2172 + // descendants, and is used in this case because we have to do manual 1.2173 + // title sorting. 1.2174 + // Query RecursiveSort will instead only sort descendants if we are a 1.2175 + // constinaersQuery, e.g. a grouped query that will return other queries. 1.2176 + // For other type of queries it will act as the base one. 1.2177 + if (IsContainersQuery() && 1.2178 + sortType == mOptions->SortingMode() && 1.2179 + (sortType == nsINavHistoryQueryOptions::SORT_BY_TITLE_ASCENDING || 1.2180 + sortType == nsINavHistoryQueryOptions::SORT_BY_TITLE_DESCENDING)) 1.2181 + nsNavHistoryContainerResultNode::RecursiveSort(sortingAnnotation.get(), comparator); 1.2182 + else 1.2183 + RecursiveSort(sortingAnnotation.get(), comparator); 1.2184 + } 1.2185 + } 1.2186 + 1.2187 + // if we are limiting our results remove items from the end of the 1.2188 + // mChildren array after sorting. This is done for root node only. 1.2189 + // note, if count < max results, we won't do anything. 1.2190 + if (!mParent && mOptions->MaxResults()) { 1.2191 + while ((uint32_t)mChildren.Count() > mOptions->MaxResults()) 1.2192 + mChildren.RemoveObjectAt(mChildren.Count() - 1); 1.2193 + } 1.2194 + 1.2195 + nsNavHistoryResult* result = GetResult(); 1.2196 + NS_ENSURE_STATE(result); 1.2197 + 1.2198 + if (mOptions->QueryType() == nsINavHistoryQueryOptions::QUERY_TYPE_HISTORY || 1.2199 + mOptions->QueryType() == nsINavHistoryQueryOptions::QUERY_TYPE_UNIFIED) { 1.2200 + // Date containers that contain site containers have no reason to observe 1.2201 + // history, if the inside site container is expanded it will update, 1.2202 + // otherwise we are going to refresh the parent query. 1.2203 + if (!mParent || mParent->mOptions->ResultType() != nsINavHistoryQueryOptions::RESULTS_AS_DATE_SITE_QUERY) { 1.2204 + // register with the result for history updates 1.2205 + result->AddHistoryObserver(this); 1.2206 + } 1.2207 + } 1.2208 + 1.2209 + if (mOptions->QueryType() == nsINavHistoryQueryOptions::QUERY_TYPE_BOOKMARKS || 1.2210 + mOptions->QueryType() == nsINavHistoryQueryOptions::QUERY_TYPE_UNIFIED || 1.2211 + mLiveUpdate == QUERYUPDATE_COMPLEX_WITH_BOOKMARKS || 1.2212 + mHasSearchTerms) { 1.2213 + // register with the result for bookmark updates 1.2214 + result->AddAllBookmarksObserver(this); 1.2215 + } 1.2216 + 1.2217 + mContentsValid = true; 1.2218 + return NS_OK; 1.2219 +} 1.2220 + 1.2221 + 1.2222 +/** 1.2223 + * Call with unregister = false when we are going to update the children (for 1.2224 + * example, when the container is open). This will clear the list and notify 1.2225 + * all the children that they are going away. 1.2226 + * 1.2227 + * When the results are becoming invalid and we are not going to refresh them, 1.2228 + * set unregister = true, which will unregister the listener from the 1.2229 + * result if any. We use unregister = false when we are refreshing the list 1.2230 + * immediately so want to stay a notifier. 1.2231 + */ 1.2232 +void 1.2233 +nsNavHistoryQueryResultNode::ClearChildren(bool aUnregister) 1.2234 +{ 1.2235 + for (int32_t i = 0; i < mChildren.Count(); ++i) 1.2236 + mChildren[i]->OnRemoving(); 1.2237 + mChildren.Clear(); 1.2238 + 1.2239 + if (aUnregister && mContentsValid) { 1.2240 + nsNavHistoryResult* result = GetResult(); 1.2241 + if (result) { 1.2242 + result->RemoveHistoryObserver(this); 1.2243 + result->RemoveAllBookmarksObserver(this); 1.2244 + } 1.2245 + } 1.2246 + mContentsValid = false; 1.2247 +} 1.2248 + 1.2249 + 1.2250 +/** 1.2251 + * This is called to update the result when something has changed that we 1.2252 + * can not incrementally update. 1.2253 + */ 1.2254 +nsresult 1.2255 +nsNavHistoryQueryResultNode::Refresh() 1.2256 +{ 1.2257 + nsNavHistoryResult* result = GetResult(); 1.2258 + NS_ENSURE_STATE(result); 1.2259 + if (result->mBatchInProgress) { 1.2260 + result->requestRefresh(this); 1.2261 + return NS_OK; 1.2262 + } 1.2263 + 1.2264 + // This is not a root node but it does not have a parent - this means that 1.2265 + // the node has already been cleared and it is now called, because it was 1.2266 + // left in a local copy of the observers array. 1.2267 + if (mIndentLevel > -1 && !mParent) 1.2268 + return NS_OK; 1.2269 + 1.2270 + // Do not refresh if we are not expanded or if we are child of a query 1.2271 + // containing other queries. In this case calling Refresh for each child 1.2272 + // query could cause a major slowdown. We should not refresh nested 1.2273 + // queries, since we will already refresh the parent one. 1.2274 + if (!mExpanded || 1.2275 + (mParent && mParent->IsQuery() && 1.2276 + mParent->GetAsQuery()->IsContainersQuery())) { 1.2277 + // Don't update, just invalidate and unhook 1.2278 + ClearChildren(true); 1.2279 + return NS_OK; // no updates in tree state 1.2280 + } 1.2281 + 1.2282 + if (mLiveUpdate == QUERYUPDATE_COMPLEX_WITH_BOOKMARKS) 1.2283 + ClearChildren(true); 1.2284 + else 1.2285 + ClearChildren(false); 1.2286 + 1.2287 + // Ignore errors from FillChildren, since we will still want to refresh 1.2288 + // the tree (there just might not be anything in it on error). 1.2289 + (void)FillChildren(); 1.2290 + 1.2291 + NOTIFY_RESULT_OBSERVERS(result, InvalidateContainer(TO_CONTAINER(this))); 1.2292 + return NS_OK; 1.2293 +} 1.2294 + 1.2295 + 1.2296 +/** 1.2297 + * Here, we override GetSortType to return the current sorting for this 1.2298 + * query. GetSortType is used when dynamically inserting query results so we 1.2299 + * can see which comparator we should use to find the proper insertion point 1.2300 + * (it shouldn't be called from folder containers which maintain their own 1.2301 + * sorting). 1.2302 + * 1.2303 + * Normally, the container just forwards it up the chain. This is what we want 1.2304 + * for host groups, for example. For queries, we often want to use the query's 1.2305 + * sorting mode. 1.2306 + * 1.2307 + * However, we only use this query node's sorting when it is not the root. 1.2308 + * When it is the root, we use the result's sorting mode. This is because 1.2309 + * there are two cases: 1.2310 + * - You are looking at a bookmark hierarchy that contains an embedded 1.2311 + * result. We should always use the query's sort ordering since the result 1.2312 + * node's headers have nothing to do with us (and are disabled). 1.2313 + * - You are looking at a query in the tree. In this case, we want the 1.2314 + * result sorting to override ours (it should be initialized to the same 1.2315 + * sorting mode). 1.2316 + */ 1.2317 +uint16_t 1.2318 +nsNavHistoryQueryResultNode::GetSortType() 1.2319 +{ 1.2320 + if (mParent) 1.2321 + return mOptions->SortingMode(); 1.2322 + if (mResult) 1.2323 + return mResult->mSortingMode; 1.2324 + 1.2325 + // This is a detached container, just use natural order. 1.2326 + return nsINavHistoryQueryOptions::SORT_BY_NONE; 1.2327 +} 1.2328 + 1.2329 + 1.2330 +void 1.2331 +nsNavHistoryQueryResultNode::GetSortingAnnotation(nsACString& aAnnotation) { 1.2332 + if (mParent) { 1.2333 + // use our sorting, we are not the root 1.2334 + mOptions->GetSortingAnnotation(aAnnotation); 1.2335 + } 1.2336 + else if (mResult) { 1.2337 + aAnnotation.Assign(mResult->mSortingAnnotation); 1.2338 + } 1.2339 +} 1.2340 + 1.2341 +void 1.2342 +nsNavHistoryQueryResultNode::RecursiveSort( 1.2343 + const char* aData, SortComparator aComparator) 1.2344 +{ 1.2345 + void* data = const_cast<void*>(static_cast<const void*>(aData)); 1.2346 + 1.2347 + if (!IsContainersQuery()) 1.2348 + mChildren.Sort(aComparator, data); 1.2349 + 1.2350 + for (int32_t i = 0; i < mChildren.Count(); ++i) { 1.2351 + if (mChildren[i]->IsContainer()) 1.2352 + mChildren[i]->GetAsContainer()->RecursiveSort(aData, aComparator); 1.2353 + } 1.2354 +} 1.2355 + 1.2356 + 1.2357 +NS_IMETHODIMP 1.2358 +nsNavHistoryQueryResultNode::OnBeginUpdateBatch() 1.2359 +{ 1.2360 + return NS_OK; 1.2361 +} 1.2362 + 1.2363 + 1.2364 +NS_IMETHODIMP 1.2365 +nsNavHistoryQueryResultNode::OnEndUpdateBatch() 1.2366 +{ 1.2367 + // If the query has no children it's possible it's not yet listening to 1.2368 + // bookmarks changes, in such a case it's safer to force a refresh to gather 1.2369 + // eventual new nodes matching query options. 1.2370 + if (mChildren.Count() == 0) { 1.2371 + nsresult rv = Refresh(); 1.2372 + NS_ENSURE_SUCCESS(rv, rv); 1.2373 + } 1.2374 + 1.2375 + mBatchChanges = 0; 1.2376 + return NS_OK; 1.2377 +} 1.2378 + 1.2379 +static nsresult setHistoryDetailsCallback(nsNavHistoryResultNode* aNode, 1.2380 + const void* aClosure, 1.2381 + const nsNavHistoryResult* aResult) 1.2382 +{ 1.2383 + const nsNavHistoryResultNode* updatedNode = 1.2384 + static_cast<const nsNavHistoryResultNode*>(aClosure); 1.2385 + 1.2386 + aNode->mAccessCount = updatedNode->mAccessCount; 1.2387 + aNode->mTime = updatedNode->mTime; 1.2388 + aNode->mFrecency = updatedNode->mFrecency; 1.2389 + aNode->mHidden = updatedNode->mHidden; 1.2390 + 1.2391 + return NS_OK; 1.2392 +} 1.2393 + 1.2394 +/** 1.2395 + * Here we need to update all copies of the URI we have with the new visit 1.2396 + * count, and potentially add a new entry in our query. This is the most 1.2397 + * common update operation and it is important that it be as efficient as 1.2398 + * possible. 1.2399 + */ 1.2400 +NS_IMETHODIMP 1.2401 +nsNavHistoryQueryResultNode::OnVisit(nsIURI* aURI, int64_t aVisitId, 1.2402 + PRTime aTime, int64_t aSessionId, 1.2403 + int64_t aReferringId, 1.2404 + uint32_t aTransitionType, 1.2405 + const nsACString& aGUID, 1.2406 + bool aHidden, 1.2407 + uint32_t* aAdded) 1.2408 +{ 1.2409 + if (aHidden && !mOptions->IncludeHidden()) 1.2410 + return NS_OK; 1.2411 + 1.2412 + nsNavHistoryResult* result = GetResult(); 1.2413 + NS_ENSURE_STATE(result); 1.2414 + if (result->mBatchInProgress && 1.2415 + ++mBatchChanges > MAX_BATCH_CHANGES_BEFORE_REFRESH) { 1.2416 + nsresult rv = Refresh(); 1.2417 + NS_ENSURE_SUCCESS(rv, rv); 1.2418 + return NS_OK; 1.2419 + } 1.2420 + 1.2421 + nsNavHistory* history = nsNavHistory::GetHistoryService(); 1.2422 + NS_ENSURE_TRUE(history, NS_ERROR_OUT_OF_MEMORY); 1.2423 + 1.2424 + switch(mLiveUpdate) { 1.2425 + case QUERYUPDATE_HOST: { 1.2426 + // For these simple yet common cases we can check the host ourselves 1.2427 + // before doing the overhead of creating a new result node. 1.2428 + MOZ_ASSERT(mQueries.Count() == 1, 1.2429 + "Host updated queries can have only one object"); 1.2430 + nsRefPtr<nsNavHistoryQuery> query = do_QueryObject(mQueries[0]); 1.2431 + 1.2432 + bool hasDomain; 1.2433 + query->GetHasDomain(&hasDomain); 1.2434 + if (!hasDomain) 1.2435 + return NS_OK; 1.2436 + 1.2437 + nsAutoCString host; 1.2438 + if (NS_FAILED(aURI->GetAsciiHost(host))) 1.2439 + return NS_OK; 1.2440 + 1.2441 + if (!query->Domain().Equals(host)) 1.2442 + return NS_OK; 1.2443 + 1.2444 + // Fall through to check the time, if the time is not present it will 1.2445 + // still match. 1.2446 + } 1.2447 + 1.2448 + case QUERYUPDATE_TIME: { 1.2449 + // For these simple yet common cases we can check the time ourselves 1.2450 + // before doing the overhead of creating a new result node. 1.2451 + MOZ_ASSERT(mQueries.Count() == 1, 1.2452 + "Time updated queries can have only one object"); 1.2453 + nsRefPtr<nsNavHistoryQuery> query = do_QueryObject(mQueries[0]); 1.2454 + 1.2455 + bool hasIt; 1.2456 + query->GetHasBeginTime(&hasIt); 1.2457 + if (hasIt) { 1.2458 + PRTime beginTime = history->NormalizeTime(query->BeginTimeReference(), 1.2459 + query->BeginTime()); 1.2460 + if (aTime < beginTime) 1.2461 + return NS_OK; // before our time range 1.2462 + } 1.2463 + query->GetHasEndTime(&hasIt); 1.2464 + if (hasIt) { 1.2465 + PRTime endTime = history->NormalizeTime(query->EndTimeReference(), 1.2466 + query->EndTime()); 1.2467 + if (aTime > endTime) 1.2468 + return NS_OK; // after our time range 1.2469 + } 1.2470 + // Now we know that our visit satisfies the time range, fallback to the 1.2471 + // QUERYUPDATE_SIMPLE case. 1.2472 + } 1.2473 + 1.2474 + case QUERYUPDATE_SIMPLE: { 1.2475 + // If all of the queries are filtered by some transitions, skip the 1.2476 + // update if aTransitionType doesn't match any of them. 1.2477 + if (mTransitions.Length() > 0 && !mTransitions.Contains(aTransitionType)) 1.2478 + return NS_OK; 1.2479 + 1.2480 + // The history service can tell us whether the new item should appear 1.2481 + // in the result. We first have to construct a node for it to check. 1.2482 + nsRefPtr<nsNavHistoryResultNode> addition; 1.2483 + nsresult rv = history->VisitIdToResultNode(aVisitId, mOptions, 1.2484 + getter_AddRefs(addition)); 1.2485 + NS_ENSURE_SUCCESS(rv, rv); 1.2486 + NS_ENSURE_STATE(addition); 1.2487 + addition->mTransitionType = aTransitionType; 1.2488 + if (!history->EvaluateQueryForNode(mQueries, mOptions, addition)) 1.2489 + return NS_OK; // don't need to include in our query 1.2490 + 1.2491 + if (mOptions->ResultType() == nsNavHistoryQueryOptions::RESULTS_AS_VISIT) { 1.2492 + // If this is a visit type query, just insert the new visit. We never 1.2493 + // update visits, only add or remove them. 1.2494 + rv = InsertSortedChild(addition); 1.2495 + NS_ENSURE_SUCCESS(rv, rv); 1.2496 + } else { 1.2497 + uint16_t sortType = GetSortType(); 1.2498 + bool updateSorting = 1.2499 + sortType == nsINavHistoryQueryOptions::SORT_BY_VISITCOUNT_ASCENDING || 1.2500 + sortType == nsINavHistoryQueryOptions::SORT_BY_VISITCOUNT_DESCENDING || 1.2501 + sortType == nsINavHistoryQueryOptions::SORT_BY_DATE_ASCENDING || 1.2502 + sortType == nsINavHistoryQueryOptions::SORT_BY_DATE_DESCENDING || 1.2503 + sortType == nsINavHistoryQueryOptions::SORT_BY_FRECENCY_ASCENDING || 1.2504 + sortType == nsINavHistoryQueryOptions::SORT_BY_FRECENCY_DESCENDING; 1.2505 + 1.2506 + if (!UpdateURIs(false, true, updateSorting, addition->mURI, 1.2507 + setHistoryDetailsCallback, 1.2508 + const_cast<void*>(static_cast<void*>(addition.get())))) { 1.2509 + // Couldn't find a node to update. 1.2510 + rv = InsertSortedChild(addition); 1.2511 + NS_ENSURE_SUCCESS(rv, rv); 1.2512 + } 1.2513 + } 1.2514 + 1.2515 + if (aAdded) 1.2516 + ++(*aAdded); 1.2517 + 1.2518 + break; 1.2519 + } 1.2520 + 1.2521 + case QUERYUPDATE_COMPLEX: 1.2522 + case QUERYUPDATE_COMPLEX_WITH_BOOKMARKS: 1.2523 + // need to requery in complex cases 1.2524 + return Refresh(); 1.2525 + 1.2526 + default: 1.2527 + MOZ_ASSERT(false, "Invalid value for mLiveUpdate"); 1.2528 + return Refresh(); 1.2529 + } 1.2530 + 1.2531 + return NS_OK; 1.2532 +} 1.2533 + 1.2534 + 1.2535 +/** 1.2536 + * Find every node that matches this URI and rename it. We try to do 1.2537 + * incremental updates here, even when we are closed, because changing titles 1.2538 + * is easier than requerying if we are invalid. 1.2539 + * 1.2540 + * This actually gets called a lot. Typically, we will get an AddURI message 1.2541 + * when the user visits the page, and then the title will be set asynchronously 1.2542 + * when the title element of the page is parsed. 1.2543 + */ 1.2544 +NS_IMETHODIMP 1.2545 +nsNavHistoryQueryResultNode::OnTitleChanged(nsIURI* aURI, 1.2546 + const nsAString& aPageTitle, 1.2547 + const nsACString& aGUID) 1.2548 +{ 1.2549 + if (!mExpanded) { 1.2550 + // When we are not expanded, we don't update, just invalidate and unhook. 1.2551 + // It would still be pretty easy to traverse the results and update the 1.2552 + // titles, but when a title changes, its unlikely that it will be the only 1.2553 + // thing. Therefore, we just give up. 1.2554 + ClearChildren(true); 1.2555 + return NS_OK; // no updates in tree state 1.2556 + } 1.2557 + 1.2558 + nsNavHistoryResult* result = GetResult(); 1.2559 + NS_ENSURE_STATE(result); 1.2560 + if (result->mBatchInProgress && 1.2561 + ++mBatchChanges > MAX_BATCH_CHANGES_BEFORE_REFRESH) { 1.2562 + nsresult rv = Refresh(); 1.2563 + NS_ENSURE_SUCCESS(rv, rv); 1.2564 + return NS_OK; 1.2565 + } 1.2566 + 1.2567 + // compute what the new title should be 1.2568 + NS_ConvertUTF16toUTF8 newTitle(aPageTitle); 1.2569 + 1.2570 + bool onlyOneEntry = 1.2571 + mOptions->ResultType() == nsINavHistoryQueryOptions::RESULTS_AS_URI || 1.2572 + mOptions->ResultType() == nsINavHistoryQueryOptions::RESULTS_AS_TAG_CONTENTS; 1.2573 + 1.2574 + // See if our queries have any search term matching. 1.2575 + if (mHasSearchTerms) { 1.2576 + // Find all matching URI nodes. 1.2577 + nsCOMArray<nsNavHistoryResultNode> matches; 1.2578 + nsAutoCString spec; 1.2579 + nsresult rv = aURI->GetSpec(spec); 1.2580 + NS_ENSURE_SUCCESS(rv, rv); 1.2581 + RecursiveFindURIs(onlyOneEntry, this, spec, &matches); 1.2582 + if (matches.Count() == 0) { 1.2583 + // This could be a new node matching the query, thus we could need 1.2584 + // to add it to the result. 1.2585 + nsRefPtr<nsNavHistoryResultNode> node; 1.2586 + nsNavHistory* history = nsNavHistory::GetHistoryService(); 1.2587 + NS_ENSURE_TRUE(history, NS_ERROR_OUT_OF_MEMORY); 1.2588 + rv = history->URIToResultNode(aURI, mOptions, getter_AddRefs(node)); 1.2589 + NS_ENSURE_SUCCESS(rv, rv); 1.2590 + if (history->EvaluateQueryForNode(mQueries, mOptions, node)) { 1.2591 + rv = InsertSortedChild(node, true); 1.2592 + NS_ENSURE_SUCCESS(rv, rv); 1.2593 + } 1.2594 + } 1.2595 + for (int32_t i = 0; i < matches.Count(); ++i) { 1.2596 + // For each matched node we check if it passes the query filter, if not 1.2597 + // we remove the node from the result, otherwise we'll update the title 1.2598 + // later. 1.2599 + nsNavHistoryResultNode* node = matches[i]; 1.2600 + // We must check the node with the new title. 1.2601 + node->mTitle = newTitle; 1.2602 + 1.2603 + nsNavHistory* history = nsNavHistory::GetHistoryService(); 1.2604 + NS_ENSURE_TRUE(history, NS_ERROR_OUT_OF_MEMORY); 1.2605 + if (!history->EvaluateQueryForNode(mQueries, mOptions, node)) { 1.2606 + nsNavHistoryContainerResultNode* parent = node->mParent; 1.2607 + // URI nodes should always have parents 1.2608 + NS_ENSURE_TRUE(parent, NS_ERROR_UNEXPECTED); 1.2609 + int32_t childIndex = parent->FindChild(node); 1.2610 + NS_ASSERTION(childIndex >= 0, "Child not found in parent"); 1.2611 + parent->RemoveChildAt(childIndex); 1.2612 + } 1.2613 + } 1.2614 + } 1.2615 + 1.2616 + return ChangeTitles(aURI, newTitle, true, onlyOneEntry); 1.2617 +} 1.2618 + 1.2619 + 1.2620 +NS_IMETHODIMP 1.2621 +nsNavHistoryQueryResultNode::OnFrecencyChanged(nsIURI* aURI, 1.2622 + int32_t aNewFrecency, 1.2623 + const nsACString& aGUID, 1.2624 + bool aHidden, 1.2625 + PRTime aLastVisitDate) 1.2626 +{ 1.2627 + return NS_OK; 1.2628 +} 1.2629 + 1.2630 + 1.2631 +NS_IMETHODIMP 1.2632 +nsNavHistoryQueryResultNode::OnManyFrecenciesChanged() 1.2633 +{ 1.2634 + return NS_OK; 1.2635 +} 1.2636 + 1.2637 + 1.2638 +/** 1.2639 + * Here, we can always live update by just deleting all occurrences of 1.2640 + * the given URI. 1.2641 + */ 1.2642 +NS_IMETHODIMP 1.2643 +nsNavHistoryQueryResultNode::OnDeleteURI(nsIURI* aURI, 1.2644 + const nsACString& aGUID, 1.2645 + uint16_t aReason) 1.2646 +{ 1.2647 + nsNavHistoryResult* result = GetResult(); 1.2648 + NS_ENSURE_STATE(result); 1.2649 + if (result->mBatchInProgress && 1.2650 + ++mBatchChanges > MAX_BATCH_CHANGES_BEFORE_REFRESH) { 1.2651 + nsresult rv = Refresh(); 1.2652 + NS_ENSURE_SUCCESS(rv, rv); 1.2653 + return NS_OK; 1.2654 + } 1.2655 + 1.2656 + if (IsContainersQuery()) { 1.2657 + // Incremental updates of query returning queries are pretty much 1.2658 + // complicated. In this case it's possible one of the child queries has 1.2659 + // no more children and it should be removed. Unfortunately there is no 1.2660 + // way to know that without executing the child query and counting results. 1.2661 + nsresult rv = Refresh(); 1.2662 + NS_ENSURE_SUCCESS(rv, rv); 1.2663 + return NS_OK; 1.2664 + } 1.2665 + 1.2666 + bool onlyOneEntry = (mOptions->ResultType() == 1.2667 + nsINavHistoryQueryOptions::RESULTS_AS_URI || 1.2668 + mOptions->ResultType() == 1.2669 + nsINavHistoryQueryOptions::RESULTS_AS_TAG_CONTENTS); 1.2670 + nsAutoCString spec; 1.2671 + nsresult rv = aURI->GetSpec(spec); 1.2672 + NS_ENSURE_SUCCESS(rv, rv); 1.2673 + 1.2674 + nsCOMArray<nsNavHistoryResultNode> matches; 1.2675 + RecursiveFindURIs(onlyOneEntry, this, spec, &matches); 1.2676 + for (int32_t i = 0; i < matches.Count(); ++i) { 1.2677 + nsNavHistoryResultNode* node = matches[i]; 1.2678 + nsNavHistoryContainerResultNode* parent = node->mParent; 1.2679 + // URI nodes should always have parents 1.2680 + NS_ENSURE_TRUE(parent, NS_ERROR_UNEXPECTED); 1.2681 + 1.2682 + int32_t childIndex = parent->FindChild(node); 1.2683 + NS_ASSERTION(childIndex >= 0, "Child not found in parent"); 1.2684 + parent->RemoveChildAt(childIndex); 1.2685 + if (parent->mChildren.Count() == 0 && parent->IsQuery() && 1.2686 + parent->mIndentLevel > -1) { 1.2687 + // When query subcontainers (like hosts) get empty we should remove them 1.2688 + // as well. If the parent is not the root node, append it to our list 1.2689 + // and it will get evaluated later in the loop. 1.2690 + matches.AppendObject(parent); 1.2691 + } 1.2692 + } 1.2693 + return NS_OK; 1.2694 +} 1.2695 + 1.2696 + 1.2697 +NS_IMETHODIMP 1.2698 +nsNavHistoryQueryResultNode::OnClearHistory() 1.2699 +{ 1.2700 + nsresult rv = Refresh(); 1.2701 + NS_ENSURE_SUCCESS(rv, rv); 1.2702 + return NS_OK; 1.2703 +} 1.2704 + 1.2705 + 1.2706 +static nsresult setFaviconCallback(nsNavHistoryResultNode* aNode, 1.2707 + const void* aClosure, 1.2708 + const nsNavHistoryResult* aResult) 1.2709 +{ 1.2710 + const nsCString* newFavicon = static_cast<const nsCString*>(aClosure); 1.2711 + aNode->mFaviconURI = *newFavicon; 1.2712 + 1.2713 + if (aResult && (!aNode->mParent || aNode->mParent->AreChildrenVisible())) 1.2714 + NOTIFY_RESULT_OBSERVERS(aResult, NodeIconChanged(aNode)); 1.2715 + 1.2716 + return NS_OK; 1.2717 +} 1.2718 + 1.2719 + 1.2720 +NS_IMETHODIMP 1.2721 +nsNavHistoryQueryResultNode::OnPageChanged(nsIURI* aURI, 1.2722 + uint32_t aChangedAttribute, 1.2723 + const nsAString& aNewValue, 1.2724 + const nsACString& aGUID) 1.2725 +{ 1.2726 + nsAutoCString spec; 1.2727 + nsresult rv = aURI->GetSpec(spec); 1.2728 + NS_ENSURE_SUCCESS(rv, rv); 1.2729 + 1.2730 + switch (aChangedAttribute) { 1.2731 + case nsINavHistoryObserver::ATTRIBUTE_FAVICON: { 1.2732 + NS_ConvertUTF16toUTF8 newFavicon(aNewValue); 1.2733 + bool onlyOneEntry = (mOptions->ResultType() == 1.2734 + nsINavHistoryQueryOptions::RESULTS_AS_URI || 1.2735 + mOptions->ResultType() == 1.2736 + nsINavHistoryQueryOptions::RESULTS_AS_TAG_CONTENTS); 1.2737 + UpdateURIs(true, onlyOneEntry, false, spec, setFaviconCallback, 1.2738 + &newFavicon); 1.2739 + break; 1.2740 + } 1.2741 + default: 1.2742 + NS_WARNING("Unknown page changed notification"); 1.2743 + } 1.2744 + return NS_OK; 1.2745 +} 1.2746 + 1.2747 + 1.2748 +NS_IMETHODIMP 1.2749 +nsNavHistoryQueryResultNode::OnDeleteVisits(nsIURI* aURI, 1.2750 + PRTime aVisitTime, 1.2751 + const nsACString& aGUID, 1.2752 + uint16_t aReason, 1.2753 + uint32_t aTransitionType) 1.2754 +{ 1.2755 + NS_PRECONDITION(mOptions->QueryType() == nsINavHistoryQueryOptions::QUERY_TYPE_HISTORY, 1.2756 + "Bookmarks queries should not get a OnDeleteVisits notification"); 1.2757 + if (aVisitTime == 0) { 1.2758 + // All visits for this uri have been removed, but the uri won't be removed 1.2759 + // from the databse, most likely because it's a bookmark. For a history 1.2760 + // query this is equivalent to a onDeleteURI notification. 1.2761 + nsresult rv = OnDeleteURI(aURI, aGUID, aReason); 1.2762 + NS_ENSURE_SUCCESS(rv, rv); 1.2763 + } 1.2764 + if (aTransitionType > 0) { 1.2765 + // All visits for aTransitionType have been removed, if the query is 1.2766 + // filtering on such transition type, this is equivalent to an onDeleteURI 1.2767 + // notification. 1.2768 + if (mTransitions.Length() > 0 && mTransitions.Contains(aTransitionType)) { 1.2769 + nsresult rv = OnDeleteURI(aURI, aGUID, aReason); 1.2770 + NS_ENSURE_SUCCESS(rv, rv); 1.2771 + } 1.2772 + } 1.2773 + 1.2774 + return NS_OK; 1.2775 +} 1.2776 + 1.2777 +nsresult 1.2778 +nsNavHistoryQueryResultNode::NotifyIfTagsChanged(nsIURI* aURI) 1.2779 +{ 1.2780 + nsNavHistoryResult* result = GetResult(); 1.2781 + NS_ENSURE_STATE(result); 1.2782 + nsAutoCString spec; 1.2783 + nsresult rv = aURI->GetSpec(spec); 1.2784 + NS_ENSURE_SUCCESS(rv, rv); 1.2785 + bool onlyOneEntry = (mOptions->ResultType() == 1.2786 + nsINavHistoryQueryOptions::RESULTS_AS_URI || 1.2787 + mOptions->ResultType() == 1.2788 + nsINavHistoryQueryOptions::RESULTS_AS_TAG_CONTENTS 1.2789 + ); 1.2790 + 1.2791 + // Find matching URI nodes. 1.2792 + nsRefPtr<nsNavHistoryResultNode> node; 1.2793 + nsNavHistory* history = nsNavHistory::GetHistoryService(); 1.2794 + 1.2795 + nsCOMArray<nsNavHistoryResultNode> matches; 1.2796 + RecursiveFindURIs(onlyOneEntry, this, spec, &matches); 1.2797 + 1.2798 + if (matches.Count() == 0 && mHasSearchTerms && !mRemovingURI) { 1.2799 + // A new tag has been added, it's possible it matches our query. 1.2800 + NS_ENSURE_TRUE(history, NS_ERROR_OUT_OF_MEMORY); 1.2801 + rv = history->URIToResultNode(aURI, mOptions, getter_AddRefs(node)); 1.2802 + NS_ENSURE_SUCCESS(rv, rv); 1.2803 + if (history->EvaluateQueryForNode(mQueries, mOptions, node)) { 1.2804 + rv = InsertSortedChild(node, true); 1.2805 + NS_ENSURE_SUCCESS(rv, rv); 1.2806 + } 1.2807 + } 1.2808 + 1.2809 + for (int32_t i = 0; i < matches.Count(); ++i) { 1.2810 + nsNavHistoryResultNode* node = matches[i]; 1.2811 + // Force a tags update before checking the node. 1.2812 + node->mTags.SetIsVoid(true); 1.2813 + nsAutoString tags; 1.2814 + rv = node->GetTags(tags); 1.2815 + NS_ENSURE_SUCCESS(rv, rv); 1.2816 + // It's possible now this node does not respect anymore the conditions. 1.2817 + // In such a case it should be removed. 1.2818 + if (mHasSearchTerms && 1.2819 + !history->EvaluateQueryForNode(mQueries, mOptions, node)) { 1.2820 + nsNavHistoryContainerResultNode* parent = node->mParent; 1.2821 + // URI nodes should always have parents 1.2822 + NS_ENSURE_TRUE(parent, NS_ERROR_UNEXPECTED); 1.2823 + int32_t childIndex = parent->FindChild(node); 1.2824 + NS_ASSERTION(childIndex >= 0, "Child not found in parent"); 1.2825 + parent->RemoveChildAt(childIndex); 1.2826 + } 1.2827 + else { 1.2828 + NOTIFY_RESULT_OBSERVERS(result, NodeTagsChanged(node)); 1.2829 + } 1.2830 + } 1.2831 + 1.2832 + return NS_OK; 1.2833 +} 1.2834 + 1.2835 +/** 1.2836 + * These are the bookmark observer functions for query nodes. They listen 1.2837 + * for bookmark events and refresh the results if we have any dependence on 1.2838 + * the bookmark system. 1.2839 + */ 1.2840 +NS_IMETHODIMP 1.2841 +nsNavHistoryQueryResultNode::OnItemAdded(int64_t aItemId, 1.2842 + int64_t aParentId, 1.2843 + int32_t aIndex, 1.2844 + uint16_t aItemType, 1.2845 + nsIURI* aURI, 1.2846 + const nsACString& aTitle, 1.2847 + PRTime aDateAdded, 1.2848 + const nsACString& aGUID, 1.2849 + const nsACString& aParentGUID) 1.2850 +{ 1.2851 + if (aItemType == nsINavBookmarksService::TYPE_BOOKMARK && 1.2852 + mLiveUpdate != QUERYUPDATE_SIMPLE && mLiveUpdate != QUERYUPDATE_TIME) { 1.2853 + nsresult rv = Refresh(); 1.2854 + NS_ENSURE_SUCCESS(rv, rv); 1.2855 + } 1.2856 + return NS_OK; 1.2857 +} 1.2858 + 1.2859 + 1.2860 +NS_IMETHODIMP 1.2861 +nsNavHistoryQueryResultNode::OnItemRemoved(int64_t aItemId, 1.2862 + int64_t aParentId, 1.2863 + int32_t aIndex, 1.2864 + uint16_t aItemType, 1.2865 + nsIURI* aURI, 1.2866 + const nsACString& aGUID, 1.2867 + const nsACString& aParentGUID) 1.2868 +{ 1.2869 + mRemovingURI = aURI; 1.2870 + if (aItemType == nsINavBookmarksService::TYPE_BOOKMARK && 1.2871 + mLiveUpdate != QUERYUPDATE_SIMPLE && mLiveUpdate != QUERYUPDATE_TIME) { 1.2872 + nsresult rv = Refresh(); 1.2873 + NS_ENSURE_SUCCESS(rv, rv); 1.2874 + } 1.2875 + return NS_OK; 1.2876 +} 1.2877 + 1.2878 + 1.2879 +NS_IMETHODIMP 1.2880 +nsNavHistoryQueryResultNode::OnItemChanged(int64_t aItemId, 1.2881 + const nsACString& aProperty, 1.2882 + bool aIsAnnotationProperty, 1.2883 + const nsACString& aNewValue, 1.2884 + PRTime aLastModified, 1.2885 + uint16_t aItemType, 1.2886 + int64_t aParentId, 1.2887 + const nsACString& aGUID, 1.2888 + const nsACString& aParentGUID) 1.2889 +{ 1.2890 + // History observers should not get OnItemChanged 1.2891 + // but should get the corresponding history notifications instead. 1.2892 + // For bookmark queries, "all bookmark" observers should get OnItemChanged. 1.2893 + // For example, when a title of a bookmark changes, we want that to refresh. 1.2894 + 1.2895 + if (mLiveUpdate == QUERYUPDATE_COMPLEX_WITH_BOOKMARKS) { 1.2896 + switch (aItemType) { 1.2897 + case nsINavBookmarksService::TYPE_SEPARATOR: 1.2898 + // No separators in queries. 1.2899 + return NS_OK; 1.2900 + case nsINavBookmarksService::TYPE_FOLDER: 1.2901 + // Queries never result as "folders", but the tags-query results as 1.2902 + // special "tag" containers, which should follow their corresponding 1.2903 + // folders titles. 1.2904 + if (mOptions->ResultType() != nsINavHistoryQueryOptions::RESULTS_AS_TAG_QUERY) 1.2905 + return NS_OK; 1.2906 + default: 1.2907 + (void)Refresh(); 1.2908 + } 1.2909 + } 1.2910 + else { 1.2911 + // Some node could observe both bookmarks and history. But a node observing 1.2912 + // only history should never get a bookmark notification. 1.2913 + NS_WARN_IF_FALSE(mResult && (mResult->mIsAllBookmarksObserver || mResult->mIsBookmarkFolderObserver), 1.2914 + "history observers should not get OnItemChanged, but should get the corresponding history notifications instead"); 1.2915 + 1.2916 + // Tags in history queries are a special case since tags are per uri and 1.2917 + // we filter tags based on searchterms. 1.2918 + if (aItemType == nsINavBookmarksService::TYPE_BOOKMARK && 1.2919 + aProperty.EqualsLiteral("tags")) { 1.2920 + nsNavBookmarks* bookmarks = nsNavBookmarks::GetBookmarksService(); 1.2921 + NS_ENSURE_TRUE(bookmarks, NS_ERROR_OUT_OF_MEMORY); 1.2922 + nsCOMPtr<nsIURI> uri; 1.2923 + nsresult rv = bookmarks->GetBookmarkURI(aItemId, getter_AddRefs(uri)); 1.2924 + NS_ENSURE_SUCCESS(rv, rv); 1.2925 + rv = NotifyIfTagsChanged(uri); 1.2926 + NS_ENSURE_SUCCESS(rv, rv); 1.2927 + } 1.2928 + } 1.2929 + 1.2930 + return nsNavHistoryResultNode::OnItemChanged(aItemId, aProperty, 1.2931 + aIsAnnotationProperty, 1.2932 + aNewValue, aLastModified, 1.2933 + aItemType, aParentId, aGUID, 1.2934 + aParentGUID); 1.2935 +} 1.2936 + 1.2937 +NS_IMETHODIMP 1.2938 +nsNavHistoryQueryResultNode::OnItemVisited(int64_t aItemId, 1.2939 + int64_t aVisitId, 1.2940 + PRTime aTime, 1.2941 + uint32_t aTransitionType, 1.2942 + nsIURI* aURI, 1.2943 + int64_t aParentId, 1.2944 + const nsACString& aGUID, 1.2945 + const nsACString& aParentGUID) 1.2946 +{ 1.2947 + // for bookmark queries, "all bookmark" observer should get OnItemVisited 1.2948 + // but it is ignored. 1.2949 + if (mLiveUpdate != QUERYUPDATE_COMPLEX_WITH_BOOKMARKS) 1.2950 + NS_WARN_IF_FALSE(mResult && (mResult->mIsAllBookmarksObserver || mResult->mIsBookmarkFolderObserver), 1.2951 + "history observers should not get OnItemVisited, but should get OnVisit instead"); 1.2952 + return NS_OK; 1.2953 +} 1.2954 + 1.2955 +NS_IMETHODIMP 1.2956 +nsNavHistoryQueryResultNode::OnItemMoved(int64_t aFolder, 1.2957 + int64_t aOldParent, 1.2958 + int32_t aOldIndex, 1.2959 + int64_t aNewParent, 1.2960 + int32_t aNewIndex, 1.2961 + uint16_t aItemType, 1.2962 + const nsACString& aGUID, 1.2963 + const nsACString& aOldParentGUID, 1.2964 + const nsACString& aNewParentGUID) 1.2965 +{ 1.2966 + // 1. The query cannot be affected by the item's position 1.2967 + // 2. For the time being, we cannot optimize this not to update 1.2968 + // queries which are not restricted to some folders, due to way 1.2969 + // sub-queries are updated (see Refresh) 1.2970 + if (mLiveUpdate == QUERYUPDATE_COMPLEX_WITH_BOOKMARKS && 1.2971 + aItemType != nsINavBookmarksService::TYPE_SEPARATOR && 1.2972 + aOldParent != aNewParent) { 1.2973 + return Refresh(); 1.2974 + } 1.2975 + return NS_OK; 1.2976 +} 1.2977 + 1.2978 +/** 1.2979 + * HOW DYNAMIC FOLDER UPDATING WORKS 1.2980 + * 1.2981 + * When you create a result, it will automatically keep itself in sync with 1.2982 + * stuff that happens in the system. For folder nodes, this means changes to 1.2983 + * bookmarks. 1.2984 + * 1.2985 + * A folder will fill its children "when necessary." This means it is being 1.2986 + * opened or whether we need to see if it is empty for twisty drawing. It will 1.2987 + * then register its ID with the main result object that owns it. This result 1.2988 + * object will listen for all bookmark notifications and pass those 1.2989 + * notifications to folder nodes that have registered for that specific folder 1.2990 + * ID. 1.2991 + * 1.2992 + * When a bookmark folder is closed, it will not clear its children. Instead, 1.2993 + * it will keep them and also stay registered as a listener. This means that 1.2994 + * you can more quickly re-open the same folder without doing any work. This 1.2995 + * happens a lot for menus, and bookmarks don't change very often. 1.2996 + * 1.2997 + * When a message comes in and the folder is open, we will do the correct 1.2998 + * operations to keep ourselves in sync with the bookmark service. If the 1.2999 + * folder is closed, we just clear our list to mark it as invalid and 1.3000 + * unregister as a listener. This means we do not have to keep maintaining 1.3001 + * an up-to-date list for the entire bookmark menu structure in every place 1.3002 + * it is used. 1.3003 + */ 1.3004 +NS_IMPL_ISUPPORTS_INHERITED(nsNavHistoryFolderResultNode, 1.3005 + nsNavHistoryContainerResultNode, 1.3006 + nsINavHistoryQueryResultNode) 1.3007 + 1.3008 +nsNavHistoryFolderResultNode::nsNavHistoryFolderResultNode( 1.3009 + const nsACString& aTitle, nsNavHistoryQueryOptions* aOptions, 1.3010 + int64_t aFolderId) : 1.3011 + nsNavHistoryContainerResultNode(EmptyCString(), aTitle, EmptyCString(), 1.3012 + nsNavHistoryResultNode::RESULT_TYPE_FOLDER, 1.3013 + false, aOptions), 1.3014 + mContentsValid(false), 1.3015 + mQueryItemId(-1), 1.3016 + mIsRegisteredFolderObserver(false) 1.3017 +{ 1.3018 + mItemId = aFolderId; 1.3019 +} 1.3020 + 1.3021 +nsNavHistoryFolderResultNode::~nsNavHistoryFolderResultNode() 1.3022 +{ 1.3023 + if (mIsRegisteredFolderObserver && mResult) 1.3024 + mResult->RemoveBookmarkFolderObserver(this, mItemId); 1.3025 +} 1.3026 + 1.3027 + 1.3028 +/** 1.3029 + * Here we do not want to call ContainerResultNode::OnRemoving since our own 1.3030 + * ClearChildren will do the same thing and more (unregister the observers). 1.3031 + * The base ResultNode::OnRemoving will clear some regular node stats, so it is 1.3032 + * OK. 1.3033 + */ 1.3034 +void 1.3035 +nsNavHistoryFolderResultNode::OnRemoving() 1.3036 +{ 1.3037 + nsNavHistoryResultNode::OnRemoving(); 1.3038 + ClearChildren(true); 1.3039 + mResult = nullptr; 1.3040 +} 1.3041 + 1.3042 + 1.3043 +nsresult 1.3044 +nsNavHistoryFolderResultNode::OpenContainer() 1.3045 +{ 1.3046 + NS_ASSERTION(!mExpanded, "Container must be expanded to close it"); 1.3047 + nsresult rv; 1.3048 + 1.3049 + if (!mContentsValid) { 1.3050 + rv = FillChildren(); 1.3051 + NS_ENSURE_SUCCESS(rv, rv); 1.3052 + } 1.3053 + mExpanded = true; 1.3054 + 1.3055 + rv = NotifyOnStateChange(STATE_CLOSED); 1.3056 + NS_ENSURE_SUCCESS(rv, rv); 1.3057 + 1.3058 + return NS_OK; 1.3059 +} 1.3060 + 1.3061 + 1.3062 +/** 1.3063 + * The async version of OpenContainer. 1.3064 + */ 1.3065 +nsresult 1.3066 +nsNavHistoryFolderResultNode::OpenContainerAsync() 1.3067 +{ 1.3068 + NS_ASSERTION(!mExpanded, "Container already expanded when opening it"); 1.3069 + 1.3070 + // If the children are valid, open the container synchronously. This will be 1.3071 + // the case when the container has already been opened and any other time 1.3072 + // FillChildren or FillChildrenAsync has previously been called. 1.3073 + if (mContentsValid) 1.3074 + return OpenContainer(); 1.3075 + 1.3076 + nsresult rv = FillChildrenAsync(); 1.3077 + NS_ENSURE_SUCCESS(rv, rv); 1.3078 + 1.3079 + rv = NotifyOnStateChange(STATE_CLOSED); 1.3080 + NS_ENSURE_SUCCESS(rv, rv); 1.3081 + 1.3082 + return NS_OK; 1.3083 +} 1.3084 + 1.3085 + 1.3086 +/** 1.3087 + * @see nsNavHistoryQueryResultNode::HasChildren. The semantics here are a 1.3088 + * little different. Querying the contents of a bookmark folder is relatively 1.3089 + * fast and it is common to have empty folders. Therefore, we always want to 1.3090 + * return the correct result so that twisties are drawn properly. 1.3091 + */ 1.3092 +NS_IMETHODIMP 1.3093 +nsNavHistoryFolderResultNode::GetHasChildren(bool* aHasChildren) 1.3094 +{ 1.3095 + if (!mContentsValid) { 1.3096 + nsresult rv = FillChildren(); 1.3097 + NS_ENSURE_SUCCESS(rv, rv); 1.3098 + } 1.3099 + *aHasChildren = (mChildren.Count() > 0); 1.3100 + return NS_OK; 1.3101 +} 1.3102 + 1.3103 +/** 1.3104 + * @return the id of the item from which the folder node was generated, it 1.3105 + * could be either a concrete folder-itemId or the id used in a 1.3106 + * simple-folder-query-bookmark (place:folder=X). 1.3107 + */ 1.3108 +NS_IMETHODIMP 1.3109 +nsNavHistoryFolderResultNode::GetItemId(int64_t* aItemId) 1.3110 +{ 1.3111 + *aItemId = mQueryItemId == -1 ? mItemId : mQueryItemId; 1.3112 + return NS_OK; 1.3113 +} 1.3114 + 1.3115 +/** 1.3116 + * Here, we override the getter and ignore the value stored in our object. 1.3117 + * The bookmarks service can tell us whether this folder should be read-only 1.3118 + * or not. 1.3119 + * 1.3120 + * It would be nice to put this code in the folder constructor, but the 1.3121 + * database was complaining. I believe it is because most folders are created 1.3122 + * while enumerating the bookmarks table and having a statement open, and doing 1.3123 + * another statement might make it unhappy in some cases. 1.3124 + */ 1.3125 +NS_IMETHODIMP 1.3126 +nsNavHistoryFolderResultNode::GetChildrenReadOnly(bool *aChildrenReadOnly) 1.3127 +{ 1.3128 + nsNavBookmarks* bookmarks = nsNavBookmarks::GetBookmarksService(); 1.3129 + NS_ENSURE_TRUE(bookmarks, NS_ERROR_UNEXPECTED); 1.3130 + return bookmarks->GetFolderReadonly(mItemId, aChildrenReadOnly); 1.3131 +} 1.3132 + 1.3133 + 1.3134 +NS_IMETHODIMP 1.3135 +nsNavHistoryFolderResultNode::GetFolderItemId(int64_t* aItemId) 1.3136 +{ 1.3137 + *aItemId = mItemId; 1.3138 + return NS_OK; 1.3139 +} 1.3140 + 1.3141 +/** 1.3142 + * Lazily computes the URI for this specific folder query with the current 1.3143 + * options. 1.3144 + */ 1.3145 +NS_IMETHODIMP 1.3146 +nsNavHistoryFolderResultNode::GetUri(nsACString& aURI) 1.3147 +{ 1.3148 + if (!mURI.IsEmpty()) { 1.3149 + aURI = mURI; 1.3150 + return NS_OK; 1.3151 + } 1.3152 + 1.3153 + uint32_t queryCount; 1.3154 + nsINavHistoryQuery** queries; 1.3155 + nsresult rv = GetQueries(&queryCount, &queries); 1.3156 + NS_ENSURE_SUCCESS(rv, rv); 1.3157 + 1.3158 + nsNavHistory* history = nsNavHistory::GetHistoryService(); 1.3159 + NS_ENSURE_TRUE(history, NS_ERROR_OUT_OF_MEMORY); 1.3160 + 1.3161 + rv = history->QueriesToQueryString(queries, queryCount, mOptions, aURI); 1.3162 + for (uint32_t queryIndex = 0; queryIndex < queryCount; ++queryIndex) { 1.3163 + NS_RELEASE(queries[queryIndex]); 1.3164 + } 1.3165 + nsMemory::Free(queries); 1.3166 + return rv; 1.3167 +} 1.3168 + 1.3169 + 1.3170 +/** 1.3171 + * @return the queries that give you this bookmarks folder 1.3172 + */ 1.3173 +NS_IMETHODIMP 1.3174 +nsNavHistoryFolderResultNode::GetQueries(uint32_t* queryCount, 1.3175 + nsINavHistoryQuery*** queries) 1.3176 +{ 1.3177 + // get the query object 1.3178 + nsCOMPtr<nsINavHistoryQuery> query; 1.3179 + nsNavHistory* history = nsNavHistory::GetHistoryService(); 1.3180 + NS_ENSURE_TRUE(history, NS_ERROR_OUT_OF_MEMORY); 1.3181 + nsresult rv = history->GetNewQuery(getter_AddRefs(query)); 1.3182 + NS_ENSURE_SUCCESS(rv, rv); 1.3183 + 1.3184 + // query just has the folder ID set and nothing else 1.3185 + rv = query->SetFolders(&mItemId, 1); 1.3186 + NS_ENSURE_SUCCESS(rv, rv); 1.3187 + 1.3188 + // make array of our 1 query 1.3189 + *queries = static_cast<nsINavHistoryQuery**> 1.3190 + (nsMemory::Alloc(sizeof(nsINavHistoryQuery*))); 1.3191 + if (!*queries) 1.3192 + return NS_ERROR_OUT_OF_MEMORY; 1.3193 + NS_ADDREF((*queries)[0] = query); 1.3194 + *queryCount = 1; 1.3195 + return NS_OK; 1.3196 +} 1.3197 + 1.3198 + 1.3199 +/** 1.3200 + * Options for the query that gives you this bookmarks folder. This is just 1.3201 + * the options for the folder with the current folder ID set. 1.3202 + */ 1.3203 +NS_IMETHODIMP 1.3204 +nsNavHistoryFolderResultNode::GetQueryOptions( 1.3205 + nsINavHistoryQueryOptions** aQueryOptions) 1.3206 +{ 1.3207 + NS_ASSERTION(mOptions, "Options invalid"); 1.3208 + 1.3209 + *aQueryOptions = mOptions; 1.3210 + NS_ADDREF(*aQueryOptions); 1.3211 + return NS_OK; 1.3212 +} 1.3213 + 1.3214 + 1.3215 +nsresult 1.3216 +nsNavHistoryFolderResultNode::FillChildren() 1.3217 +{ 1.3218 + NS_ASSERTION(!mContentsValid, 1.3219 + "Don't call FillChildren when contents are valid"); 1.3220 + NS_ASSERTION(mChildren.Count() == 0, 1.3221 + "We are trying to fill children when there already are some"); 1.3222 + 1.3223 + nsNavBookmarks* bookmarks = nsNavBookmarks::GetBookmarksService(); 1.3224 + NS_ENSURE_TRUE(bookmarks, NS_ERROR_OUT_OF_MEMORY); 1.3225 + 1.3226 + // Actually get the folder children from the bookmark service. 1.3227 + nsresult rv = bookmarks->QueryFolderChildren(mItemId, mOptions, &mChildren); 1.3228 + NS_ENSURE_SUCCESS(rv, rv); 1.3229 + 1.3230 + // PERFORMANCE: it may be better to also fill any child folders at this point 1.3231 + // so that we can draw tree twisties without doing a separate query later. 1.3232 + // If we don't end up drawing twisties a lot, it doesn't matter. If we do 1.3233 + // this, we should wrap everything in a transaction here on the bookmark 1.3234 + // service's connection. 1.3235 + 1.3236 + return OnChildrenFilled(); 1.3237 +} 1.3238 + 1.3239 + 1.3240 +/** 1.3241 + * Performs some tasks after all the children of the container have been added. 1.3242 + * The container's contents are not valid until this method has been called. 1.3243 + */ 1.3244 +nsresult 1.3245 +nsNavHistoryFolderResultNode::OnChildrenFilled() 1.3246 +{ 1.3247 + // It is important to call FillStats to fill in the parents on all 1.3248 + // nodes and the result node pointers on the containers. 1.3249 + FillStats(); 1.3250 + 1.3251 + if (mResult && mResult->mNeedsToApplySortingMode) { 1.3252 + // We should repopulate container and then apply sortingMode. To avoid 1.3253 + // sorting 2 times we simply do that here. 1.3254 + mResult->SetSortingMode(mResult->mSortingMode); 1.3255 + } 1.3256 + else { 1.3257 + // Once we've computed all tree stats, we can sort, because containers will 1.3258 + // then have proper visit counts and dates. 1.3259 + SortComparator comparator = GetSortingComparator(GetSortType()); 1.3260 + if (comparator) { 1.3261 + nsAutoCString sortingAnnotation; 1.3262 + GetSortingAnnotation(sortingAnnotation); 1.3263 + RecursiveSort(sortingAnnotation.get(), comparator); 1.3264 + } 1.3265 + } 1.3266 + 1.3267 + // If we are limiting our results remove items from the end of the 1.3268 + // mChildren array after sorting. This is done for root node only. 1.3269 + // Note, if count < max results, we won't do anything. 1.3270 + if (!mParent && mOptions->MaxResults()) { 1.3271 + while ((uint32_t)mChildren.Count() > mOptions->MaxResults()) 1.3272 + mChildren.RemoveObjectAt(mChildren.Count() - 1); 1.3273 + } 1.3274 + 1.3275 + // Register with the result for updates. 1.3276 + EnsureRegisteredAsFolderObserver(); 1.3277 + 1.3278 + mContentsValid = true; 1.3279 + return NS_OK; 1.3280 +} 1.3281 + 1.3282 + 1.3283 +/** 1.3284 + * Registers the node with its result as a folder observer if it is not already 1.3285 + * registered. 1.3286 + */ 1.3287 +void 1.3288 +nsNavHistoryFolderResultNode::EnsureRegisteredAsFolderObserver() 1.3289 +{ 1.3290 + if (!mIsRegisteredFolderObserver && mResult) { 1.3291 + mResult->AddBookmarkFolderObserver(this, mItemId); 1.3292 + mIsRegisteredFolderObserver = true; 1.3293 + } 1.3294 +} 1.3295 + 1.3296 + 1.3297 +/** 1.3298 + * The async version of FillChildren. This begins asynchronous execution by 1.3299 + * calling nsNavBookmarks::QueryFolderChildrenAsync. During execution, this 1.3300 + * node's async Storage callbacks, HandleResult and HandleCompletion, will be 1.3301 + * called. 1.3302 + */ 1.3303 +nsresult 1.3304 +nsNavHistoryFolderResultNode::FillChildrenAsync() 1.3305 +{ 1.3306 + NS_ASSERTION(!mContentsValid, "FillChildrenAsync when contents are valid"); 1.3307 + NS_ASSERTION(mChildren.Count() == 0, "FillChildrenAsync when children exist"); 1.3308 + 1.3309 + // ProcessFolderNodeChild, called in HandleResult, increments this for every 1.3310 + // result row it processes. Initialize it here as we begin async execution. 1.3311 + mAsyncBookmarkIndex = -1; 1.3312 + 1.3313 + nsNavBookmarks* bmSvc = nsNavBookmarks::GetBookmarksService(); 1.3314 + NS_ENSURE_TRUE(bmSvc, NS_ERROR_OUT_OF_MEMORY); 1.3315 + nsresult rv = 1.3316 + bmSvc->QueryFolderChildrenAsync(this, mItemId, 1.3317 + getter_AddRefs(mAsyncPendingStmt)); 1.3318 + NS_ENSURE_SUCCESS(rv, rv); 1.3319 + 1.3320 + // Register with the result for updates. All updates during async execution 1.3321 + // will cause it to be restarted. 1.3322 + EnsureRegisteredAsFolderObserver(); 1.3323 + 1.3324 + return NS_OK; 1.3325 +} 1.3326 + 1.3327 + 1.3328 +/** 1.3329 + * A mozIStorageStatementCallback method. Called during the async execution 1.3330 + * begun by FillChildrenAsync. 1.3331 + * 1.3332 + * @param aResultSet 1.3333 + * The result set containing the data from the database. 1.3334 + */ 1.3335 +NS_IMETHODIMP 1.3336 +nsNavHistoryFolderResultNode::HandleResult(mozIStorageResultSet* aResultSet) 1.3337 +{ 1.3338 + NS_ENSURE_ARG_POINTER(aResultSet); 1.3339 + 1.3340 + nsNavBookmarks* bmSvc = nsNavBookmarks::GetBookmarksService(); 1.3341 + if (!bmSvc) { 1.3342 + CancelAsyncOpen(false); 1.3343 + return NS_ERROR_OUT_OF_MEMORY; 1.3344 + } 1.3345 + 1.3346 + // Consume all the currently available rows of the result set. 1.3347 + nsCOMPtr<mozIStorageRow> row; 1.3348 + while (NS_SUCCEEDED(aResultSet->GetNextRow(getter_AddRefs(row))) && row) { 1.3349 + nsresult rv = bmSvc->ProcessFolderNodeRow(row, mOptions, &mChildren, 1.3350 + mAsyncBookmarkIndex); 1.3351 + if (NS_FAILED(rv)) { 1.3352 + CancelAsyncOpen(false); 1.3353 + return rv; 1.3354 + } 1.3355 + } 1.3356 + 1.3357 + return NS_OK; 1.3358 +} 1.3359 + 1.3360 + 1.3361 +/** 1.3362 + * A mozIStorageStatementCallback method. Called during the async execution 1.3363 + * begun by FillChildrenAsync. 1.3364 + * 1.3365 + * @param aReason 1.3366 + * Indicates the final state of execution. 1.3367 + */ 1.3368 +NS_IMETHODIMP 1.3369 +nsNavHistoryFolderResultNode::HandleCompletion(uint16_t aReason) 1.3370 +{ 1.3371 + if (aReason == mozIStorageStatementCallback::REASON_FINISHED && 1.3372 + mAsyncCanceledState == NOT_CANCELED) { 1.3373 + // Async execution successfully completed. The container is ready to open. 1.3374 + 1.3375 + nsresult rv = OnChildrenFilled(); 1.3376 + NS_ENSURE_SUCCESS(rv, rv); 1.3377 + 1.3378 + mExpanded = true; 1.3379 + mAsyncPendingStmt = nullptr; 1.3380 + 1.3381 + // Notify observers only after mExpanded and mAsyncPendingStmt are set. 1.3382 + rv = NotifyOnStateChange(STATE_LOADING); 1.3383 + NS_ENSURE_SUCCESS(rv, rv); 1.3384 + } 1.3385 + 1.3386 + else if (mAsyncCanceledState == CANCELED_RESTART_NEEDED) { 1.3387 + // Async execution was canceled and needs to be restarted. 1.3388 + mAsyncCanceledState = NOT_CANCELED; 1.3389 + ClearChildren(false); 1.3390 + FillChildrenAsync(); 1.3391 + } 1.3392 + 1.3393 + else { 1.3394 + // Async execution failed or was canceled without restart. Remove all 1.3395 + // children and close the container, notifying observers. 1.3396 + mAsyncCanceledState = NOT_CANCELED; 1.3397 + ClearChildren(true); 1.3398 + CloseContainer(); 1.3399 + } 1.3400 + 1.3401 + return NS_OK; 1.3402 +} 1.3403 + 1.3404 + 1.3405 +void 1.3406 +nsNavHistoryFolderResultNode::ClearChildren(bool unregister) 1.3407 +{ 1.3408 + for (int32_t i = 0; i < mChildren.Count(); ++i) 1.3409 + mChildren[i]->OnRemoving(); 1.3410 + mChildren.Clear(); 1.3411 + 1.3412 + bool needsUnregister = unregister && (mContentsValid || mAsyncPendingStmt); 1.3413 + if (needsUnregister && mResult && mIsRegisteredFolderObserver) { 1.3414 + mResult->RemoveBookmarkFolderObserver(this, mItemId); 1.3415 + mIsRegisteredFolderObserver = false; 1.3416 + } 1.3417 + mContentsValid = false; 1.3418 +} 1.3419 + 1.3420 + 1.3421 +/** 1.3422 + * This is called to update the result when something has changed that we 1.3423 + * can not incrementally update. 1.3424 + */ 1.3425 +nsresult 1.3426 +nsNavHistoryFolderResultNode::Refresh() 1.3427 +{ 1.3428 + nsNavHistoryResult* result = GetResult(); 1.3429 + NS_ENSURE_STATE(result); 1.3430 + if (result->mBatchInProgress) { 1.3431 + result->requestRefresh(this); 1.3432 + return NS_OK; 1.3433 + } 1.3434 + 1.3435 + ClearChildren(true); 1.3436 + 1.3437 + if (!mExpanded) { 1.3438 + // When we are not expanded, we don't update, just invalidate and unhook. 1.3439 + return NS_OK; 1.3440 + } 1.3441 + 1.3442 + // Ignore errors from FillChildren, since we will still want to refresh 1.3443 + // the tree (there just might not be anything in it on error). ClearChildren 1.3444 + // has unregistered us as an observer since FillChildren will try to 1.3445 + // re-register us. 1.3446 + (void)FillChildren(); 1.3447 + 1.3448 + NOTIFY_RESULT_OBSERVERS(result, InvalidateContainer(TO_CONTAINER(this))); 1.3449 + return NS_OK; 1.3450 +} 1.3451 + 1.3452 + 1.3453 +/** 1.3454 + * Implements the logic described above the constructor. This sees if we 1.3455 + * should do an incremental update and returns true if so. If not, it 1.3456 + * invalidates our children, unregisters us an observer, and returns false. 1.3457 + */ 1.3458 +bool 1.3459 +nsNavHistoryFolderResultNode::StartIncrementalUpdate() 1.3460 +{ 1.3461 + // if any items are excluded, we can not do incremental updates since the 1.3462 + // indices from the bookmark service will not be valid 1.3463 + 1.3464 + if (!mOptions->ExcludeItems() && 1.3465 + !mOptions->ExcludeQueries() && 1.3466 + !mOptions->ExcludeReadOnlyFolders()) { 1.3467 + // easy case: we are visible, always do incremental update 1.3468 + if (mExpanded || AreChildrenVisible()) 1.3469 + return true; 1.3470 + 1.3471 + nsNavHistoryResult* result = GetResult(); 1.3472 + NS_ENSURE_TRUE(result, false); 1.3473 + 1.3474 + // When any observers are attached also do incremental updates if our 1.3475 + // parent is visible, so that twisties are drawn correctly. 1.3476 + if (mParent) 1.3477 + return result->mObservers.Length() > 0; 1.3478 + } 1.3479 + 1.3480 + // otherwise, we don't do incremental updates, invalidate and unregister 1.3481 + (void)Refresh(); 1.3482 + return false; 1.3483 +} 1.3484 + 1.3485 + 1.3486 +/** 1.3487 + * This function adds aDelta to all bookmark indices between the two endpoints, 1.3488 + * inclusive. It is used when items are added or removed from the bookmark 1.3489 + * folder. 1.3490 + */ 1.3491 +void 1.3492 +nsNavHistoryFolderResultNode::ReindexRange(int32_t aStartIndex, 1.3493 + int32_t aEndIndex, 1.3494 + int32_t aDelta) 1.3495 +{ 1.3496 + for (int32_t i = 0; i < mChildren.Count(); ++i) { 1.3497 + nsNavHistoryResultNode* node = mChildren[i]; 1.3498 + if (node->mBookmarkIndex >= aStartIndex && 1.3499 + node->mBookmarkIndex <= aEndIndex) 1.3500 + node->mBookmarkIndex += aDelta; 1.3501 + } 1.3502 +} 1.3503 + 1.3504 + 1.3505 +/** 1.3506 + * Searches this folder for a node with the given id. 1.3507 + * 1.3508 + * @return the node if found, null otherwise. 1.3509 + * @note Does not addref the node! 1.3510 + */ 1.3511 +nsNavHistoryResultNode* 1.3512 +nsNavHistoryFolderResultNode::FindChildById(int64_t aItemId, 1.3513 + uint32_t* aNodeIndex) 1.3514 +{ 1.3515 + for (int32_t i = 0; i < mChildren.Count(); ++i) { 1.3516 + if (mChildren[i]->mItemId == aItemId || 1.3517 + (mChildren[i]->IsFolder() && 1.3518 + mChildren[i]->GetAsFolder()->mQueryItemId == aItemId)) { 1.3519 + *aNodeIndex = i; 1.3520 + return mChildren[i]; 1.3521 + } 1.3522 + } 1.3523 + return nullptr; 1.3524 +} 1.3525 + 1.3526 + 1.3527 +// Used by nsNavHistoryFolderResultNode's nsINavBookmarkObserver methods below. 1.3528 +// If the container is notified of a bookmark event while asynchronous execution 1.3529 +// is pending, this restarts it and returns. 1.3530 +#define RESTART_AND_RETURN_IF_ASYNC_PENDING() \ 1.3531 + if (mAsyncPendingStmt) { \ 1.3532 + CancelAsyncOpen(true); \ 1.3533 + return NS_OK; \ 1.3534 + } 1.3535 + 1.3536 + 1.3537 +NS_IMETHODIMP 1.3538 +nsNavHistoryFolderResultNode::OnBeginUpdateBatch() 1.3539 +{ 1.3540 + return NS_OK; 1.3541 +} 1.3542 + 1.3543 + 1.3544 +NS_IMETHODIMP 1.3545 +nsNavHistoryFolderResultNode::OnEndUpdateBatch() 1.3546 +{ 1.3547 + return NS_OK; 1.3548 +} 1.3549 + 1.3550 + 1.3551 +NS_IMETHODIMP 1.3552 +nsNavHistoryFolderResultNode::OnItemAdded(int64_t aItemId, 1.3553 + int64_t aParentFolder, 1.3554 + int32_t aIndex, 1.3555 + uint16_t aItemType, 1.3556 + nsIURI* aURI, 1.3557 + const nsACString& aTitle, 1.3558 + PRTime aDateAdded, 1.3559 + const nsACString& aGUID, 1.3560 + const nsACString& aParentGUID) 1.3561 +{ 1.3562 + NS_ASSERTION(aParentFolder == mItemId, "Got wrong bookmark update"); 1.3563 + 1.3564 + bool excludeItems = (mResult && mResult->mRootNode->mOptions->ExcludeItems()) || 1.3565 + (mParent && mParent->mOptions->ExcludeItems()) || 1.3566 + mOptions->ExcludeItems(); 1.3567 + 1.3568 + // here, try to do something reasonable if the bookmark service gives us 1.3569 + // a bogus index. 1.3570 + if (aIndex < 0) { 1.3571 + NS_NOTREACHED("Invalid index for item adding: <0"); 1.3572 + aIndex = 0; 1.3573 + } 1.3574 + else if (aIndex > mChildren.Count()) { 1.3575 + if (!excludeItems) { 1.3576 + // Something wrong happened while updating indexes. 1.3577 + NS_NOTREACHED("Invalid index for item adding: greater than count"); 1.3578 + } 1.3579 + aIndex = mChildren.Count(); 1.3580 + } 1.3581 + 1.3582 + RESTART_AND_RETURN_IF_ASYNC_PENDING(); 1.3583 + 1.3584 + nsresult rv; 1.3585 + 1.3586 + // Check for query URIs, which are bookmarks, but treated as containers 1.3587 + // in results and views. 1.3588 + bool isQuery = false; 1.3589 + if (aItemType == nsINavBookmarksService::TYPE_BOOKMARK) { 1.3590 + NS_ASSERTION(aURI, "Got a null URI when we are a bookmark?!"); 1.3591 + nsAutoCString itemURISpec; 1.3592 + rv = aURI->GetSpec(itemURISpec); 1.3593 + NS_ENSURE_SUCCESS(rv, rv); 1.3594 + isQuery = IsQueryURI(itemURISpec); 1.3595 + } 1.3596 + 1.3597 + if (aItemType != nsINavBookmarksService::TYPE_FOLDER && 1.3598 + !isQuery && excludeItems) { 1.3599 + // don't update items when we aren't displaying them, but we still need 1.3600 + // to adjust bookmark indices to account for the insertion 1.3601 + ReindexRange(aIndex, INT32_MAX, 1); 1.3602 + return NS_OK; 1.3603 + } 1.3604 + 1.3605 + if (!StartIncrementalUpdate()) 1.3606 + return NS_OK; // folder was completely refreshed for us 1.3607 + 1.3608 + // adjust indices to account for insertion 1.3609 + ReindexRange(aIndex, INT32_MAX, 1); 1.3610 + 1.3611 + nsRefPtr<nsNavHistoryResultNode> node; 1.3612 + if (aItemType == nsINavBookmarksService::TYPE_BOOKMARK) { 1.3613 + nsNavHistory* history = nsNavHistory::GetHistoryService(); 1.3614 + NS_ENSURE_TRUE(history, NS_ERROR_OUT_OF_MEMORY); 1.3615 + rv = history->BookmarkIdToResultNode(aItemId, mOptions, getter_AddRefs(node)); 1.3616 + NS_ENSURE_SUCCESS(rv, rv); 1.3617 + } 1.3618 + else if (aItemType == nsINavBookmarksService::TYPE_FOLDER) { 1.3619 + nsNavBookmarks* bookmarks = nsNavBookmarks::GetBookmarksService(); 1.3620 + NS_ENSURE_TRUE(bookmarks, NS_ERROR_OUT_OF_MEMORY); 1.3621 + rv = bookmarks->ResultNodeForContainer(aItemId, mOptions, getter_AddRefs(node)); 1.3622 + NS_ENSURE_SUCCESS(rv, rv); 1.3623 + } 1.3624 + else if (aItemType == nsINavBookmarksService::TYPE_SEPARATOR) { 1.3625 + node = new nsNavHistorySeparatorResultNode(); 1.3626 + NS_ENSURE_TRUE(node, NS_ERROR_OUT_OF_MEMORY); 1.3627 + node->mItemId = aItemId; 1.3628 + } 1.3629 + 1.3630 + node->mBookmarkIndex = aIndex; 1.3631 + 1.3632 + if (aItemType == nsINavBookmarksService::TYPE_SEPARATOR || 1.3633 + GetSortType() == nsINavHistoryQueryOptions::SORT_BY_NONE) { 1.3634 + // insert at natural bookmarks position 1.3635 + return InsertChildAt(node, aIndex); 1.3636 + } 1.3637 + 1.3638 + // insert at sorted position 1.3639 + return InsertSortedChild(node, false); 1.3640 +} 1.3641 + 1.3642 + 1.3643 +NS_IMETHODIMP 1.3644 +nsNavHistoryFolderResultNode::OnItemRemoved(int64_t aItemId, 1.3645 + int64_t aParentFolder, 1.3646 + int32_t aIndex, 1.3647 + uint16_t aItemType, 1.3648 + nsIURI* aURI, 1.3649 + const nsACString& aGUID, 1.3650 + const nsACString& aParentGUID) 1.3651 +{ 1.3652 + // We only care about notifications when a child changes. When the deleted 1.3653 + // item is us, our parent should also be registered and will remove us from 1.3654 + // its list. 1.3655 + if (mItemId == aItemId) 1.3656 + return NS_OK; 1.3657 + 1.3658 + NS_ASSERTION(aParentFolder == mItemId, "Got wrong bookmark update"); 1.3659 + 1.3660 + RESTART_AND_RETURN_IF_ASYNC_PENDING(); 1.3661 + 1.3662 + bool excludeItems = (mResult && mResult->mRootNode->mOptions->ExcludeItems()) || 1.3663 + (mParent && mParent->mOptions->ExcludeItems()) || 1.3664 + mOptions->ExcludeItems(); 1.3665 + 1.3666 + // don't trust the index from the bookmark service, find it ourselves. The 1.3667 + // sorting could be different, or the bookmark services indices and ours might 1.3668 + // be out of sync somehow. 1.3669 + uint32_t index; 1.3670 + nsNavHistoryResultNode* node = FindChildById(aItemId, &index); 1.3671 + if (!node) { 1.3672 + if (excludeItems) 1.3673 + return NS_OK; 1.3674 + 1.3675 + NS_NOTREACHED("Removing item we don't have"); 1.3676 + return NS_ERROR_FAILURE; 1.3677 + } 1.3678 + 1.3679 + if ((node->IsURI() || node->IsSeparator()) && excludeItems) { 1.3680 + // don't update items when we aren't displaying them, but we do need to 1.3681 + // adjust everybody's bookmark indices to account for the removal 1.3682 + ReindexRange(aIndex, INT32_MAX, -1); 1.3683 + return NS_OK; 1.3684 + } 1.3685 + 1.3686 + if (!StartIncrementalUpdate()) 1.3687 + return NS_OK; // we are completely refreshed 1.3688 + 1.3689 + // shift all following indices down 1.3690 + ReindexRange(aIndex + 1, INT32_MAX, -1); 1.3691 + 1.3692 + return RemoveChildAt(index); 1.3693 +} 1.3694 + 1.3695 + 1.3696 +NS_IMETHODIMP 1.3697 +nsNavHistoryResultNode::OnItemChanged(int64_t aItemId, 1.3698 + const nsACString& aProperty, 1.3699 + bool aIsAnnotationProperty, 1.3700 + const nsACString& aNewValue, 1.3701 + PRTime aLastModified, 1.3702 + uint16_t aItemType, 1.3703 + int64_t aParentId, 1.3704 + const nsACString& aGUID, 1.3705 + const nsACString& aParentGUID) 1.3706 +{ 1.3707 + if (aItemId != mItemId) 1.3708 + return NS_OK; 1.3709 + 1.3710 + mLastModified = aLastModified; 1.3711 + 1.3712 + nsNavHistoryResult* result = GetResult(); 1.3713 + NS_ENSURE_STATE(result); 1.3714 + 1.3715 + bool shouldNotify = !mParent || mParent->AreChildrenVisible(); 1.3716 + 1.3717 + if (aIsAnnotationProperty) { 1.3718 + if (shouldNotify) 1.3719 + NOTIFY_RESULT_OBSERVERS(result, NodeAnnotationChanged(this, aProperty)); 1.3720 + } 1.3721 + else if (aProperty.EqualsLiteral("title")) { 1.3722 + // XXX: what should we do if the new title is void? 1.3723 + mTitle = aNewValue; 1.3724 + if (shouldNotify) 1.3725 + NOTIFY_RESULT_OBSERVERS(result, NodeTitleChanged(this, mTitle)); 1.3726 + } 1.3727 + else if (aProperty.EqualsLiteral("uri")) { 1.3728 + // clear the tags string as well 1.3729 + mTags.SetIsVoid(true); 1.3730 + mURI = aNewValue; 1.3731 + if (shouldNotify) 1.3732 + NOTIFY_RESULT_OBSERVERS(result, NodeURIChanged(this, mURI)); 1.3733 + } 1.3734 + else if (aProperty.EqualsLiteral("favicon")) { 1.3735 + mFaviconURI = aNewValue; 1.3736 + if (shouldNotify) 1.3737 + NOTIFY_RESULT_OBSERVERS(result, NodeIconChanged(this)); 1.3738 + } 1.3739 + else if (aProperty.EqualsLiteral("cleartime")) { 1.3740 + mTime = 0; 1.3741 + if (shouldNotify) { 1.3742 + NOTIFY_RESULT_OBSERVERS(result, 1.3743 + NodeHistoryDetailsChanged(this, 0, mAccessCount)); 1.3744 + } 1.3745 + } 1.3746 + else if (aProperty.EqualsLiteral("tags")) { 1.3747 + mTags.SetIsVoid(true); 1.3748 + if (shouldNotify) 1.3749 + NOTIFY_RESULT_OBSERVERS(result, NodeTagsChanged(this)); 1.3750 + } 1.3751 + else if (aProperty.EqualsLiteral("dateAdded")) { 1.3752 + // aNewValue has the date as a string, but we can use aLastModified, 1.3753 + // because it's set to the same value when dateAdded is changed. 1.3754 + mDateAdded = aLastModified; 1.3755 + if (shouldNotify) 1.3756 + NOTIFY_RESULT_OBSERVERS(result, NodeDateAddedChanged(this, mDateAdded)); 1.3757 + } 1.3758 + else if (aProperty.EqualsLiteral("lastModified")) { 1.3759 + if (shouldNotify) { 1.3760 + NOTIFY_RESULT_OBSERVERS(result, 1.3761 + NodeLastModifiedChanged(this, aLastModified)); 1.3762 + } 1.3763 + } 1.3764 + else if (aProperty.EqualsLiteral("keyword")) { 1.3765 + if (shouldNotify) 1.3766 + NOTIFY_RESULT_OBSERVERS(result, NodeKeywordChanged(this, aNewValue)); 1.3767 + } 1.3768 + else 1.3769 + NS_NOTREACHED("Unknown bookmark property changing."); 1.3770 + 1.3771 + if (!mParent) 1.3772 + return NS_OK; 1.3773 + 1.3774 + // DO NOT OPTIMIZE THIS TO CHECK aProperty 1.3775 + // The sorting methods fall back to each other so we need to re-sort the 1.3776 + // result even if it's not set to sort by the given property. 1.3777 + int32_t ourIndex = mParent->FindChild(this); 1.3778 + NS_ASSERTION(ourIndex >= 0, "Could not find self in parent"); 1.3779 + if (ourIndex >= 0) 1.3780 + mParent->EnsureItemPosition(ourIndex); 1.3781 + 1.3782 + return NS_OK; 1.3783 +} 1.3784 + 1.3785 + 1.3786 +NS_IMETHODIMP 1.3787 +nsNavHistoryFolderResultNode::OnItemChanged(int64_t aItemId, 1.3788 + const nsACString& aProperty, 1.3789 + bool aIsAnnotationProperty, 1.3790 + const nsACString& aNewValue, 1.3791 + PRTime aLastModified, 1.3792 + uint16_t aItemType, 1.3793 + int64_t aParentId, 1.3794 + const nsACString& aGUID, 1.3795 + const nsACString&aParentGUID) 1.3796 +{ 1.3797 + // The query-item's title is used for simple-query nodes 1.3798 + if (mQueryItemId != -1) { 1.3799 + bool isTitleChange = aProperty.EqualsLiteral("title"); 1.3800 + if ((mQueryItemId == aItemId && !isTitleChange) || 1.3801 + (mQueryItemId != aItemId && isTitleChange)) { 1.3802 + return NS_OK; 1.3803 + } 1.3804 + } 1.3805 + 1.3806 + RESTART_AND_RETURN_IF_ASYNC_PENDING(); 1.3807 + 1.3808 + return nsNavHistoryResultNode::OnItemChanged(aItemId, aProperty, 1.3809 + aIsAnnotationProperty, 1.3810 + aNewValue, aLastModified, 1.3811 + aItemType, aParentId, aGUID, 1.3812 + aParentGUID); 1.3813 +} 1.3814 + 1.3815 +/** 1.3816 + * Updates visit count and last visit time and refreshes. 1.3817 + */ 1.3818 +NS_IMETHODIMP 1.3819 +nsNavHistoryFolderResultNode::OnItemVisited(int64_t aItemId, 1.3820 + int64_t aVisitId, 1.3821 + PRTime aTime, 1.3822 + uint32_t aTransitionType, 1.3823 + nsIURI* aURI, 1.3824 + int64_t aParentId, 1.3825 + const nsACString& aGUID, 1.3826 + const nsACString& aParentGUID) 1.3827 +{ 1.3828 + bool excludeItems = (mResult && mResult->mRootNode->mOptions->ExcludeItems()) || 1.3829 + (mParent && mParent->mOptions->ExcludeItems()) || 1.3830 + mOptions->ExcludeItems(); 1.3831 + if (excludeItems) 1.3832 + return NS_OK; // don't update items when we aren't displaying them 1.3833 + 1.3834 + RESTART_AND_RETURN_IF_ASYNC_PENDING(); 1.3835 + 1.3836 + if (!StartIncrementalUpdate()) 1.3837 + return NS_OK; 1.3838 + 1.3839 + uint32_t nodeIndex; 1.3840 + nsNavHistoryResultNode* node = FindChildById(aItemId, &nodeIndex); 1.3841 + if (!node) 1.3842 + return NS_ERROR_FAILURE; 1.3843 + 1.3844 + // Update node. 1.3845 + node->mTime = aTime; 1.3846 + ++node->mAccessCount; 1.3847 + 1.3848 + // Update us. 1.3849 + int32_t oldAccessCount = mAccessCount; 1.3850 + ++mAccessCount; 1.3851 + if (aTime > mTime) 1.3852 + mTime = aTime; 1.3853 + nsresult rv = ReverseUpdateStats(mAccessCount - oldAccessCount); 1.3854 + NS_ENSURE_SUCCESS(rv, rv); 1.3855 + 1.3856 + // Update frecency for proper frecency ordering. 1.3857 + // TODO (bug 832617): we may avoid one query here, by providing the new 1.3858 + // frecency value in the notification. 1.3859 + nsNavHistory* history = nsNavHistory::GetHistoryService(); 1.3860 + NS_ENSURE_TRUE(history, NS_OK); 1.3861 + nsRefPtr<nsNavHistoryResultNode> visitNode; 1.3862 + rv = history->VisitIdToResultNode(aVisitId, mOptions, 1.3863 + getter_AddRefs(visitNode)); 1.3864 + NS_ENSURE_SUCCESS(rv, rv); 1.3865 + NS_ENSURE_STATE(visitNode); 1.3866 + node->mFrecency = visitNode->mFrecency; 1.3867 + 1.3868 + if (AreChildrenVisible()) { 1.3869 + // Sorting has not changed, just redraw the row if it's visible. 1.3870 + nsNavHistoryResult* result = GetResult(); 1.3871 + NOTIFY_RESULT_OBSERVERS(result, 1.3872 + NodeHistoryDetailsChanged(node, mTime, mAccessCount)); 1.3873 + } 1.3874 + 1.3875 + // Update sorting if necessary. 1.3876 + uint32_t sortType = GetSortType(); 1.3877 + if (sortType == nsINavHistoryQueryOptions::SORT_BY_VISITCOUNT_ASCENDING || 1.3878 + sortType == nsINavHistoryQueryOptions::SORT_BY_VISITCOUNT_DESCENDING || 1.3879 + sortType == nsINavHistoryQueryOptions::SORT_BY_DATE_ASCENDING || 1.3880 + sortType == nsINavHistoryQueryOptions::SORT_BY_DATE_DESCENDING || 1.3881 + sortType == nsINavHistoryQueryOptions::SORT_BY_FRECENCY_ASCENDING || 1.3882 + sortType == nsINavHistoryQueryOptions::SORT_BY_FRECENCY_DESCENDING) { 1.3883 + int32_t childIndex = FindChild(node); 1.3884 + NS_ASSERTION(childIndex >= 0, "Could not find child we just got a reference to"); 1.3885 + if (childIndex >= 0) { 1.3886 + EnsureItemPosition(childIndex); 1.3887 + } 1.3888 + } 1.3889 + 1.3890 + return NS_OK; 1.3891 +} 1.3892 + 1.3893 + 1.3894 +NS_IMETHODIMP 1.3895 +nsNavHistoryFolderResultNode::OnItemMoved(int64_t aItemId, 1.3896 + int64_t aOldParent, 1.3897 + int32_t aOldIndex, 1.3898 + int64_t aNewParent, 1.3899 + int32_t aNewIndex, 1.3900 + uint16_t aItemType, 1.3901 + const nsACString& aGUID, 1.3902 + const nsACString& aOldParentGUID, 1.3903 + const nsACString& aNewParentGUID) 1.3904 +{ 1.3905 + NS_ASSERTION(aOldParent == mItemId || aNewParent == mItemId, 1.3906 + "Got a bookmark message that doesn't belong to us"); 1.3907 + 1.3908 + RESTART_AND_RETURN_IF_ASYNC_PENDING(); 1.3909 + 1.3910 + if (!StartIncrementalUpdate()) 1.3911 + return NS_OK; // entire container was refreshed for us 1.3912 + 1.3913 + if (aOldParent == aNewParent) { 1.3914 + // getting moved within the same folder, we don't want to do a remove and 1.3915 + // an add because that will lose your tree state. 1.3916 + 1.3917 + // adjust bookmark indices 1.3918 + ReindexRange(aOldIndex + 1, INT32_MAX, -1); 1.3919 + ReindexRange(aNewIndex, INT32_MAX, 1); 1.3920 + 1.3921 + uint32_t index; 1.3922 + nsNavHistoryResultNode* node = FindChildById(aItemId, &index); 1.3923 + if (!node) { 1.3924 + NS_NOTREACHED("Can't find folder that is moving!"); 1.3925 + return NS_ERROR_FAILURE; 1.3926 + } 1.3927 + NS_ASSERTION(index < uint32_t(mChildren.Count()), "Invalid index!"); 1.3928 + node->mBookmarkIndex = aNewIndex; 1.3929 + 1.3930 + // adjust position 1.3931 + EnsureItemPosition(index); 1.3932 + return NS_OK; 1.3933 + } else { 1.3934 + // moving between two different folders, just do a remove and an add 1.3935 + nsCOMPtr<nsIURI> itemURI; 1.3936 + nsAutoCString itemTitle; 1.3937 + if (aItemType == nsINavBookmarksService::TYPE_BOOKMARK) { 1.3938 + nsNavBookmarks* bookmarks = nsNavBookmarks::GetBookmarksService(); 1.3939 + NS_ENSURE_TRUE(bookmarks, NS_ERROR_OUT_OF_MEMORY); 1.3940 + nsresult rv = bookmarks->GetBookmarkURI(aItemId, getter_AddRefs(itemURI)); 1.3941 + NS_ENSURE_SUCCESS(rv, rv); 1.3942 + rv = bookmarks->GetItemTitle(aItemId, itemTitle); 1.3943 + NS_ENSURE_SUCCESS(rv, rv); 1.3944 + } 1.3945 + if (aOldParent == mItemId) { 1.3946 + OnItemRemoved(aItemId, aOldParent, aOldIndex, aItemType, itemURI, 1.3947 + aGUID, aOldParentGUID); 1.3948 + } 1.3949 + if (aNewParent == mItemId) { 1.3950 + OnItemAdded(aItemId, aNewParent, aNewIndex, aItemType, itemURI, itemTitle, 1.3951 + PR_Now(), // This is a dummy dateAdded, not the real value. 1.3952 + aGUID, aNewParentGUID); 1.3953 + } 1.3954 + } 1.3955 + return NS_OK; 1.3956 +} 1.3957 + 1.3958 + 1.3959 +/** 1.3960 + * Separator nodes do not hold any data. 1.3961 + */ 1.3962 +nsNavHistorySeparatorResultNode::nsNavHistorySeparatorResultNode() 1.3963 + : nsNavHistoryResultNode(EmptyCString(), EmptyCString(), 1.3964 + 0, 0, EmptyCString()) 1.3965 +{ 1.3966 +} 1.3967 + 1.3968 + 1.3969 +static PLDHashOperator 1.3970 +RemoveBookmarkFolderObserversCallback(nsTrimInt64HashKey::KeyType aKey, 1.3971 + nsNavHistoryResult::FolderObserverList*& aData, 1.3972 + void* userArg) 1.3973 +{ 1.3974 + delete aData; 1.3975 + return PL_DHASH_REMOVE; 1.3976 +} 1.3977 + 1.3978 +NS_IMPL_CYCLE_COLLECTION_CLASS(nsNavHistoryResult) 1.3979 + 1.3980 +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsNavHistoryResult) 1.3981 + tmp->StopObserving(); 1.3982 + NS_IMPL_CYCLE_COLLECTION_UNLINK(mRootNode) 1.3983 + NS_IMPL_CYCLE_COLLECTION_UNLINK(mObservers) 1.3984 + tmp->mBookmarkFolderObservers.Enumerate(&RemoveBookmarkFolderObserversCallback, nullptr); 1.3985 + NS_IMPL_CYCLE_COLLECTION_UNLINK(mAllBookmarksObservers) 1.3986 + NS_IMPL_CYCLE_COLLECTION_UNLINK(mHistoryObservers) 1.3987 +NS_IMPL_CYCLE_COLLECTION_UNLINK_END 1.3988 + 1.3989 +static PLDHashOperator 1.3990 +TraverseBookmarkFolderObservers(nsTrimInt64HashKey::KeyType aKey, 1.3991 + nsNavHistoryResult::FolderObserverList* &aData, 1.3992 + void *aClosure) 1.3993 +{ 1.3994 + nsCycleCollectionTraversalCallback* cb = 1.3995 + static_cast<nsCycleCollectionTraversalCallback*>(aClosure); 1.3996 + for (uint32_t i = 0; i < aData->Length(); ++i) { 1.3997 + NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb, 1.3998 + "mBookmarkFolderObservers value[i]"); 1.3999 + nsNavHistoryResultNode* node = aData->ElementAt(i); 1.4000 + cb->NoteXPCOMChild(node); 1.4001 + } 1.4002 + return PL_DHASH_NEXT; 1.4003 +} 1.4004 + 1.4005 +static void 1.4006 +traverseResultObservers(nsMaybeWeakPtrArray<nsINavHistoryResultObserver> aObservers, 1.4007 + void *aClosure) 1.4008 +{ 1.4009 + nsCycleCollectionTraversalCallback* cb = 1.4010 + static_cast<nsCycleCollectionTraversalCallback*>(aClosure); 1.4011 + for (uint32_t i = 0; i < aObservers.Length(); ++i) { 1.4012 + NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb, "mResultObservers value[i]"); 1.4013 + const nsCOMPtr<nsINavHistoryResultObserver> &obs = aObservers.ElementAt(i); 1.4014 + cb->NoteXPCOMChild(obs); 1.4015 + } 1.4016 +} 1.4017 + 1.4018 +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsNavHistoryResult) 1.4019 + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRootNode) 1.4020 + traverseResultObservers(tmp->mObservers, &cb); 1.4021 + tmp->mBookmarkFolderObservers.Enumerate(&TraverseBookmarkFolderObservers, &cb); 1.4022 + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAllBookmarksObservers) 1.4023 + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mHistoryObservers) 1.4024 +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END 1.4025 + 1.4026 +NS_IMPL_CYCLE_COLLECTING_ADDREF(nsNavHistoryResult) 1.4027 +NS_IMPL_CYCLE_COLLECTING_RELEASE(nsNavHistoryResult) 1.4028 + 1.4029 +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsNavHistoryResult) 1.4030 + NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsINavHistoryResult) 1.4031 + NS_INTERFACE_MAP_STATIC_AMBIGUOUS(nsNavHistoryResult) 1.4032 + NS_INTERFACE_MAP_ENTRY(nsINavHistoryResult) 1.4033 + NS_INTERFACE_MAP_ENTRY(nsINavBookmarkObserver) 1.4034 + NS_INTERFACE_MAP_ENTRY(nsINavHistoryObserver) 1.4035 + NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference) 1.4036 +NS_INTERFACE_MAP_END 1.4037 + 1.4038 +nsNavHistoryResult::nsNavHistoryResult(nsNavHistoryContainerResultNode* aRoot) 1.4039 + : mRootNode(aRoot) 1.4040 + , mNeedsToApplySortingMode(false) 1.4041 + , mIsHistoryObserver(false) 1.4042 + , mIsBookmarkFolderObserver(false) 1.4043 + , mIsAllBookmarksObserver(false) 1.4044 + , mBookmarkFolderObservers(128) 1.4045 + , mBatchInProgress(false) 1.4046 + , mSuppressNotifications(false) 1.4047 +{ 1.4048 + mRootNode->mResult = this; 1.4049 +} 1.4050 + 1.4051 +nsNavHistoryResult::~nsNavHistoryResult() 1.4052 +{ 1.4053 + // delete all bookmark folder observer arrays which are allocated on the heap 1.4054 + mBookmarkFolderObservers.Enumerate(&RemoveBookmarkFolderObserversCallback, nullptr); 1.4055 +} 1.4056 + 1.4057 +void 1.4058 +nsNavHistoryResult::StopObserving() 1.4059 +{ 1.4060 + if (mIsBookmarkFolderObserver || mIsAllBookmarksObserver) { 1.4061 + nsNavBookmarks* bookmarks = nsNavBookmarks::GetBookmarksService(); 1.4062 + if (bookmarks) { 1.4063 + bookmarks->RemoveObserver(this); 1.4064 + mIsBookmarkFolderObserver = false; 1.4065 + mIsAllBookmarksObserver = false; 1.4066 + } 1.4067 + } 1.4068 + if (mIsHistoryObserver) { 1.4069 + nsNavHistory* history = nsNavHistory::GetHistoryService(); 1.4070 + if (history) { 1.4071 + history->RemoveObserver(this); 1.4072 + mIsHistoryObserver = false; 1.4073 + } 1.4074 + } 1.4075 +} 1.4076 + 1.4077 +/** 1.4078 + * @note you must call AddRef before this, since we may do things like 1.4079 + * register ourselves. 1.4080 + */ 1.4081 +nsresult 1.4082 +nsNavHistoryResult::Init(nsINavHistoryQuery** aQueries, 1.4083 + uint32_t aQueryCount, 1.4084 + nsNavHistoryQueryOptions *aOptions) 1.4085 +{ 1.4086 + nsresult rv; 1.4087 + NS_ASSERTION(aOptions, "Must have valid options"); 1.4088 + NS_ASSERTION(aQueries && aQueryCount > 0, "Must have >1 query in result"); 1.4089 + 1.4090 + // Fill saved source queries with copies of the original (the caller might 1.4091 + // change their original objects, and we always want to reflect the source 1.4092 + // parameters). 1.4093 + for (uint32_t i = 0; i < aQueryCount; ++i) { 1.4094 + nsCOMPtr<nsINavHistoryQuery> queryClone; 1.4095 + rv = aQueries[i]->Clone(getter_AddRefs(queryClone)); 1.4096 + NS_ENSURE_SUCCESS(rv, rv); 1.4097 + if (!mQueries.AppendObject(queryClone)) 1.4098 + return NS_ERROR_OUT_OF_MEMORY; 1.4099 + } 1.4100 + rv = aOptions->Clone(getter_AddRefs(mOptions)); 1.4101 + NS_ENSURE_SUCCESS(rv, rv); 1.4102 + mSortingMode = aOptions->SortingMode(); 1.4103 + rv = aOptions->GetSortingAnnotation(mSortingAnnotation); 1.4104 + NS_ENSURE_SUCCESS(rv, rv); 1.4105 + 1.4106 + NS_ASSERTION(mRootNode->mIndentLevel == -1, 1.4107 + "Root node's indent level initialized wrong"); 1.4108 + mRootNode->FillStats(); 1.4109 + 1.4110 + return NS_OK; 1.4111 +} 1.4112 + 1.4113 + 1.4114 +/** 1.4115 + * Constructs a new history result object. 1.4116 + */ 1.4117 +nsresult // static 1.4118 +nsNavHistoryResult::NewHistoryResult(nsINavHistoryQuery** aQueries, 1.4119 + uint32_t aQueryCount, 1.4120 + nsNavHistoryQueryOptions* aOptions, 1.4121 + nsNavHistoryContainerResultNode* aRoot, 1.4122 + bool aBatchInProgress, 1.4123 + nsNavHistoryResult** result) 1.4124 +{ 1.4125 + *result = new nsNavHistoryResult(aRoot); 1.4126 + if (!*result) 1.4127 + return NS_ERROR_OUT_OF_MEMORY; 1.4128 + NS_ADDREF(*result); // must happen before Init 1.4129 + // Correctly set mBatchInProgress for the result based on the root node value. 1.4130 + (*result)->mBatchInProgress = aBatchInProgress; 1.4131 + nsresult rv = (*result)->Init(aQueries, aQueryCount, aOptions); 1.4132 + if (NS_FAILED(rv)) { 1.4133 + NS_RELEASE(*result); 1.4134 + *result = nullptr; 1.4135 + return rv; 1.4136 + } 1.4137 + 1.4138 + return NS_OK; 1.4139 +} 1.4140 + 1.4141 + 1.4142 +void 1.4143 +nsNavHistoryResult::AddHistoryObserver(nsNavHistoryQueryResultNode* aNode) 1.4144 +{ 1.4145 + if (!mIsHistoryObserver) { 1.4146 + nsNavHistory* history = nsNavHistory::GetHistoryService(); 1.4147 + NS_ASSERTION(history, "Can't create history service"); 1.4148 + history->AddObserver(this, true); 1.4149 + mIsHistoryObserver = true; 1.4150 + } 1.4151 + // Don't add duplicate observers. In some case we don't unregister when 1.4152 + // children are cleared (see ClearChildren) and the next FillChildren call 1.4153 + // will try to add the observer again. 1.4154 + if (mHistoryObservers.IndexOf(aNode) == mHistoryObservers.NoIndex) { 1.4155 + mHistoryObservers.AppendElement(aNode); 1.4156 + } 1.4157 +} 1.4158 + 1.4159 + 1.4160 +void 1.4161 +nsNavHistoryResult::AddAllBookmarksObserver(nsNavHistoryQueryResultNode* aNode) 1.4162 +{ 1.4163 + if (!mIsAllBookmarksObserver && !mIsBookmarkFolderObserver) { 1.4164 + nsNavBookmarks* bookmarks = nsNavBookmarks::GetBookmarksService(); 1.4165 + if (!bookmarks) { 1.4166 + NS_NOTREACHED("Can't create bookmark service"); 1.4167 + return; 1.4168 + } 1.4169 + bookmarks->AddObserver(this, true); 1.4170 + mIsAllBookmarksObserver = true; 1.4171 + } 1.4172 + // Don't add duplicate observers. In some case we don't unregister when 1.4173 + // children are cleared (see ClearChildren) and the next FillChildren call 1.4174 + // will try to add the observer again. 1.4175 + if (mAllBookmarksObservers.IndexOf(aNode) == mAllBookmarksObservers.NoIndex) { 1.4176 + mAllBookmarksObservers.AppendElement(aNode); 1.4177 + } 1.4178 +} 1.4179 + 1.4180 + 1.4181 +void 1.4182 +nsNavHistoryResult::AddBookmarkFolderObserver(nsNavHistoryFolderResultNode* aNode, 1.4183 + int64_t aFolder) 1.4184 +{ 1.4185 + if (!mIsBookmarkFolderObserver && !mIsAllBookmarksObserver) { 1.4186 + nsNavBookmarks* bookmarks = nsNavBookmarks::GetBookmarksService(); 1.4187 + if (!bookmarks) { 1.4188 + NS_NOTREACHED("Can't create bookmark service"); 1.4189 + return; 1.4190 + } 1.4191 + bookmarks->AddObserver(this, true); 1.4192 + mIsBookmarkFolderObserver = true; 1.4193 + } 1.4194 + // Don't add duplicate observers. In some case we don't unregister when 1.4195 + // children are cleared (see ClearChildren) and the next FillChildren call 1.4196 + // will try to add the observer again. 1.4197 + FolderObserverList* list = BookmarkFolderObserversForId(aFolder, true); 1.4198 + if (list->IndexOf(aNode) == list->NoIndex) { 1.4199 + list->AppendElement(aNode); 1.4200 + } 1.4201 +} 1.4202 + 1.4203 + 1.4204 +void 1.4205 +nsNavHistoryResult::RemoveHistoryObserver(nsNavHistoryQueryResultNode* aNode) 1.4206 +{ 1.4207 + mHistoryObservers.RemoveElement(aNode); 1.4208 +} 1.4209 + 1.4210 + 1.4211 +void 1.4212 +nsNavHistoryResult::RemoveAllBookmarksObserver(nsNavHistoryQueryResultNode* aNode) 1.4213 +{ 1.4214 + mAllBookmarksObservers.RemoveElement(aNode); 1.4215 +} 1.4216 + 1.4217 + 1.4218 +void 1.4219 +nsNavHistoryResult::RemoveBookmarkFolderObserver(nsNavHistoryFolderResultNode* aNode, 1.4220 + int64_t aFolder) 1.4221 +{ 1.4222 + FolderObserverList* list = BookmarkFolderObserversForId(aFolder, false); 1.4223 + if (!list) 1.4224 + return; // we don't even have an entry for that folder 1.4225 + list->RemoveElement(aNode); 1.4226 +} 1.4227 + 1.4228 + 1.4229 +nsNavHistoryResult::FolderObserverList* 1.4230 +nsNavHistoryResult::BookmarkFolderObserversForId(int64_t aFolderId, bool aCreate) 1.4231 +{ 1.4232 + FolderObserverList* list; 1.4233 + if (mBookmarkFolderObservers.Get(aFolderId, &list)) 1.4234 + return list; 1.4235 + if (!aCreate) 1.4236 + return nullptr; 1.4237 + 1.4238 + // need to create a new list 1.4239 + list = new FolderObserverList; 1.4240 + mBookmarkFolderObservers.Put(aFolderId, list); 1.4241 + return list; 1.4242 +} 1.4243 + 1.4244 + 1.4245 +NS_IMETHODIMP 1.4246 +nsNavHistoryResult::GetSortingMode(uint16_t* aSortingMode) 1.4247 +{ 1.4248 + *aSortingMode = mSortingMode; 1.4249 + return NS_OK; 1.4250 +} 1.4251 + 1.4252 + 1.4253 +NS_IMETHODIMP 1.4254 +nsNavHistoryResult::SetSortingMode(uint16_t aSortingMode) 1.4255 +{ 1.4256 + NS_ENSURE_STATE(mRootNode); 1.4257 + 1.4258 + if (aSortingMode > nsINavHistoryQueryOptions::SORT_BY_FRECENCY_DESCENDING) 1.4259 + return NS_ERROR_INVALID_ARG; 1.4260 + 1.4261 + // Keep everything in sync. 1.4262 + NS_ASSERTION(mOptions, "Options should always be present for a root query"); 1.4263 + 1.4264 + mSortingMode = aSortingMode; 1.4265 + 1.4266 + if (!mRootNode->mExpanded) { 1.4267 + // Need to do this later when node will be expanded. 1.4268 + mNeedsToApplySortingMode = true; 1.4269 + return NS_OK; 1.4270 + } 1.4271 + 1.4272 + // Actually do sorting. 1.4273 + nsNavHistoryContainerResultNode::SortComparator comparator = 1.4274 + nsNavHistoryContainerResultNode::GetSortingComparator(aSortingMode); 1.4275 + if (comparator) { 1.4276 + nsNavHistory* history = nsNavHistory::GetHistoryService(); 1.4277 + NS_ENSURE_TRUE(history, NS_ERROR_OUT_OF_MEMORY); 1.4278 + mRootNode->RecursiveSort(mSortingAnnotation.get(), comparator); 1.4279 + } 1.4280 + 1.4281 + NOTIFY_RESULT_OBSERVERS(this, SortingChanged(aSortingMode)); 1.4282 + NOTIFY_RESULT_OBSERVERS(this, InvalidateContainer(mRootNode)); 1.4283 + return NS_OK; 1.4284 +} 1.4285 + 1.4286 + 1.4287 +NS_IMETHODIMP 1.4288 +nsNavHistoryResult::GetSortingAnnotation(nsACString& _result) { 1.4289 + _result.Assign(mSortingAnnotation); 1.4290 + return NS_OK; 1.4291 +} 1.4292 + 1.4293 + 1.4294 +NS_IMETHODIMP 1.4295 +nsNavHistoryResult::SetSortingAnnotation(const nsACString& aSortingAnnotation) { 1.4296 + mSortingAnnotation.Assign(aSortingAnnotation); 1.4297 + return NS_OK; 1.4298 +} 1.4299 + 1.4300 + 1.4301 +NS_IMETHODIMP 1.4302 +nsNavHistoryResult::AddObserver(nsINavHistoryResultObserver* aObserver, 1.4303 + bool aOwnsWeak) 1.4304 +{ 1.4305 + NS_ENSURE_ARG(aObserver); 1.4306 + nsresult rv = mObservers.AppendWeakElement(aObserver, aOwnsWeak); 1.4307 + NS_ENSURE_SUCCESS(rv, rv); 1.4308 + 1.4309 + rv = aObserver->SetResult(this); 1.4310 + NS_ENSURE_SUCCESS(rv, rv); 1.4311 + 1.4312 + // If we are batching, notify a fake batch start to the observers. 1.4313 + // Not doing so would then notify a not coupled batch end. 1.4314 + if (mBatchInProgress) { 1.4315 + NOTIFY_RESULT_OBSERVERS(this, Batching(true)); 1.4316 + } 1.4317 + 1.4318 + return NS_OK; 1.4319 +} 1.4320 + 1.4321 + 1.4322 +NS_IMETHODIMP 1.4323 +nsNavHistoryResult::RemoveObserver(nsINavHistoryResultObserver* aObserver) 1.4324 +{ 1.4325 + NS_ENSURE_ARG(aObserver); 1.4326 + return mObservers.RemoveWeakElement(aObserver); 1.4327 +} 1.4328 + 1.4329 + 1.4330 +NS_IMETHODIMP 1.4331 +nsNavHistoryResult::GetSuppressNotifications(bool* _retval) 1.4332 +{ 1.4333 + *_retval = mSuppressNotifications; 1.4334 + return NS_OK; 1.4335 +} 1.4336 + 1.4337 + 1.4338 +NS_IMETHODIMP 1.4339 +nsNavHistoryResult::SetSuppressNotifications(bool aSuppressNotifications) 1.4340 +{ 1.4341 + mSuppressNotifications = aSuppressNotifications; 1.4342 + return NS_OK; 1.4343 +} 1.4344 + 1.4345 + 1.4346 +NS_IMETHODIMP 1.4347 +nsNavHistoryResult::GetRoot(nsINavHistoryContainerResultNode** aRoot) 1.4348 +{ 1.4349 + if (!mRootNode) { 1.4350 + NS_NOTREACHED("Root is null"); 1.4351 + *aRoot = nullptr; 1.4352 + return NS_ERROR_FAILURE; 1.4353 + } 1.4354 + return mRootNode->QueryInterface(NS_GET_IID(nsINavHistoryContainerResultNode), 1.4355 + reinterpret_cast<void**>(aRoot)); 1.4356 +} 1.4357 + 1.4358 + 1.4359 +void 1.4360 +nsNavHistoryResult::requestRefresh(nsNavHistoryContainerResultNode* aContainer) 1.4361 +{ 1.4362 + // Don't add twice the same container. 1.4363 + if (mRefreshParticipants.IndexOf(aContainer) == mRefreshParticipants.NoIndex) 1.4364 + mRefreshParticipants.AppendElement(aContainer); 1.4365 +} 1.4366 + 1.4367 +// nsINavBookmarkObserver implementation 1.4368 + 1.4369 +// Here, it is important that we create a COPY of the observer array. Some 1.4370 +// observers will requery themselves, which may cause the observer array to 1.4371 +// be modified or added to. 1.4372 +#define ENUMERATE_BOOKMARK_FOLDER_OBSERVERS(_folderId, _functionCall) \ 1.4373 + PR_BEGIN_MACRO \ 1.4374 + FolderObserverList* _fol = BookmarkFolderObserversForId(_folderId, false); \ 1.4375 + if (_fol) { \ 1.4376 + FolderObserverList _listCopy(*_fol); \ 1.4377 + for (uint32_t _fol_i = 0; _fol_i < _listCopy.Length(); ++_fol_i) { \ 1.4378 + if (_listCopy[_fol_i]) \ 1.4379 + _listCopy[_fol_i]->_functionCall; \ 1.4380 + } \ 1.4381 + } \ 1.4382 + PR_END_MACRO 1.4383 +#define ENUMERATE_LIST_OBSERVERS(_listType, _functionCall, _observersList, _conditionCall) \ 1.4384 + PR_BEGIN_MACRO \ 1.4385 + _listType _listCopy(_observersList); \ 1.4386 + for (uint32_t _obs_i = 0; _obs_i < _listCopy.Length(); ++_obs_i) { \ 1.4387 + if (_listCopy[_obs_i] && _listCopy[_obs_i]->_conditionCall) \ 1.4388 + _listCopy[_obs_i]->_functionCall; \ 1.4389 + } \ 1.4390 + PR_END_MACRO 1.4391 +#define ENUMERATE_QUERY_OBSERVERS(_functionCall, _observersList, _conditionCall) \ 1.4392 + ENUMERATE_LIST_OBSERVERS(QueryObserverList, _functionCall, _observersList, _conditionCall) 1.4393 +#define ENUMERATE_ALL_BOOKMARKS_OBSERVERS(_functionCall) \ 1.4394 + ENUMERATE_QUERY_OBSERVERS(_functionCall, mAllBookmarksObservers, IsQuery()) 1.4395 +#define ENUMERATE_HISTORY_OBSERVERS(_functionCall) \ 1.4396 + ENUMERATE_QUERY_OBSERVERS(_functionCall, mHistoryObservers, IsQuery()) 1.4397 + 1.4398 +#define NOTIFY_REFRESH_PARTICIPANTS() \ 1.4399 + PR_BEGIN_MACRO \ 1.4400 + ENUMERATE_LIST_OBSERVERS(ContainerObserverList, Refresh(), mRefreshParticipants, IsContainer()); \ 1.4401 + mRefreshParticipants.Clear(); \ 1.4402 + PR_END_MACRO 1.4403 + 1.4404 +NS_IMETHODIMP 1.4405 +nsNavHistoryResult::OnBeginUpdateBatch() 1.4406 +{ 1.4407 + // Since we could be observing both history and bookmarks, it's possible both 1.4408 + // notify the batch. We can safely ignore nested calls. 1.4409 + if (!mBatchInProgress) { 1.4410 + mBatchInProgress = true; 1.4411 + ENUMERATE_HISTORY_OBSERVERS(OnBeginUpdateBatch()); 1.4412 + ENUMERATE_ALL_BOOKMARKS_OBSERVERS(OnBeginUpdateBatch()); 1.4413 + 1.4414 + NOTIFY_RESULT_OBSERVERS(this, Batching(true)); 1.4415 + } 1.4416 + 1.4417 + return NS_OK; 1.4418 +} 1.4419 + 1.4420 + 1.4421 +NS_IMETHODIMP 1.4422 +nsNavHistoryResult::OnEndUpdateBatch() 1.4423 +{ 1.4424 + // Since we could be observing both history and bookmarks, it's possible both 1.4425 + // notify the batch. We can safely ignore nested calls. 1.4426 + // Notice it's possible we are notified OnEndUpdateBatch more times than 1.4427 + // onBeginUpdateBatch, since the result could be created in the middle of 1.4428 + // nested batches. 1.4429 + if (mBatchInProgress) { 1.4430 + ENUMERATE_HISTORY_OBSERVERS(OnEndUpdateBatch()); 1.4431 + ENUMERATE_ALL_BOOKMARKS_OBSERVERS(OnEndUpdateBatch()); 1.4432 + 1.4433 + // Setting mBatchInProgress before notifying the end of the batch to 1.4434 + // observers would make evantual calls to Refresh() directly handled rather 1.4435 + // than enqueued. Thus set it just before handling refreshes. 1.4436 + mBatchInProgress = false; 1.4437 + NOTIFY_REFRESH_PARTICIPANTS(); 1.4438 + NOTIFY_RESULT_OBSERVERS(this, Batching(false)); 1.4439 + } 1.4440 + 1.4441 + return NS_OK; 1.4442 +} 1.4443 + 1.4444 + 1.4445 +NS_IMETHODIMP 1.4446 +nsNavHistoryResult::OnItemAdded(int64_t aItemId, 1.4447 + int64_t aParentId, 1.4448 + int32_t aIndex, 1.4449 + uint16_t aItemType, 1.4450 + nsIURI* aURI, 1.4451 + const nsACString& aTitle, 1.4452 + PRTime aDateAdded, 1.4453 + const nsACString& aGUID, 1.4454 + const nsACString& aParentGUID) 1.4455 +{ 1.4456 + ENUMERATE_BOOKMARK_FOLDER_OBSERVERS(aParentId, 1.4457 + OnItemAdded(aItemId, aParentId, aIndex, aItemType, aURI, aTitle, aDateAdded, 1.4458 + aGUID, aParentGUID) 1.4459 + ); 1.4460 + ENUMERATE_HISTORY_OBSERVERS( 1.4461 + OnItemAdded(aItemId, aParentId, aIndex, aItemType, aURI, aTitle, aDateAdded, 1.4462 + aGUID, aParentGUID) 1.4463 + ); 1.4464 + ENUMERATE_ALL_BOOKMARKS_OBSERVERS( 1.4465 + OnItemAdded(aItemId, aParentId, aIndex, aItemType, aURI, aTitle, aDateAdded, 1.4466 + aGUID, aParentGUID) 1.4467 + ); 1.4468 + return NS_OK; 1.4469 +} 1.4470 + 1.4471 + 1.4472 +NS_IMETHODIMP 1.4473 +nsNavHistoryResult::OnItemRemoved(int64_t aItemId, 1.4474 + int64_t aParentId, 1.4475 + int32_t aIndex, 1.4476 + uint16_t aItemType, 1.4477 + nsIURI* aURI, 1.4478 + const nsACString& aGUID, 1.4479 + const nsACString& aParentGUID) 1.4480 +{ 1.4481 + ENUMERATE_BOOKMARK_FOLDER_OBSERVERS(aParentId, 1.4482 + OnItemRemoved(aItemId, aParentId, aIndex, aItemType, aURI, aGUID, 1.4483 + aParentGUID)); 1.4484 + ENUMERATE_ALL_BOOKMARKS_OBSERVERS( 1.4485 + OnItemRemoved(aItemId, aParentId, aIndex, aItemType, aURI, aGUID, 1.4486 + aParentGUID)); 1.4487 + ENUMERATE_HISTORY_OBSERVERS( 1.4488 + OnItemRemoved(aItemId, aParentId, aIndex, aItemType, aURI, aGUID, 1.4489 + aParentGUID)); 1.4490 + return NS_OK; 1.4491 +} 1.4492 + 1.4493 + 1.4494 +NS_IMETHODIMP 1.4495 +nsNavHistoryResult::OnItemChanged(int64_t aItemId, 1.4496 + const nsACString &aProperty, 1.4497 + bool aIsAnnotationProperty, 1.4498 + const nsACString &aNewValue, 1.4499 + PRTime aLastModified, 1.4500 + uint16_t aItemType, 1.4501 + int64_t aParentId, 1.4502 + const nsACString& aGUID, 1.4503 + const nsACString& aParentGUID) 1.4504 +{ 1.4505 + ENUMERATE_ALL_BOOKMARKS_OBSERVERS( 1.4506 + OnItemChanged(aItemId, aProperty, aIsAnnotationProperty, aNewValue, 1.4507 + aLastModified, aItemType, aParentId, aGUID, aParentGUID)); 1.4508 + 1.4509 + // Note: folder-nodes set their own bookmark observer only once they're 1.4510 + // opened, meaning we cannot optimize this code path for changes done to 1.4511 + // folder-nodes. 1.4512 + 1.4513 + FolderObserverList* list = BookmarkFolderObserversForId(aParentId, false); 1.4514 + if (!list) 1.4515 + return NS_OK; 1.4516 + 1.4517 + for (uint32_t i = 0; i < list->Length(); ++i) { 1.4518 + nsRefPtr<nsNavHistoryFolderResultNode> folder = list->ElementAt(i); 1.4519 + if (folder) { 1.4520 + uint32_t nodeIndex; 1.4521 + nsRefPtr<nsNavHistoryResultNode> node = 1.4522 + folder->FindChildById(aItemId, &nodeIndex); 1.4523 + // if ExcludeItems is true we don't update non visible items 1.4524 + bool excludeItems = (mRootNode->mOptions->ExcludeItems()) || 1.4525 + folder->mOptions->ExcludeItems(); 1.4526 + if (node && 1.4527 + (!excludeItems || !(node->IsURI() || node->IsSeparator())) && 1.4528 + folder->StartIncrementalUpdate()) { 1.4529 + node->OnItemChanged(aItemId, aProperty, aIsAnnotationProperty, 1.4530 + aNewValue, aLastModified, aItemType, aParentId, 1.4531 + aGUID, aParentGUID); 1.4532 + } 1.4533 + } 1.4534 + } 1.4535 + 1.4536 + // Note: we do NOT call history observers in this case. This notification is 1.4537 + // the same as other history notification, except that here we know the item 1.4538 + // is a bookmark. History observers will handle the history notification 1.4539 + // instead. 1.4540 + return NS_OK; 1.4541 +} 1.4542 + 1.4543 + 1.4544 +NS_IMETHODIMP 1.4545 +nsNavHistoryResult::OnItemVisited(int64_t aItemId, 1.4546 + int64_t aVisitId, 1.4547 + PRTime aVisitTime, 1.4548 + uint32_t aTransitionType, 1.4549 + nsIURI* aURI, 1.4550 + int64_t aParentId, 1.4551 + const nsACString& aGUID, 1.4552 + const nsACString& aParentGUID) 1.4553 +{ 1.4554 + ENUMERATE_BOOKMARK_FOLDER_OBSERVERS(aParentId, 1.4555 + OnItemVisited(aItemId, aVisitId, aVisitTime, aTransitionType, aURI, 1.4556 + aParentId, aGUID, aParentGUID)); 1.4557 + ENUMERATE_ALL_BOOKMARKS_OBSERVERS( 1.4558 + OnItemVisited(aItemId, aVisitId, aVisitTime, aTransitionType, aURI, 1.4559 + aParentId, aGUID, aParentGUID)); 1.4560 + // Note: we do NOT call history observers in this case. This notification is 1.4561 + // the same as OnVisit, except that here we know the item is a bookmark. 1.4562 + // History observers will handle the history notification instead. 1.4563 + return NS_OK; 1.4564 +} 1.4565 + 1.4566 + 1.4567 +/** 1.4568 + * Need to notify both the source and the destination folders (if they are 1.4569 + * different). 1.4570 + */ 1.4571 +NS_IMETHODIMP 1.4572 +nsNavHistoryResult::OnItemMoved(int64_t aItemId, 1.4573 + int64_t aOldParent, 1.4574 + int32_t aOldIndex, 1.4575 + int64_t aNewParent, 1.4576 + int32_t aNewIndex, 1.4577 + uint16_t aItemType, 1.4578 + const nsACString& aGUID, 1.4579 + const nsACString& aOldParentGUID, 1.4580 + const nsACString& aNewParentGUID) 1.4581 +{ 1.4582 + ENUMERATE_BOOKMARK_FOLDER_OBSERVERS(aOldParent, 1.4583 + OnItemMoved(aItemId, aOldParent, aOldIndex, aNewParent, aNewIndex, 1.4584 + aItemType, aGUID, aOldParentGUID, aNewParentGUID)); 1.4585 + if (aNewParent != aOldParent) { 1.4586 + ENUMERATE_BOOKMARK_FOLDER_OBSERVERS(aNewParent, 1.4587 + OnItemMoved(aItemId, aOldParent, aOldIndex, aNewParent, aNewIndex, 1.4588 + aItemType, aGUID, aOldParentGUID, aNewParentGUID)); 1.4589 + } 1.4590 + ENUMERATE_ALL_BOOKMARKS_OBSERVERS(OnItemMoved(aItemId, aOldParent, aOldIndex, 1.4591 + aNewParent, aNewIndex, 1.4592 + aItemType, aGUID, 1.4593 + aOldParentGUID, 1.4594 + aNewParentGUID)); 1.4595 + ENUMERATE_HISTORY_OBSERVERS(OnItemMoved(aItemId, aOldParent, aOldIndex, 1.4596 + aNewParent, aNewIndex, aItemType, 1.4597 + aGUID, aOldParentGUID, 1.4598 + aNewParentGUID)); 1.4599 + return NS_OK; 1.4600 +} 1.4601 + 1.4602 + 1.4603 +NS_IMETHODIMP 1.4604 +nsNavHistoryResult::OnVisit(nsIURI* aURI, int64_t aVisitId, PRTime aTime, 1.4605 + int64_t aSessionId, int64_t aReferringId, 1.4606 + uint32_t aTransitionType, const nsACString& aGUID, 1.4607 + bool aHidden) 1.4608 +{ 1.4609 + uint32_t added = 0; 1.4610 + 1.4611 + ENUMERATE_HISTORY_OBSERVERS(OnVisit(aURI, aVisitId, aTime, aSessionId, 1.4612 + aReferringId, aTransitionType, aGUID, 1.4613 + aHidden, &added)); 1.4614 + 1.4615 + if (!mRootNode->mExpanded) 1.4616 + return NS_OK; 1.4617 + 1.4618 + // If this visit is accepted by an overlapped container, and not all 1.4619 + // overlapped containers are visible, we should still call Refresh if the 1.4620 + // visit falls into any of them. 1.4621 + bool todayIsMissing = false; 1.4622 + uint32_t resultType = mRootNode->mOptions->ResultType(); 1.4623 + if (resultType == nsINavHistoryQueryOptions::RESULTS_AS_DATE_QUERY || 1.4624 + resultType == nsINavHistoryQueryOptions::RESULTS_AS_DATE_SITE_QUERY) { 1.4625 + uint32_t childCount; 1.4626 + nsresult rv = mRootNode->GetChildCount(&childCount); 1.4627 + NS_ENSURE_SUCCESS(rv, rv); 1.4628 + if (childCount) { 1.4629 + nsCOMPtr<nsINavHistoryResultNode> firstChild; 1.4630 + rv = mRootNode->GetChild(0, getter_AddRefs(firstChild)); 1.4631 + NS_ENSURE_SUCCESS(rv, rv); 1.4632 + nsAutoCString title; 1.4633 + rv = firstChild->GetTitle(title); 1.4634 + NS_ENSURE_SUCCESS(rv, rv); 1.4635 + nsNavHistory* history = nsNavHistory::GetHistoryService(); 1.4636 + NS_ENSURE_TRUE(history, NS_OK); 1.4637 + nsAutoCString todayLabel; 1.4638 + history->GetStringFromName( 1.4639 + MOZ_UTF16("finduri-AgeInDays-is-0"), todayLabel); 1.4640 + todayIsMissing = !todayLabel.Equals(title); 1.4641 + } 1.4642 + } 1.4643 + 1.4644 + if (!added || todayIsMissing) { 1.4645 + // None of registered query observers has accepted our URI. This means, 1.4646 + // that a matching query either was not expanded or it does not exist. 1.4647 + uint32_t resultType = mRootNode->mOptions->ResultType(); 1.4648 + if (resultType == nsINavHistoryQueryOptions::RESULTS_AS_DATE_QUERY || 1.4649 + resultType == nsINavHistoryQueryOptions::RESULTS_AS_DATE_SITE_QUERY) { 1.4650 + // If the visit falls into the Today bucket and the bucket exists, it was 1.4651 + // just not expanded, thus there's no reason to update. 1.4652 + int64_t beginOfToday = 1.4653 + nsNavHistory::NormalizeTime(nsINavHistoryQuery::TIME_RELATIVE_TODAY, 0); 1.4654 + if (todayIsMissing || aTime < beginOfToday) { 1.4655 + (void)mRootNode->GetAsQuery()->Refresh(); 1.4656 + } 1.4657 + return NS_OK; 1.4658 + } 1.4659 + 1.4660 + if (resultType == nsINavHistoryQueryOptions::RESULTS_AS_SITE_QUERY) { 1.4661 + (void)mRootNode->GetAsQuery()->Refresh(); 1.4662 + return NS_OK; 1.4663 + } 1.4664 + 1.4665 + // We are result of a folder node, then we should run through history 1.4666 + // observers that are containers queries and refresh them. 1.4667 + // We use a copy of the observers array since requerying could potentially 1.4668 + // cause changes to the array. 1.4669 + ENUMERATE_QUERY_OBSERVERS(Refresh(), mHistoryObservers, IsContainersQuery()); 1.4670 + } 1.4671 + 1.4672 + return NS_OK; 1.4673 +} 1.4674 + 1.4675 + 1.4676 +NS_IMETHODIMP 1.4677 +nsNavHistoryResult::OnTitleChanged(nsIURI* aURI, 1.4678 + const nsAString& aPageTitle, 1.4679 + const nsACString& aGUID) 1.4680 +{ 1.4681 + ENUMERATE_HISTORY_OBSERVERS(OnTitleChanged(aURI, aPageTitle, aGUID)); 1.4682 + return NS_OK; 1.4683 +} 1.4684 + 1.4685 + 1.4686 +NS_IMETHODIMP 1.4687 +nsNavHistoryResult::OnFrecencyChanged(nsIURI* aURI, 1.4688 + int32_t aNewFrecency, 1.4689 + const nsACString& aGUID, 1.4690 + bool aHidden, 1.4691 + PRTime aLastVisitDate) 1.4692 +{ 1.4693 + return NS_OK; 1.4694 +} 1.4695 + 1.4696 + 1.4697 +NS_IMETHODIMP 1.4698 +nsNavHistoryResult::OnManyFrecenciesChanged() 1.4699 +{ 1.4700 + return NS_OK; 1.4701 +} 1.4702 + 1.4703 + 1.4704 +NS_IMETHODIMP 1.4705 +nsNavHistoryResult::OnDeleteURI(nsIURI *aURI, 1.4706 + const nsACString& aGUID, 1.4707 + uint16_t aReason) 1.4708 +{ 1.4709 + ENUMERATE_HISTORY_OBSERVERS(OnDeleteURI(aURI, aGUID, aReason)); 1.4710 + return NS_OK; 1.4711 +} 1.4712 + 1.4713 + 1.4714 +NS_IMETHODIMP 1.4715 +nsNavHistoryResult::OnClearHistory() 1.4716 +{ 1.4717 + ENUMERATE_HISTORY_OBSERVERS(OnClearHistory()); 1.4718 + return NS_OK; 1.4719 +} 1.4720 + 1.4721 + 1.4722 +NS_IMETHODIMP 1.4723 +nsNavHistoryResult::OnPageChanged(nsIURI* aURI, 1.4724 + uint32_t aChangedAttribute, 1.4725 + const nsAString& aValue, 1.4726 + const nsACString& aGUID) 1.4727 +{ 1.4728 + ENUMERATE_HISTORY_OBSERVERS(OnPageChanged(aURI, aChangedAttribute, aValue, aGUID)); 1.4729 + return NS_OK; 1.4730 +} 1.4731 + 1.4732 + 1.4733 +/** 1.4734 + * Don't do anything when visits expire. 1.4735 + */ 1.4736 +NS_IMETHODIMP 1.4737 +nsNavHistoryResult::OnDeleteVisits(nsIURI* aURI, 1.4738 + PRTime aVisitTime, 1.4739 + const nsACString& aGUID, 1.4740 + uint16_t aReason, 1.4741 + uint32_t aTransitionType) 1.4742 +{ 1.4743 + ENUMERATE_HISTORY_OBSERVERS(OnDeleteVisits(aURI, aVisitTime, aGUID, aReason, 1.4744 + aTransitionType)); 1.4745 + return NS_OK; 1.4746 +}