Index: ios/chrome/browser/ui/tab_switcher/tab_switcher_utils.mm |
diff --git a/ios/chrome/browser/ui/tab_switcher/tab_switcher_utils.mm b/ios/chrome/browser/ui/tab_switcher/tab_switcher_utils.mm |
new file mode 100644 |
index 0000000000000000000000000000000000000000..76237df82a8594d95e4c15e0e2b30e51e3ca4489 |
--- /dev/null |
+++ b/ios/chrome/browser/ui/tab_switcher/tab_switcher_utils.mm |
@@ -0,0 +1,190 @@ |
+// 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/browser/ui/tab_switcher/tab_switcher_utils.h" |
+ |
+#import <UIKit/UIKit.h> |
+ |
+#include "base/logging.h" |
+#include "base/mac/scoped_nsobject.h" |
+#include "components/browser_sync/profile_sync_service.h" |
+#include "components/sync/driver/sync_service.h" |
+#include "components/sync_sessions/open_tabs_ui_delegate.h" |
+#include "ios/chrome/browser/browser_state/chrome_browser_state.h" |
+#import "ios/chrome/browser/favicon/favicon_loader.h" |
+#include "ios/chrome/browser/favicon/ios_chrome_favicon_loader_factory.h" |
+#include "ios/chrome/browser/sync/ios_chrome_profile_sync_service_factory.h" |
+#include "ios/chrome/grit/ios_theme_resources.h" |
+#include "ui/base/resource/resource_bundle.h" |
+ |
+namespace ios_internal { |
+ |
+UIImage* DefaultFaviconImage() { |
+ ResourceBundle& rb = ResourceBundle::GetSharedInstance(); |
+ return rb.GetNativeImageNamed(IDR_IOS_OMNIBOX_HTTP).ToUIImage(); |
+} |
+ |
+void GetFavicon(GURL const& url, |
+ ios::ChromeBrowserState* browser_state, |
+ ios_internal::FaviconGetterCompletionBlock block) { |
+ DCHECK(browser_state); |
+ syncer::SyncService* sync_service = |
+ IOSChromeProfileSyncServiceFactory::GetForBrowserState(browser_state); |
+ sync_sessions::OpenTabsUIDelegate* open_tabs = |
+ sync_service ? sync_service->GetOpenTabsUIDelegate() : NULL; |
+ scoped_refptr<base::RefCountedMemory> favicon; |
+ if (open_tabs && |
+ open_tabs->GetSyncedFaviconForPageURL(url.spec(), &favicon)) { |
+ dispatch_queue_t queue = |
+ dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0); |
+ dispatch_async(queue, ^{ |
+ NSData* pngData = |
+ [NSData dataWithBytes:favicon->front() length:favicon->size()]; |
+ base::scoped_nsobject<UIImage> image( |
+ [[UIImage alloc] initWithData:pngData]); |
+ dispatch_async(dispatch_get_main_queue(), ^{ |
+ // |UIImage initWithData:| may return nil. |
+ if (image) { |
+ block(image); |
+ } else { |
+ block(DefaultFaviconImage()); |
+ } |
+ }); |
+ }); |
+ block(DefaultFaviconImage()); |
+ return; |
+ } |
+ |
+ // Use the FaviconCache if there is no synced favicon. |
+ FaviconLoader* loader = |
+ IOSChromeFaviconLoaderFactory::GetForBrowserState(browser_state); |
+ if (loader) { |
+ int types = favicon_base::FAVICON | favicon_base::TOUCH_ICON | |
+ favicon_base::TOUCH_PRECOMPOSED_ICON; |
+ UIImage* image = loader->ImageForURL(url, types, block); |
+ DCHECK(image); |
+ block(image); |
+ return; |
+ } |
+ // Finally returns a default image. |
+ block(DefaultFaviconImage()); |
+} |
+ |
+enum BacktrackOperation { NOTHING, SUBSTITUTION, DELETION, INSERTION }; |
+ |
+BacktrackOperation BacktrackOperationInCostMatrix( |
+ std::vector<std::vector<int>> const& costMatrix, |
+ size_t finalIndex, |
+ size_t initialIndex) { |
+ DCHECK(finalIndex || initialIndex); |
+ DCHECK(initialIndex < costMatrix.size()); |
+ DCHECK(finalIndex < costMatrix[initialIndex].size()); |
+ |
+ if (finalIndex == 0) |
+ return DELETION; |
+ if (initialIndex == 0) |
+ return INSERTION; |
+ |
+ int currentCost = costMatrix[initialIndex][finalIndex]; |
+ |
+ int costBeforeInsertion = costMatrix[initialIndex][finalIndex - 1]; |
+ if (costBeforeInsertion + 1 == currentCost) |
+ return INSERTION; |
+ |
+ int costBeforeDeletion = costMatrix[initialIndex - 1][finalIndex]; |
+ if (costBeforeDeletion + 1 == currentCost) |
+ return DELETION; |
+ |
+ int costBeforeSubstitution = costMatrix[initialIndex - 1][finalIndex - 1]; |
+ if (costBeforeSubstitution == currentCost) |
+ return NOTHING; |
+ |
+ return SUBSTITUTION; |
+} |
+ |
+void MinimalReplacementOperations(std::vector<size_t> const& initial, |
+ std::vector<size_t> const& final, |
+ std::vector<size_t>* substitutions, |
+ std::vector<size_t>* deletions, |
+ std::vector<size_t>* insertions) { |
+ DCHECK(substitutions); |
+ DCHECK(deletions); |
+ DCHECK(insertions); |
+ DCHECK_EQ(substitutions->size(), 0UL); |
+ DCHECK_EQ(deletions->size(), 0UL); |
+ DCHECK_EQ(insertions->size(), 0UL); |
+ |
+ // The substitutions/deletions/insertions are computed using the Levenshtein |
+ // algorithm. |
+ // https://en.wikipedia.org/wiki/Levenshtein_distance |
+ |
+ const size_t initialSize = initial.size() + 1; |
+ const size_t finalSize = final.size() + 1; |
+ |
+ std::vector<std::vector<int>> costMatrix(initialSize, |
+ std::vector<int>(finalSize)); |
+ |
+ for (size_t i = 1; i < initialSize; i++) |
+ costMatrix[i][0] = i; |
+ for (size_t i = 1; i < finalSize; i++) |
+ costMatrix[0][i] = i; |
+ |
+ // Step 1: Generate cost matrix. |
+ for (size_t initialIndex = 1; initialIndex < initialSize; initialIndex++) { |
+ for (size_t finalIndex = 1; finalIndex < finalSize; finalIndex++) { |
+ if (initial[initialIndex - 1] == final[finalIndex - 1]) { |
+ costMatrix[initialIndex][finalIndex] = |
+ costMatrix[initialIndex - 1][finalIndex - 1]; |
+ } else { |
+ int costAfterSubstitution = |
+ costMatrix[initialIndex - 1][finalIndex - 1] + 1; |
+ int costAfterInsertion = costMatrix[initialIndex][finalIndex - 1] + 1; |
+ int costAfterDeletion = costMatrix[initialIndex - 1][finalIndex] + 1; |
+ costMatrix[initialIndex][finalIndex] = |
+ std::min(std::min(costAfterDeletion, costAfterInsertion), |
+ costAfterSubstitution); |
+ } |
+ } |
+ } |
+ |
+ // Step 2: Backtrack to find the operations (deletion, insertion, |
+ // substitution). |
+ size_t initialIndex = initialSize - 1; |
+ size_t finalIndex = finalSize - 1; |
+ while (initialIndex != 0 || finalIndex != 0) { |
+ BacktrackOperation op = |
+ BacktrackOperationInCostMatrix(costMatrix, finalIndex, initialIndex); |
+ switch (op) { |
+ case SUBSTITUTION: |
+ finalIndex--; |
+ initialIndex--; |
+ // The substitution is relative to |initial|. |
+ substitutions->push_back(initialIndex); |
+ break; |
+ case DELETION: |
+ initialIndex--; |
+ // The deletion is relative to |initial|. |
+ deletions->push_back(initialIndex); |
+ break; |
+ case INSERTION: |
+ finalIndex--; |
+ // The insertion is relative to |final|. |
+ insertions->push_back(finalIndex); |
+ break; |
+ case NOTHING: |
+ finalIndex--; |
+ initialIndex--; |
+ break; |
+ } |
+ } |
+ |
+ // The backtracking results in the indexes of the operations being stored in |
+ // decreasing order. |
+ // For readability, order them in ascending value. |
+ std::reverse(std::begin(*substitutions), std::end(*substitutions)); |
+ std::reverse(std::begin(*deletions), std::end(*deletions)); |
+ std::reverse(std::begin(*insertions), std::end(*insertions)); |
+} |
+ |
+} // namespace ios_internal |