Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(623)

Unified Diff: ios/chrome/app/spotlight/bookmarks_spotlight_manager.mm

Issue 2580363002: Upstream Chrome on iOS source code [1/11]. (Closed)
Patch Set: Created 4 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: ios/chrome/app/spotlight/bookmarks_spotlight_manager.mm
diff --git a/ios/chrome/app/spotlight/bookmarks_spotlight_manager.mm b/ios/chrome/app/spotlight/bookmarks_spotlight_manager.mm
new file mode 100644
index 0000000000000000000000000000000000000000..edbfd6a7017fbf4c428d2872d7e850c9c57606a5
--- /dev/null
+++ b/ios/chrome/app/spotlight/bookmarks_spotlight_manager.mm
@@ -0,0 +1,348 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/chrome/app/spotlight/bookmarks_spotlight_manager.h"
+
+#include <memory>
+
+#import <CoreSpotlight/CoreSpotlight.h>
+
+#include "base/ios/weak_nsobject.h"
+#include "base/metrics/histogram.h"
+#include "base/strings/sys_string_conversions.h"
+#include "base/version.h"
+#include "components/bookmarks/browser/base_bookmark_model_observer.h"
+#include "components/bookmarks/browser/bookmark_model.h"
+#include "ios/chrome/browser/bookmarks/bookmark_model_factory.h"
+#include "ios/chrome/browser/favicon/ios_chrome_large_icon_service_factory.h"
+
+namespace {
+// Limit the size of the initial indexing. This will not limit the size of the
+// index as new bookmarks can be added afterwards.
+const int kMaxInitialIndexSize = 1000;
+
+// Minimum delay between two global indexing of bookmarks.
+const int kDelayBetweenTwoIndexingInSeconds = 7 * 86400; // One week.
+
+} // namespace
+
+class SpotlightBookmarkModelBridge;
+
+// Called from the BrowserBookmarkModelBridge from C++ -> ObjC.
+@interface BookmarksSpotlightManager () {
+ base::WeakNSProtocol<id<BookmarkUpdatedDelegate>> _delegate;
+
+ // Bridge to register for bookmark changes.
+ std::unique_ptr<SpotlightBookmarkModelBridge> _bookmarkModelBridge;
+
+ // Keep a reference to detach before deallocing. Life cycle of _bookmarkModel
+ // is longer than life cycle of a SpotlightManager as
+ // |BookmarkModelBeingDeleted| will cause deletion of SpotlightManager.
+ bookmarks::BookmarkModel* _bookmarkModel; // weak
+
+ // Number of nodes indexed in initial scan.
+ NSUInteger _nodesIndexed;
+
+ // Tracks whether initial indexing has been done.
+ BOOL _initialIndexDone;
+}
+
+// Detaches the |SpotlightBookmarkModelBridge| from the bookmark model. The
+// manager must not be used after calling this method.
+- (void)detachBookmarkModel;
+
+// Removes the node from the Spotlight index.
+- (void)removeNodeFromIndex:(const bookmarks::BookmarkNode*)node;
+
+// Clears all the bookmarks in the Spotlight index then index the bookmarks in
+// the model.
+- (void)clearAndReindexModel;
+
+// Refreshes all nodes in the subtree of node.
+// If |initial| is YES, limit the number of nodes to kMaxInitialIndexSize.
+- (void)refreshNodeInIndex:(const bookmarks::BookmarkNode*)node
+ initial:(BOOL)initial;
+
+// Returns true is the current index is too old or from an incompatible version.
+- (BOOL)shouldReindex;
+
+@end
+
+// Handles notification that bookmarks has been removed changed so we can update
+// the Spotlight index.
+class SpotlightBookmarkModelBridge : public bookmarks::BookmarkModelObserver {
+ public:
+ explicit SpotlightBookmarkModelBridge(BookmarksSpotlightManager* owner)
+ : owner_(owner){};
+
+ ~SpotlightBookmarkModelBridge() override{};
+
+ void BookmarkNodeRemoved(bookmarks::BookmarkModel* model,
+ const bookmarks::BookmarkNode* parent,
+ int old_index,
+ const bookmarks::BookmarkNode* node,
+ const std::set<GURL>& removed_urls) override {}
+
+ void OnWillRemoveBookmarks(bookmarks::BookmarkModel* model,
+ const bookmarks::BookmarkNode* parent,
+ int old_index,
+ const bookmarks::BookmarkNode* node) override {
+ [owner_ removeNodeFromIndex:node];
+ }
+
+ void BookmarkModelBeingDeleted(bookmarks::BookmarkModel* model) override {
+ [owner_ detachBookmarkModel];
+ };
+
+ void BookmarkModelLoaded(bookmarks::BookmarkModel* model,
+ bool ids_reassigned) override {
+ [owner_ reindexBookmarksIfNeeded];
+ }
+
+ void BookmarkNodeAdded(bookmarks::BookmarkModel* model,
+ const bookmarks::BookmarkNode* parent,
+ int index) override {
+ [owner_ refreshNodeInIndex:parent->GetChild(index) initial:NO];
+ }
+
+ void OnWillChangeBookmarkNode(bookmarks::BookmarkModel* model,
+ const bookmarks::BookmarkNode* node) override {
+ [owner_ removeNodeFromIndex:node];
+ }
+
+ void BookmarkNodeChanged(bookmarks::BookmarkModel* model,
+ const bookmarks::BookmarkNode* node) override {
+ [owner_ refreshNodeInIndex:node initial:NO];
+ }
+
+ void BookmarkNodeFaviconChanged(
+ bookmarks::BookmarkModel* model,
+ const bookmarks::BookmarkNode* node) override {
+ [owner_ refreshNodeInIndex:node initial:NO];
+ }
+
+ void BookmarkAllUserNodesRemoved(
+ bookmarks::BookmarkModel* model,
+ const std::set<GURL>& removed_urls) override {
+ [owner_ clearAllSpotlightItems:nil];
+ }
+
+ void BookmarkNodeChildrenReordered(
+ bookmarks::BookmarkModel* model,
+ const bookmarks::BookmarkNode* node) override{};
+
+ void BookmarkNodeMoved(bookmarks::BookmarkModel* model,
+ const bookmarks::BookmarkNode* old_parent,
+ int old_index,
+ const bookmarks::BookmarkNode* new_parent,
+ int new_index) override {
+ [owner_ refreshNodeInIndex:new_parent->GetChild(new_index) initial:NO];
+ };
+
+ private:
+ __unsafe_unretained BookmarksSpotlightManager* owner_; // Weak.
+};
+
+@implementation BookmarksSpotlightManager
+
++ (BookmarksSpotlightManager*)bookmarksSpotlightManagerWithBrowserState:
+ (ios::ChromeBrowserState*)browserState {
+ return [[[BookmarksSpotlightManager alloc]
+ initWithLargeIconService:IOSChromeLargeIconServiceFactory::
+ GetForBrowserState(browserState)
+ bookmarkModel:ios::BookmarkModelFactory::GetForBrowserState(
+ browserState)] autorelease];
+}
+
+- (instancetype)
+initWithLargeIconService:(favicon::LargeIconService*)largeIconService
+ bookmarkModel:(bookmarks::BookmarkModel*)bookmarkModel {
+ self = [super initWithLargeIconService:largeIconService
+ domain:spotlight::DOMAIN_BOOKMARKS];
+ if (self) {
+ _bookmarkModelBridge.reset(new SpotlightBookmarkModelBridge(self));
+ _bookmarkModel = bookmarkModel;
+ bookmarkModel->AddObserver(_bookmarkModelBridge.get());
+ }
+ return self;
+}
+
+- (void)dealloc {
+ [self detachBookmarkModel];
+ [super dealloc];
+}
+
+- (void)detachBookmarkModel {
+ [self cancelAllLargeIconPendingTasks];
+ if (_bookmarkModelBridge.get()) {
+ _bookmarkModel->RemoveObserver(_bookmarkModelBridge.get());
+ _bookmarkModelBridge.reset();
+ }
+}
+
+- (id<BookmarkUpdatedDelegate>)delegate {
+ return _delegate;
+}
+
+- (void)setDelegate:(id<BookmarkUpdatedDelegate>)delegate {
+ _delegate.reset(delegate);
+}
+
+- (void)getParentKeywordsForNode:(const bookmarks::BookmarkNode*)node
+ inArray:(NSMutableArray*)keywords {
+ if (!node) {
+ return;
+ }
+ if (node->is_folder() && !_bookmarkModel->is_permanent_node(node)) {
+ [keywords addObject:base::SysUTF16ToNSString(node->GetTitle())];
+ }
+ [self getParentKeywordsForNode:node->parent() inArray:keywords];
+}
+
+- (void)removeNodeFromIndex:(const bookmarks::BookmarkNode*)node {
+ if (node->is_url()) {
+ GURL url(node->url());
+ NSString* title = base::SysUTF16ToNSString(node->GetTitle());
+ NSString* spotlightID = [self spotlightIDForURL:url title:title];
+ base::WeakNSObject<BookmarksSpotlightManager> weakself(self);
+ BlockWithError completion = ^(NSError* error) {
+ dispatch_async(dispatch_get_main_queue(), ^{
+ [weakself refreshItemsWithURL:url title:nil];
+ [_delegate bookmarkUpdated];
+ });
+ };
+ spotlight::DeleteItemsWithIdentifiers(@[ spotlightID ], completion);
+ return;
+ }
+ int childCount = node->child_count();
+ for (int child = 0; child < childCount; child++) {
+ [self removeNodeFromIndex:node->GetChild(child)];
+ }
+}
+
+- (BOOL)shouldReindex {
+ NSDate* date = [[NSUserDefaults standardUserDefaults]
+ objectForKey:@(spotlight::kSpotlightLastIndexingDateKey)];
+ if (!date) {
+ return YES;
+ }
+ NSDate* expirationDate =
+ [date dateByAddingTimeInterval:kDelayBetweenTwoIndexingInSeconds];
+ if ([expirationDate compare:[NSDate date]] == NSOrderedAscending) {
+ return YES;
+ }
+ NSNumber* lastIndexedVersionString = [[NSUserDefaults standardUserDefaults]
+ objectForKey:@(spotlight::kSpotlightLastIndexingVersionKey)];
+ if (!lastIndexedVersionString) {
+ return YES;
+ }
+
+ if ([lastIndexedVersionString integerValue] <
+ spotlight::kCurrentSpotlightIndexVersion) {
+ return YES;
+ }
+ return NO;
+}
+
+- (void)reindexBookmarksIfNeeded {
+ if (!_bookmarkModel->loaded() || _initialIndexDone) {
+ return;
+ }
+ _initialIndexDone = YES;
+ if ([self shouldReindex]) {
+ [self clearAndReindexModel];
+ }
+}
+
+- (void)addKeywords:(NSArray*)keywords
+ toSearchableItem:(CSSearchableItem*)item {
+ NSSet* itemKeywords = [NSSet setWithArray:[[item attributeSet] keywords]];
+ itemKeywords = [itemKeywords setByAddingObjectsFromArray:keywords];
+ [[item attributeSet] setKeywords:[itemKeywords allObjects]];
+}
+
+- (void)refreshNodeInIndex:(const bookmarks::BookmarkNode*)node
+ initial:(BOOL)initial {
+ if (initial && _nodesIndexed > kMaxInitialIndexSize) {
+ return;
+ }
+ if (node->is_url()) {
+ _nodesIndexed++;
+ [self refreshItemsWithURL:node->url() title:nil];
+ if (!initial) {
+ [_delegate bookmarkUpdated];
+ }
+ return;
+ }
+ int childCount = node->child_count();
+ for (int child = 0; child < childCount; child++) {
+ [self refreshNodeInIndex:node->GetChild(child) initial:initial];
+ }
+}
+
+- (NSArray*)spotlightItemsWithURL:(const GURL&)URL
+ favicon:(UIImage*)favicon
+ defaultTitle:(NSString*)defaultTitle {
+ base::scoped_nsobject<NSMutableDictionary> spotlightItems(
+ [[NSMutableDictionary alloc] init]);
+ std::vector<const bookmarks::BookmarkNode*> nodes;
+ _bookmarkModel->GetNodesByURL(URL, &nodes);
+ for (auto node : nodes) {
+ NSString* nodeTitle = base::SysUTF16ToNSString(node->GetTitle());
+ NSString* spotlightID = [self spotlightIDForURL:URL title:nodeTitle];
+ CSSearchableItem* item = [spotlightItems objectForKey:spotlightID];
+ if (!item) {
+ item = [[super spotlightItemsWithURL:URL
+ favicon:favicon
+ defaultTitle:nodeTitle] objectAtIndex:0];
+ }
+ base::scoped_nsobject<NSMutableArray> nodeKeywords(
+ [[NSMutableArray alloc] init]);
+ [self getParentKeywordsForNode:node inArray:nodeKeywords.get()];
+ [self addKeywords:nodeKeywords toSearchableItem:item];
+ [spotlightItems setObject:item forKey:spotlightID];
+ }
+ return [spotlightItems allValues];
+}
+
+- (void)clearAndReindexModel {
+ [self cancelAllLargeIconPendingTasks];
+ base::WeakNSObject<BookmarksSpotlightManager> weakself(self);
+ BlockWithError completion = ^(NSError* error) {
+ if (!error) {
+ dispatch_async(dispatch_get_main_queue(), ^{
+ base::scoped_nsobject<BookmarksSpotlightManager> strongSelf(
+ [weakself retain]);
+ if (!strongSelf)
+ return;
+
+ NSDate* startOfReindexing = [NSDate date];
+ strongSelf.get()->_nodesIndexed = 0;
+ [strongSelf
+ refreshNodeInIndex:strongSelf.get()->_bookmarkModel->root_node()
+ initial:YES];
+ NSDate* endOfReindexing = [NSDate date];
+ NSTimeInterval indexingDuration =
+ [endOfReindexing timeIntervalSinceDate:startOfReindexing];
+ UMA_HISTOGRAM_TIMES(
+ "IOS.Spotlight.BookmarksIndexingDuration",
+ base::TimeDelta::FromMillisecondsD(1000 * indexingDuration));
+ UMA_HISTOGRAM_COUNTS_1000("IOS.Spotlight.BookmarksInitialIndexSize",
+ [strongSelf pendingLargeIconTasksCount]);
+ [[NSUserDefaults standardUserDefaults]
+ setObject:endOfReindexing
+ forKey:@(spotlight::kSpotlightLastIndexingDateKey)];
+
+ [[NSUserDefaults standardUserDefaults]
+ setObject:[NSNumber numberWithInteger:
+ spotlight::kCurrentSpotlightIndexVersion]
+ forKey:@(spotlight::kSpotlightLastIndexingVersionKey)];
+ [_delegate bookmarkUpdated];
+ });
+ }
+ };
+ [self clearAllSpotlightItems:completion];
+}
+
+@end
« no previous file with comments | « ios/chrome/app/spotlight/bookmarks_spotlight_manager.h ('k') | ios/chrome/app/spotlight/spotlight_manager.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698