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

Unified Diff: ios/chrome/app/application_delegate/user_activity_handler.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/application_delegate/user_activity_handler.mm
diff --git a/ios/chrome/app/application_delegate/user_activity_handler.mm b/ios/chrome/app/application_delegate/user_activity_handler.mm
new file mode 100644
index 0000000000000000000000000000000000000000..6b3b9e8c9112b7c4154bce1a118aa998ea483384
--- /dev/null
+++ b/ios/chrome/app/application_delegate/user_activity_handler.mm
@@ -0,0 +1,320 @@
+// Copyright 2016 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/application_delegate/user_activity_handler.h"
+
+#import <CoreSpotlight/CoreSpotlight.h>
+#import <UIKit/UIKit.h>
+
+#include "base/ios/block_types.h"
+#include "base/ios/ios_util.h"
+#include "base/ios/weak_nsobject.h"
+#include "base/mac/foundation_util.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/metrics/user_metrics_action.h"
+#include "base/strings/sys_string_conversions.h"
+#include "components/handoff/handoff_utility.h"
+#import "ios/chrome/app/application_delegate/startup_information.h"
+#import "ios/chrome/app/application_delegate/tab_opening.h"
+#include "ios/chrome/app/application_mode.h"
+#import "ios/chrome/app/spotlight/actions_spotlight_manager.h"
+#import "ios/chrome/app/spotlight/spotlight_util.h"
+#include "ios/chrome/browser/app_startup_parameters.h"
+#include "ios/chrome/browser/chrome_url_constants.h"
+#include "ios/chrome/browser/experimental_flags.h"
+#include "ios/chrome/browser/metrics/first_user_action_recorder.h"
+#import "ios/chrome/browser/tabs/tab.h"
+#import "ios/chrome/browser/tabs/tab_model.h"
+#import "ios/chrome/browser/u2f/u2f_controller.h"
+#import "ios/chrome/browser/ui/main/browser_view_information.h"
+#import "net/base/mac/url_conversions.h"
+#include "ui/base/page_transition_types.h"
+#include "url/gurl.h"
+
+using base::UserMetricsAction;
+
+namespace {
+// Constants for 3D touch application static shortcuts.
+NSString* const kShortcutNewTab = @"OpenNewTab";
+NSString* const kShortcutNewIncognitoTab = @"OpenIncognitoTab";
+NSString* const kShortcutVoiceSearch = @"OpenVoiceSearch";
+NSString* const kShortcutQRScanner = @"OpenQRScanner";
+} // namespace
+
+@interface UserActivityHandler ()
+// Handles the 3D touch application static items. Does nothing if in first run.
++ (BOOL)handleShortcutItem:(UIApplicationShortcutItem*)shortcutItem
+ startupInformation:(id<StartupInformation>)startupInformation;
+// Routes Universal 2nd Factor (U2F) callback to the correct Tab.
++ (void)routeU2FURL:(const GURL&)URL
+ browserViewInformation:(id<BrowserViewInformation>)browserViewInformation;
+@end
+
+@implementation UserActivityHandler
+
+#pragma mark - Public methods.
+
++ (BOOL)continueUserActivity:(NSUserActivity*)userActivity
+ applicationIsActive:(BOOL)applicationIsActive
+ tabOpener:(id<TabOpening>)tabOpener
+ startupInformation:(id<StartupInformation>)startupInformation {
+ NSURL* webpageURL = userActivity.webpageURL;
+
+ if ([userActivity.activityType
+ isEqualToString:handoff::kChromeHandoffActivityType]) {
+ // App was launched by iOS as a result of Handoff.
+ NSString* originString = base::mac::ObjCCast<NSString>(
+ userActivity.userInfo[handoff::kOriginKey]);
+ handoff::Origin origin = handoff::OriginFromString(originString);
+ UMA_HISTOGRAM_ENUMERATION("IOS.Handoff.Origin", origin,
+ handoff::ORIGIN_COUNT);
+ } else if ([userActivity.activityType
+ isEqualToString:NSUserActivityTypeBrowsingWeb]) {
+ // App was launched as the result of a Universal Link navigation. The value
+ // of userActivity.webpageURL is not used. The only supported action
+ // at this time is opening a New Tab Page.
+ GURL newTabURL(kChromeUINewTabURL);
+ webpageURL = net::NSURLWithGURL(newTabURL);
+ base::scoped_nsobject<AppStartupParameters> startupParams(
+ [[AppStartupParameters alloc] initWithExternalURL:newTabURL]);
+ [startupInformation setStartupParameters:startupParams];
+ base::RecordAction(base::UserMetricsAction("IOSLaunchedByUniversalLink"));
+ } else if (spotlight::IsSpotlightAvailable() &&
+ [userActivity.activityType
+ isEqualToString:CSSearchableItemActionType]) {
+ // App was launched by iOS as the result of a tap on a Spotlight Search
+ // result.
+ NSString* itemID =
+ [userActivity.userInfo objectForKey:CSSearchableItemActivityIdentifier];
+ spotlight::Domain domain = spotlight::SpotlightDomainFromString(itemID);
+ if (domain == spotlight::DOMAIN_ACTIONS &&
+ !experimental_flags::IsSpotlightActionsEnabled()) {
+ return NO;
+ }
+ UMA_HISTOGRAM_ENUMERATION("IOS.Spotlight.Origin", domain,
+ spotlight::DOMAIN_COUNT);
+
+ if (!itemID) {
+ return NO;
+ }
+ if (domain == spotlight::DOMAIN_ACTIONS) {
+ webpageURL =
+ [NSURL URLWithString:base::SysUTF8ToNSString(kChromeUINewTabURL)];
+ base::scoped_nsobject<AppStartupParameters> startupParams(
+ [[AppStartupParameters alloc]
+ initWithExternalURL:GURL(kChromeUINewTabURL)]);
+ BOOL startupParamsSet = spotlight::SetStartupParametersForSpotlightAction(
+ itemID, startupParams);
+ if (!startupParamsSet) {
+ return NO;
+ }
+ [startupInformation setStartupParameters:startupParams];
+ } else if (!webpageURL && base::ios::IsRunningOnIOS10OrLater()) {
+ // spotlight::GetURLForSpotlightItemID uses CSSearchQuery, which is only
+ // supported from iOS 10.
+ spotlight::GetURLForSpotlightItemID(itemID, ^(NSURL* contentURL) {
+ if (!contentURL) {
+ return;
+ }
+ dispatch_async(dispatch_get_main_queue(), ^{
+ // Update the isActive flag as it may have changed during the async
+ // calls.
+ BOOL isActive = [[UIApplication sharedApplication]
+ applicationState] == UIApplicationStateActive;
+ [self continueUserActivityURL:contentURL
+ applicationIsActive:isActive
+ tabOpener:tabOpener
+ startupInformation:startupInformation];
+ });
+ });
+ return YES;
+ }
+ } else {
+ // Do nothing for unknown activity type.
+ return NO;
+ }
+
+ return [self continueUserActivityURL:webpageURL
+ applicationIsActive:applicationIsActive
+ tabOpener:tabOpener
+ startupInformation:startupInformation];
+}
+
++ (BOOL)continueUserActivityURL:(NSURL*)webpageURL
+ applicationIsActive:(BOOL)applicationIsActive
+ tabOpener:(id<TabOpening>)tabOpener
+ startupInformation:(id<StartupInformation>)startupInformation {
+ if (!webpageURL)
+ return NO;
+
+ GURL webpageGURL(net::GURLWithNSURL(webpageURL));
+ if (!webpageGURL.is_valid())
+ return NO;
+
+ if (applicationIsActive && ![startupInformation isPresentingFirstRunUI]) {
+ // The app is already active so the applicationDidBecomeActive: method will
+ // never be called. Open the requested URL immediately.
+ ApplicationMode targetMode =
+ [[startupInformation startupParameters] launchInIncognito]
+ ? ApplicationMode::INCOGNITO
+ : ApplicationMode::NORMAL;
+ [tabOpener dismissModalsAndOpenSelectedTabInMode:targetMode
+ withURL:webpageGURL
+ transition:ui::PAGE_TRANSITION_LINK
+ completion:^{
+ [startupInformation
+ setStartupParameters:nil];
+ }];
+ return YES;
+ }
+
+ // Don't record the first action as a user action, since it will not be
+ // initiated by the user.
+ [startupInformation resetFirstUserActionRecorder];
+
+ if (![startupInformation startupParameters]) {
+ base::scoped_nsobject<AppStartupParameters> startupParams(
+ [[AppStartupParameters alloc] initWithExternalURL:webpageGURL]);
+ [startupInformation setStartupParameters:startupParams];
+ }
+ return YES;
+}
+
++ (void)performActionForShortcutItem:(UIApplicationShortcutItem*)shortcutItem
+ completionHandler:(void (^)(BOOL succeeded))completionHandler
+ tabOpener:(id<TabOpening>)tabOpener
+ startupInformation:(id<StartupInformation>)startupInformation
+ browserViewInformation:
+ (id<BrowserViewInformation>)browserViewInformation {
+ BOOL handledShortcutItem =
+ [UserActivityHandler handleShortcutItem:shortcutItem
+ startupInformation:startupInformation];
+ if (handledShortcutItem) {
+ [UserActivityHandler
+ handleStartupParametersWithTabOpener:tabOpener
+ startupInformation:startupInformation
+ browserViewInformation:browserViewInformation];
+ }
+ completionHandler(handledShortcutItem);
+}
+
++ (BOOL)willContinueUserActivityWithType:(NSString*)userActivityType {
+ return
+ [userActivityType isEqualToString:handoff::kChromeHandoffActivityType] ||
+ (spotlight::IsSpotlightAvailable() &&
+ [userActivityType isEqualToString:CSSearchableItemActionType]);
+}
+
++ (void)handleStartupParametersWithTabOpener:(id<TabOpening>)tabOpener
+ startupInformation:
+ (id<StartupInformation>)startupInformation
+ browserViewInformation:
+ (id<BrowserViewInformation>)browserViewInformation {
+ DCHECK([startupInformation startupParameters]);
+ // Do not load the external URL if the user has not accepted the terms of
+ // service. This corresponds to the case when the user installed Chrome,
+ // has never launched it and attempts to open an external URL in Chrome.
+ if ([startupInformation isPresentingFirstRunUI])
+ return;
+
+ // Check if it's an U2F call. If so, route it to correct tab.
+ // If not, open or reuse tab in main BVC.
+ if ([U2FController
+ isU2FURL:[[startupInformation startupParameters] externalURL]]) {
+ [UserActivityHandler routeU2FURL:[[startupInformation startupParameters]
+ externalURL]
+ browserViewInformation:browserViewInformation];
+ // It's OK to clear startup parameters here because routeU2FURL works
+ // synchronously.
+ [startupInformation setStartupParameters:nil];
+ } else {
+ // The app is already active so the applicationDidBecomeActive: method
+ // will never be called. Open the requested URL after all modal UIs have
+ // been dismissed. |_startupParameters| must be retained until all deferred
+ // modal UIs are dismissed and tab opened with requested URL.
+ ApplicationMode targetMode =
+ [[startupInformation startupParameters] launchInIncognito]
+ ? ApplicationMode::INCOGNITO
+ : ApplicationMode::NORMAL;
+ [tabOpener dismissModalsAndOpenSelectedTabInMode:targetMode
+ withURL:[[startupInformation
+ startupParameters]
+ externalURL]
+ transition:ui::PAGE_TRANSITION_LINK
+ completion:^{
+ [startupInformation
+ setStartupParameters:nil];
+ }];
+ }
+}
+
+#pragma mark - Internal methods.
+
++ (BOOL)handleShortcutItem:(UIApplicationShortcutItem*)shortcutItem
+ startupInformation:(id<StartupInformation>)startupInformation {
+ if ([startupInformation isPresentingFirstRunUI])
+ return NO;
+
+ base::scoped_nsobject<AppStartupParameters> startupParams(
+ [[AppStartupParameters alloc]
+ initWithExternalURL:GURL(kChromeUINewTabURL)]);
+
+ if ([shortcutItem.type isEqualToString:kShortcutNewTab]) {
+ base::RecordAction(UserMetricsAction("ApplicationShortcut.NewTabPressed"));
+ [startupInformation setStartupParameters:startupParams];
+ return YES;
+
+ } else if ([shortcutItem.type isEqualToString:kShortcutNewIncognitoTab]) {
+ base::RecordAction(
+ UserMetricsAction("ApplicationShortcut.NewIncognitoTabPressed"));
+ [startupParams setLaunchInIncognito:YES];
+ [startupInformation setStartupParameters:startupParams];
+ return YES;
+
+ } else if ([shortcutItem.type isEqualToString:kShortcutVoiceSearch]) {
+ base::RecordAction(
+ UserMetricsAction("ApplicationShortcut.VoiceSearchPressed"));
+ [startupParams setLaunchVoiceSearch:YES];
+ [startupInformation setStartupParameters:startupParams];
+ return YES;
+
+ } else if ([shortcutItem.type isEqualToString:kShortcutQRScanner]) {
+ if (experimental_flags::IsQRCodeReaderEnabled()) {
+ base::RecordAction(
+ UserMetricsAction("ApplicationShortcut.ScanQRCodePressed"));
+ [startupParams setLaunchQRScanner:YES];
+ }
+ [startupInformation setStartupParameters:startupParams];
+ return YES;
+ }
+
+ NOTREACHED();
+ return NO;
+}
+
++ (void)routeU2FURL:(const GURL&)URL
+ browserViewInformation:(id<BrowserViewInformation>)browserViewInformation {
+ // Retrieve the designated TabID from U2F URL.
+ NSString* tabID = [U2FController tabIDFromResponseURL:URL];
+ if (!tabID) {
+ return;
+ }
+
+ // TODO(crbug.com/619598): move this code to BrowserViewInformation to hide
+ // implementation details of TabModel.
+ // Iterate through mainTabModel and OTRTabModel to find the corresponding tab.
+ NSArray* tabModels = @[
+ [browserViewInformation mainTabModel], [browserViewInformation otrTabModel]
+ ];
+ for (TabModel* tabModel in tabModels) {
+ for (Tab* tab in tabModel) {
+ if ([tab.tabId isEqualToString:tabID]) {
+ [tab evaluateU2FResultFromURL:URL];
+ return;
+ }
+ }
+ }
+}
+
+@end

Powered by Google App Engine
This is Rietveld 408576698