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

Unified Diff: ios/chrome/browser/native_app_launcher/native_app_navigation_controller.mm

Issue 2585233003: Upstream Chrome on iOS source code [2/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/browser/native_app_launcher/native_app_navigation_controller.mm
diff --git a/ios/chrome/browser/native_app_launcher/native_app_navigation_controller.mm b/ios/chrome/browser/native_app_launcher/native_app_navigation_controller.mm
new file mode 100644
index 0000000000000000000000000000000000000000..4515dcc5c0fa477a8dc60d7dd546d39145ec807c
--- /dev/null
+++ b/ios/chrome/browser/native_app_launcher/native_app_navigation_controller.mm
@@ -0,0 +1,247 @@
+// Copyright 2012 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/native_app_launcher/native_app_navigation_controller.h"
+
+#import <StoreKit/StoreKit.h>
+
+#include "base/metrics/user_metrics.h"
+#include "base/metrics/user_metrics_action.h"
+#include "components/infobars/core/infobar_manager.h"
+#import "ios/chrome/browser/installation_notifier.h"
+#include "ios/chrome/browser/native_app_launcher/native_app_infobar_delegate.h"
+#import "ios/chrome/browser/open_url_util.h"
+#import "ios/chrome/browser/tabs/tab.h"
+#include "ios/public/provider/chrome/browser/chrome_browser_provider.h"
+#import "ios/public/provider/chrome/browser/native_app_launcher/native_app_metadata.h"
+#import "ios/public/provider/chrome/browser/native_app_launcher/native_app_types.h"
+#import "ios/public/provider/chrome/browser/native_app_launcher/native_app_whitelist_manager.h"
+#import "ios/web/navigation/navigation_manager_impl.h"
+#include "ios/web/public/navigation_item.h"
+#import "net/base/mac/url_conversions.h"
+#include "net/url_request/url_request_context_getter.h"
+
+using base::UserMetricsAction;
+
+@interface NativeAppNavigationController ()
+// Shows a native app infobar by looking at the page's URL and by checking
+// wheter that infobar should be bypassed or not.
+- (void)showInfoBarIfNecessary;
+
+// Returns a pointer to the NSMutableSet of |_appsPossiblyBeingInstalled|
+- (NSMutableSet*)appsPossiblyBeingInstalled;
+
+// Records what type of infobar was opened.
+- (void)recordInfobarDisplayedOfType:(NativeAppControllerType)type
+ onLinkNavigation:(BOOL)isLinkNavigation;
+
+// Returns whether the current state is Link Navigation in the sense of Native
+// App Launcher, i.e. a navigation caused by an explicit user action in the
+// rectangle of the web content area.
+- (BOOL)isLinkNavigation;
+
+@end
+
+@implementation NativeAppNavigationController {
+ // A reference to the URLRequestContextGetter needed to fetch icons.
+ scoped_refptr<net::URLRequestContextGetter> _requestContextGetter;
+ // Tab hosting the infobar.
+ __unsafe_unretained Tab* _tab; // weak
+ base::scoped_nsprotocol<id<NativeAppMetadata>> _metadata;
+ // A set of appIds encoded as NSStrings.
+ base::scoped_nsobject<NSMutableSet> _appsPossiblyBeingInstalled;
+}
+
+// This prevents incorrect initialization of this object.
+- (id)init {
+ NOTREACHED();
+ return nil;
+}
+
+// Designated initializer. Use this instead of -init.
+- (id)initWithRequestContextGetter:(net::URLRequestContextGetter*)context
+ tab:(Tab*)tab {
+ self = [super init];
+ if (self) {
+ DCHECK(context);
+ _requestContextGetter = context;
+ // Allows |tab| to be nil for unit testing.
+ _tab = tab;
+ _appsPossiblyBeingInstalled.reset([[NSMutableSet alloc] init]);
+ }
+ return self;
+}
+
+- (void)copyStateFrom:(NativeAppNavigationController*)controller {
+ DCHECK(controller);
+ _appsPossiblyBeingInstalled.reset([[NSMutableSet alloc]
+ initWithSet:[controller appsPossiblyBeingInstalled]]);
+ for (NSString* appIdString in _appsPossiblyBeingInstalled.get()) {
+ DCHECK([appIdString isKindOfClass:[NSString class]]);
+ NSURL* appURL =
+ [ios::GetChromeBrowserProvider()->GetNativeAppWhitelistManager()
+ schemeForAppId:appIdString];
+ [[InstallationNotifier sharedInstance]
+ registerForInstallationNotifications:self
+ withSelector:@selector(appDidInstall:)
+ forScheme:[appURL scheme]];
+ }
+ [self showInfoBarIfNecessary];
+}
+
+- (void)dealloc {
+ [[InstallationNotifier sharedInstance] unregisterForNotifications:self];
+ [super dealloc];
+}
+
+- (NSMutableSet*)appsPossiblyBeingInstalled {
+ return _appsPossiblyBeingInstalled;
+}
+
+- (void)showInfoBarIfNecessary {
+ // Find a potential matching native app.
+ GURL pageURL = _tab.webState->GetLastCommittedURL();
+ _metadata.reset(
+ [ios::GetChromeBrowserProvider()->GetNativeAppWhitelistManager()
+ newNativeAppForURL:pageURL]);
+ if (!_metadata || [_metadata shouldBypassInfoBars])
+ return;
+
+ // Select the infobar type.
+ NativeAppControllerType type;
+ if ([_metadata canOpenURL:pageURL]) { // App is installed.
+ type = [self isLinkNavigation] && ![_metadata shouldAutoOpenLinks]
+ ? NATIVE_APP_OPEN_POLICY_CONTROLLER
+ : NATIVE_APP_LAUNCHER_CONTROLLER;
+ } else { // App is not installed.
+ // Check if the user already opened the store for this app.
+ if ([_appsPossiblyBeingInstalled containsObject:[_metadata appId]])
+ return;
+ type = NATIVE_APP_INSTALLER_CONTROLLER;
+ }
+ // Inform the metadata that an infobar of |type| will be shown so that metrics
+ // and ignored behavior can be handled.
+ [_metadata willBeShownInInfobarOfType:type];
+ // Display the proper infobar.
+ infobars::InfoBarManager* infoBarManager = [_tab infoBarManager];
+ NativeAppInfoBarDelegate::Create(infoBarManager, self, pageURL, type);
+ [self recordInfobarDisplayedOfType:type
+ onLinkNavigation:[self isLinkNavigation]];
+}
+
+- (void)recordInfobarDisplayedOfType:(NativeAppControllerType)type
+ onLinkNavigation:(BOOL)isLinkNavigation {
+ switch (type) {
+ case NATIVE_APP_INSTALLER_CONTROLLER:
+ base::RecordAction(
+ isLinkNavigation
+ ? UserMetricsAction("MobileGALInstallInfoBarLinkNavigation")
+ : UserMetricsAction("MobileGALInstallInfoBarDirectNavigation"));
+ break;
+ case NATIVE_APP_LAUNCHER_CONTROLLER:
+ base::RecordAction(UserMetricsAction("MobileGALLaunchInfoBar"));
+ break;
+ case NATIVE_APP_OPEN_POLICY_CONTROLLER:
+ base::RecordAction(UserMetricsAction("MobileGALOpenPolicyInfoBar"));
+ break;
+ default:
+ NOTREACHED();
+ break;
+ }
+}
+
+#pragma mark -
+#pragma mark NativeAppNavigationControllerProtocol methods
+
+- (NSString*)appId {
+ return [_metadata appId];
+}
+
+- (NSString*)appName {
+ return [_metadata appName];
+}
+
+- (void)fetchSmallIconWithCompletionBlock:(void (^)(UIImage*))block {
+ [_metadata fetchSmallIconWithContext:_requestContextGetter.get()
+ completionBlock:block];
+}
+
+- (void)openStore {
+ // Register to get a notification when the app is installed.
+ [[InstallationNotifier sharedInstance]
+ registerForInstallationNotifications:self
+ withSelector:@selector(appDidInstall:)
+ forScheme:[_metadata anyScheme]];
+ NSString* appIdString = [self appId];
+ // Defensively early return if native app metadata returns an nil string for
+ // appId which can cause subsequent -addObject: to throw exceptions.
+ if (![appIdString length])
+ return;
+ DCHECK(![_appsPossiblyBeingInstalled containsObject:appIdString]);
+ [_appsPossiblyBeingInstalled addObject:appIdString];
+ [_tab openAppStore:appIdString];
+}
+
+- (void)launchApp:(const GURL&)URL {
+ // TODO(crbug.com/353957): Pass the ChromeIdentity to
+ // -launchURLWithURL:identity:
+ GURL launchURL([_metadata launchURLWithURL:URL identity:nil]);
+ if (launchURL.is_valid()) {
+ OpenUrlWithCompletionHandler(net::NSURLWithGURL(launchURL), nil);
+ }
+}
+
+- (void)updateMetadataWithUserAction:(NativeAppActionType)userAction {
+ [_metadata updateWithUserAction:userAction];
+}
+
+#pragma mark -
+#pragma mark CRWWebControllerObserver methods
+
+- (void)pageLoaded:(CRWWebController*)webController {
+ if (![_tab isPrerenderTab])
+ [self showInfoBarIfNecessary];
+}
+
+- (void)webControllerWillClose:(CRWWebController*)webController {
+ [webController removeObserver:self];
+}
+
+- (BOOL)isLinkNavigation {
+ if (![_tab navigationManager])
+ return NO;
+ web::NavigationItem* userItem = [_tab navigationManager]->GetLastUserItem();
+ if (!userItem)
+ return NO;
+ ui::PageTransition currentTransition = userItem->GetTransitionType();
+ return PageTransitionCoreTypeIs(currentTransition,
+ ui::PAGE_TRANSITION_LINK) ||
+ PageTransitionCoreTypeIs(currentTransition,
+ ui::PAGE_TRANSITION_AUTO_BOOKMARK);
+}
+
+- (void)appDidInstall:(NSNotification*)notification {
+ [self removeAppFromNotification:notification];
+ [self showInfoBarIfNecessary];
+}
+
+- (void)removeAppFromNotification:(NSNotification*)notification {
+ DCHECK([[notification object] isKindOfClass:[InstallationNotifier class]]);
+ NSString* schemeOfInstalledApp = [notification name];
+ __block NSString* appIDToRemove = nil;
+ [_appsPossiblyBeingInstalled
+ enumerateObjectsUsingBlock:^(id appID, BOOL* stop) {
+ NSURL* appURL =
+ [ios::GetChromeBrowserProvider()->GetNativeAppWhitelistManager()
+ schemeForAppId:appID];
+ if ([[appURL scheme] isEqualToString:schemeOfInstalledApp]) {
+ appIDToRemove = appID;
+ *stop = YES;
+ }
+ }];
+ DCHECK(appIDToRemove);
+ [_appsPossiblyBeingInstalled removeObject:appIDToRemove];
+}
+
+@end

Powered by Google App Engine
This is Rietveld 408576698