blob: 941130690a9568f28a5f3e0db0eaaaf19b05b36c [file] [log] [blame]
// 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 "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"
#import "ios/chrome/browser/ui/uikit_ui_util.h"
#include "ios/chrome/grit/ios_theme_resources.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
namespace {
UIImage* DefaultFaviconImage() {
return NativeImage(IDR_IOS_OMNIBOX_HTTP);
}
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;
}
} // namespace
void TabSwitcherGetFavicon(GURL const& url,
ios::ChromeBrowserState* browser_state,
TabSwitcherFaviconGetterCompletionBlock 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()];
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());
}
void TabSwitcherMinimalReplacementOperations(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));
}