OLD | NEW |
(Empty) | |
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #import "ios/chrome/browser/ui/tab_switcher/tab_switcher_utils.h" |
| 6 |
| 7 #import <UIKit/UIKit.h> |
| 8 |
| 9 #include "base/logging.h" |
| 10 #include "base/mac/scoped_nsobject.h" |
| 11 #include "components/browser_sync/profile_sync_service.h" |
| 12 #include "components/sync/driver/sync_service.h" |
| 13 #include "components/sync_sessions/open_tabs_ui_delegate.h" |
| 14 #include "ios/chrome/browser/browser_state/chrome_browser_state.h" |
| 15 #import "ios/chrome/browser/favicon/favicon_loader.h" |
| 16 #include "ios/chrome/browser/favicon/ios_chrome_favicon_loader_factory.h" |
| 17 #include "ios/chrome/browser/sync/ios_chrome_profile_sync_service_factory.h" |
| 18 #include "ios/chrome/grit/ios_theme_resources.h" |
| 19 #include "ui/base/resource/resource_bundle.h" |
| 20 |
| 21 namespace ios_internal { |
| 22 |
| 23 UIImage* DefaultFaviconImage() { |
| 24 ResourceBundle& rb = ResourceBundle::GetSharedInstance(); |
| 25 return rb.GetNativeImageNamed(IDR_IOS_OMNIBOX_HTTP).ToUIImage(); |
| 26 } |
| 27 |
| 28 void GetFavicon(GURL const& url, |
| 29 ios::ChromeBrowserState* browser_state, |
| 30 ios_internal::FaviconGetterCompletionBlock block) { |
| 31 DCHECK(browser_state); |
| 32 syncer::SyncService* sync_service = |
| 33 IOSChromeProfileSyncServiceFactory::GetForBrowserState(browser_state); |
| 34 sync_sessions::OpenTabsUIDelegate* open_tabs = |
| 35 sync_service ? sync_service->GetOpenTabsUIDelegate() : NULL; |
| 36 scoped_refptr<base::RefCountedMemory> favicon; |
| 37 if (open_tabs && |
| 38 open_tabs->GetSyncedFaviconForPageURL(url.spec(), &favicon)) { |
| 39 dispatch_queue_t queue = |
| 40 dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0); |
| 41 dispatch_async(queue, ^{ |
| 42 NSData* pngData = |
| 43 [NSData dataWithBytes:favicon->front() length:favicon->size()]; |
| 44 base::scoped_nsobject<UIImage> image( |
| 45 [[UIImage alloc] initWithData:pngData]); |
| 46 dispatch_async(dispatch_get_main_queue(), ^{ |
| 47 // |UIImage initWithData:| may return nil. |
| 48 if (image) { |
| 49 block(image); |
| 50 } else { |
| 51 block(DefaultFaviconImage()); |
| 52 } |
| 53 }); |
| 54 }); |
| 55 block(DefaultFaviconImage()); |
| 56 return; |
| 57 } |
| 58 |
| 59 // Use the FaviconCache if there is no synced favicon. |
| 60 FaviconLoader* loader = |
| 61 IOSChromeFaviconLoaderFactory::GetForBrowserState(browser_state); |
| 62 if (loader) { |
| 63 int types = favicon_base::FAVICON | favicon_base::TOUCH_ICON | |
| 64 favicon_base::TOUCH_PRECOMPOSED_ICON; |
| 65 UIImage* image = loader->ImageForURL(url, types, block); |
| 66 DCHECK(image); |
| 67 block(image); |
| 68 return; |
| 69 } |
| 70 // Finally returns a default image. |
| 71 block(DefaultFaviconImage()); |
| 72 } |
| 73 |
| 74 enum BacktrackOperation { NOTHING, SUBSTITUTION, DELETION, INSERTION }; |
| 75 |
| 76 BacktrackOperation BacktrackOperationInCostMatrix( |
| 77 std::vector<std::vector<int>> const& costMatrix, |
| 78 size_t finalIndex, |
| 79 size_t initialIndex) { |
| 80 DCHECK(finalIndex || initialIndex); |
| 81 DCHECK(initialIndex < costMatrix.size()); |
| 82 DCHECK(finalIndex < costMatrix[initialIndex].size()); |
| 83 |
| 84 if (finalIndex == 0) |
| 85 return DELETION; |
| 86 if (initialIndex == 0) |
| 87 return INSERTION; |
| 88 |
| 89 int currentCost = costMatrix[initialIndex][finalIndex]; |
| 90 |
| 91 int costBeforeInsertion = costMatrix[initialIndex][finalIndex - 1]; |
| 92 if (costBeforeInsertion + 1 == currentCost) |
| 93 return INSERTION; |
| 94 |
| 95 int costBeforeDeletion = costMatrix[initialIndex - 1][finalIndex]; |
| 96 if (costBeforeDeletion + 1 == currentCost) |
| 97 return DELETION; |
| 98 |
| 99 int costBeforeSubstitution = costMatrix[initialIndex - 1][finalIndex - 1]; |
| 100 if (costBeforeSubstitution == currentCost) |
| 101 return NOTHING; |
| 102 |
| 103 return SUBSTITUTION; |
| 104 } |
| 105 |
| 106 void MinimalReplacementOperations(std::vector<size_t> const& initial, |
| 107 std::vector<size_t> const& final, |
| 108 std::vector<size_t>* substitutions, |
| 109 std::vector<size_t>* deletions, |
| 110 std::vector<size_t>* insertions) { |
| 111 DCHECK(substitutions); |
| 112 DCHECK(deletions); |
| 113 DCHECK(insertions); |
| 114 DCHECK_EQ(substitutions->size(), 0UL); |
| 115 DCHECK_EQ(deletions->size(), 0UL); |
| 116 DCHECK_EQ(insertions->size(), 0UL); |
| 117 |
| 118 // The substitutions/deletions/insertions are computed using the Levenshtein |
| 119 // algorithm. |
| 120 // https://en.wikipedia.org/wiki/Levenshtein_distance |
| 121 |
| 122 const size_t initialSize = initial.size() + 1; |
| 123 const size_t finalSize = final.size() + 1; |
| 124 |
| 125 std::vector<std::vector<int>> costMatrix(initialSize, |
| 126 std::vector<int>(finalSize)); |
| 127 |
| 128 for (size_t i = 1; i < initialSize; i++) |
| 129 costMatrix[i][0] = i; |
| 130 for (size_t i = 1; i < finalSize; i++) |
| 131 costMatrix[0][i] = i; |
| 132 |
| 133 // Step 1: Generate cost matrix. |
| 134 for (size_t initialIndex = 1; initialIndex < initialSize; initialIndex++) { |
| 135 for (size_t finalIndex = 1; finalIndex < finalSize; finalIndex++) { |
| 136 if (initial[initialIndex - 1] == final[finalIndex - 1]) { |
| 137 costMatrix[initialIndex][finalIndex] = |
| 138 costMatrix[initialIndex - 1][finalIndex - 1]; |
| 139 } else { |
| 140 int costAfterSubstitution = |
| 141 costMatrix[initialIndex - 1][finalIndex - 1] + 1; |
| 142 int costAfterInsertion = costMatrix[initialIndex][finalIndex - 1] + 1; |
| 143 int costAfterDeletion = costMatrix[initialIndex - 1][finalIndex] + 1; |
| 144 costMatrix[initialIndex][finalIndex] = |
| 145 std::min(std::min(costAfterDeletion, costAfterInsertion), |
| 146 costAfterSubstitution); |
| 147 } |
| 148 } |
| 149 } |
| 150 |
| 151 // Step 2: Backtrack to find the operations (deletion, insertion, |
| 152 // substitution). |
| 153 size_t initialIndex = initialSize - 1; |
| 154 size_t finalIndex = finalSize - 1; |
| 155 while (initialIndex != 0 || finalIndex != 0) { |
| 156 BacktrackOperation op = |
| 157 BacktrackOperationInCostMatrix(costMatrix, finalIndex, initialIndex); |
| 158 switch (op) { |
| 159 case SUBSTITUTION: |
| 160 finalIndex--; |
| 161 initialIndex--; |
| 162 // The substitution is relative to |initial|. |
| 163 substitutions->push_back(initialIndex); |
| 164 break; |
| 165 case DELETION: |
| 166 initialIndex--; |
| 167 // The deletion is relative to |initial|. |
| 168 deletions->push_back(initialIndex); |
| 169 break; |
| 170 case INSERTION: |
| 171 finalIndex--; |
| 172 // The insertion is relative to |final|. |
| 173 insertions->push_back(finalIndex); |
| 174 break; |
| 175 case NOTHING: |
| 176 finalIndex--; |
| 177 initialIndex--; |
| 178 break; |
| 179 } |
| 180 } |
| 181 |
| 182 // The backtracking results in the indexes of the operations being stored in |
| 183 // decreasing order. |
| 184 // For readability, order them in ascending value. |
| 185 std::reverse(std::begin(*substitutions), std::end(*substitutions)); |
| 186 std::reverse(std::begin(*deletions), std::end(*deletions)); |
| 187 std::reverse(std::begin(*insertions), std::end(*insertions)); |
| 188 } |
| 189 |
| 190 } // namespace ios_internal |
OLD | NEW |