Commit 1aa19e2d authored by Kurt Horimoto's avatar Kurt Horimoto Committed by Commit Bot

[iOS] Create AppLauncherBrowserAgent.

Now that the kNonModalDialogs feature is enabled by default, the
legacy coordinator-based implementation can be removed.  This CL
moves this functionality into a BrowserAgent that adds OverlayRequests
for app launcher dialogs to the appropriate WebState's queue when an
AppLauncherTabHelper attempts to open an external app.

Since this functionality no longer requires a coordinator, the tab
helper can now be constructed alongside the rest in AttachTabHelpers().

The AppLauncherTabHelperDelegate protocol was converted into a C++
interface to reduce plumbing when hooking the BrowserAgent up to the
tab helpers.  Additionally, LaunchAppForTabHelper() was updated to
return void, as its return value was unused by the tab helper.

Bug: 1068418
Change-Id: I01703acc19712b32810d73876d7e58e364711d14
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2140714
Commit-Queue: Kurt Horimoto <kkhorimoto@chromium.org>
Reviewed-by: default avatarMark Cogan <marq@chromium.org>
Cr-Commit-Position: refs/heads/master@{#757635}
parent bf78ba41
......@@ -9,9 +9,13 @@ source_set("app_launcher") {
sources = [
"app_launcher_abuse_detector.h",
"app_launcher_abuse_detector.mm",
"app_launcher_browser_agent.h",
"app_launcher_browser_agent.mm",
"app_launcher_tab_helper.h",
"app_launcher_tab_helper.mm",
"app_launcher_tab_helper_delegate.h",
"app_launcher_util.h",
"app_launcher_util.mm",
"app_launching_state.h",
"app_launching_state.mm",
]
......@@ -20,38 +24,62 @@ source_set("app_launcher") {
"//components/reading_list/core:core",
"//ios/chrome/browser",
"//ios/chrome/browser/browser_state",
"//ios/chrome/browser/main:public",
"//ios/chrome/browser/overlays",
"//ios/chrome/browser/overlays/public/web_content_area",
"//ios/chrome/browser/reading_list",
"//ios/chrome/browser/u2f",
"//ios/chrome/browser/ui/dialogs:feature_flags",
"//ios/chrome/browser/web_state_list",
"//ios/public/provider/chrome/browser",
"//ios/public/provider/chrome/browser/mailto",
"//ios/web/common",
"//ios/web/public",
"//url",
]
}
source_set("test_support") {
configs += [ "//build/config/compiler:enable_arc" ]
testonly = true
sources = [
"fake_app_launcher_abuse_detector.h",
"fake_app_launcher_abuse_detector.mm",
]
deps = [
":app_launcher",
"//base",
]
}
source_set("unit_tests") {
configs += [ "//build/config/compiler:enable_arc" ]
testonly = true
sources = [
"app_launcher_abuse_detector_unittest.mm",
"app_launcher_browser_agent_unittest.mm",
"app_launcher_tab_helper_unittest.mm",
"app_launching_state_unittest.mm",
]
deps = [
":app_launcher",
":test_support",
"//base",
"//base/test:test_support",
"//components/reading_list/core:core",
"//ios/chrome/browser",
"//ios/chrome/browser/browser_state:test_support",
"//ios/chrome/browser/main:test_support",
"//ios/chrome/browser/overlays",
"//ios/chrome/browser/overlays/public/web_content_area",
"//ios/chrome/browser/reading_list",
"//ios/chrome/browser/u2f",
"//ios/chrome/browser/web:tab_id_tab_helper",
"//ios/chrome/browser/web:web_internal",
"//ios/chrome/browser/web_state_list",
"//ios/web/public/test/fakes",
"//testing/gtest",
"//third_party/ocmock",
"//url",
]
}
// Copyright 2020 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.
#ifndef IOS_CHROME_BROWSER_APP_LAUNCHER_APP_LAUNCHER_BROWSER_AGENT_H_
#define IOS_CHROME_BROWSER_APP_LAUNCHER_APP_LAUNCHER_BROWSER_AGENT_H_
#include "base/scoped_observer.h"
#import "ios/chrome/browser/app_launcher/app_launcher_tab_helper_delegate.h"
#import "ios/chrome/browser/main/browser_observer.h"
#import "ios/chrome/browser/main/browser_user_data.h"
#import "ios/chrome/browser/web_state_list/web_state_list_observer.h"
class OverlayRequestQueue;
// A browser agent that manages opening external apps for navigations that occur
// within one of the Browser's WebStates.
class AppLauncherBrowserAgent
: public BrowserUserData<AppLauncherBrowserAgent> {
public:
~AppLauncherBrowserAgent() override;
private:
explicit AppLauncherBrowserAgent(Browser* browser);
friend class BrowserUserData<AppLauncherBrowserAgent>;
// Helper object that handles delegated AppLauncherTabHelper functionality.
class TabHelperDelegate : public AppLauncherTabHelperDelegate {
public:
explicit TabHelperDelegate(WebStateList* web_state_list);
~TabHelperDelegate() override;
private:
// AppLauncherTabHelperDelegate:
void LaunchAppForTabHelper(AppLauncherTabHelper* tab_helper,
const GURL& url,
bool link_transition) override;
void ShowRepeatedAppLaunchAlert(
AppLauncherTabHelper* tab_helper,
base::OnceCallback<void(bool)> completion) override;
// Returns the OverlayRequestQueue to use for app launch dialogs from
// |web_state|. Returns the queue for |web_state|'s opener if |web_state|
// is expected to be closed before the app launcher dialog can be shown.
OverlayRequestQueue* GetQueueForAppLaunchDialog(web::WebState* web_state);
// The Browser's WebStateList. Used to fetch the appropriate request queue
// for app launcher dialogs.
WebStateList* web_state_list_ = nullptr;
};
// Helper object that sets up the delegates for all AppLauncherTabHelpers in
// the Browser's WebStateList.
class TabHelperDelegateInstaller : public WebStateListObserver {
public:
TabHelperDelegateInstaller(AppLauncherTabHelperDelegate* delegate,
WebStateList* web_state_list);
~TabHelperDelegateInstaller() override;
private:
// WebStateListObserver:
void WebStateInsertedAt(WebStateList* web_state_list,
web::WebState* web_state,
int index,
bool activating) override;
void WebStateReplacedAt(WebStateList* web_state_list,
web::WebState* old_web_state,
web::WebState* new_web_state,
int index) override;
void WillDetachWebStateAt(WebStateList* web_state_list,
web::WebState* web_state,
int index) override;
// The delegate that is installed for each WebState in the WebStateList.
AppLauncherTabHelperDelegate* delegate_ = nullptr;
};
// Helper object that unhooks the delegate installer when the Browser is
// destroyed.
class BrowserShutdownHelper : public BrowserObserver {
public:
BrowserShutdownHelper(Browser* browser,
WebStateListObserver* web_state_list_observer);
~BrowserShutdownHelper() override;
private:
// BrowserObserver:
void BrowserDestroyed(Browser* browser) override;
// The WebStateListObserver to detach upon destruction.
WebStateListObserver* web_state_list_observer_ = nullptr;
// Scoped observer for the Browser.
ScopedObserver<Browser, BrowserObserver> scoped_observer_{this};
};
// Handler for app launches in the Browser.
TabHelperDelegate tab_helper_delegate_;
// Installer for tab helper delegates.
TabHelperDelegateInstaller tab_helper_delegate_installer_;
// Helper object for cleaning up the BrowserAgent when the Browser is
// destroyed.
BrowserShutdownHelper shutdown_helper_;
// BrowserUserData key.
BROWSER_USER_DATA_KEY_DECL();
};
#endif // IOS_CHROME_BROWSER_APP_LAUNCHER_APP_LAUNCHER_BROWSER_AGENT_H_
// Copyright 2020 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/app_launcher/app_launcher_browser_agent.h"
#include "base/bind.h"
#include "base/logging.h"
#include "base/metrics/histogram_macros.h"
#import "ios/chrome/browser/app_launcher/app_launcher_tab_helper.h"
#import "ios/chrome/browser/app_launcher/app_launcher_util.h"
#import "ios/chrome/browser/main/browser.h"
#include "ios/chrome/browser/overlays/public/overlay_callback_manager.h"
#import "ios/chrome/browser/overlays/public/overlay_request.h"
#import "ios/chrome/browser/overlays/public/overlay_request_queue.h"
#import "ios/chrome/browser/overlays/public/overlay_response.h"
#import "ios/chrome/browser/overlays/public/web_content_area/app_launcher_alert_overlay.h"
#import "ios/chrome/browser/web_state_list/web_state_list.h"
#import "ios/chrome/browser/web_state_list/web_state_opener.h"
#include "ios/public/provider/chrome/browser/chrome_browser_provider.h"
#include "ios/public/provider/chrome/browser/mailto/mailto_handler_provider.h"
#import "ios/web/public/navigation/navigation_manager.h"
#import "ios/web/public/web_state.h"
#import "net/base/mac/url_conversions.h"
#include "url/gurl.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
BROWSER_USER_DATA_KEY_IMPL(AppLauncherBrowserAgent)
namespace {
// Records histogram metric on the user's response when prompted to open another
// application. |user_accepted| should be YES if the user accepted the prompt to
// launch another application. This call is extracted to a separate function to
// reduce macro code expansion.
void RecordUserAcceptedAppLaunchMetric(BOOL user_accepted) {
UMA_HISTOGRAM_BOOLEAN("Tab.ExternalApplicationOpened", user_accepted);
}
// Callback for the app launcher alert overlay.
void AppLauncherOverlayCallback(base::OnceCallback<void(bool)> completion,
bool repeated_request,
OverlayResponse* response) {
// Extract the user decision from |response|.
bool user_accepted = false;
if (response) {
AppLauncherAlertOverlayResponseInfo* info =
response->GetInfo<AppLauncherAlertOverlayResponseInfo>();
user_accepted = info && info->allow_navigation();
}
// Record the UMA for repeated requests.
if (repeated_request)
RecordUserAcceptedAppLaunchMetric(user_accepted);
// Execute the completion with the response.
DCHECK(!completion.is_null());
std::move(completion).Run(user_accepted);
}
// Launches the app for |url| if |user_accepted| is true.
void LaunchExternalApp(const GURL url, bool user_accepted = true) {
if (!user_accepted)
return;
[[UIApplication sharedApplication] openURL:net::NSURLWithGURL(url)
options:@{}
completionHandler:nil];
}
} // namespace
#pragma mark - AppLauncherBrowserAgent
AppLauncherBrowserAgent::AppLauncherBrowserAgent(Browser* browser)
: tab_helper_delegate_(browser->GetWebStateList()),
tab_helper_delegate_installer_(&tab_helper_delegate_,
browser->GetWebStateList()),
shutdown_helper_(browser, &tab_helper_delegate_installer_) {}
AppLauncherBrowserAgent::~AppLauncherBrowserAgent() = default;
#pragma mark - AppLauncherBrowserAgent::TabHelperDelegate
AppLauncherBrowserAgent::TabHelperDelegate::TabHelperDelegate(
WebStateList* web_state_list)
: web_state_list_(web_state_list) {
DCHECK(web_state_list_);
}
AppLauncherBrowserAgent::TabHelperDelegate::~TabHelperDelegate() = default;
#pragma mark AppLauncherTabHelperDelegate
void AppLauncherBrowserAgent::TabHelperDelegate::LaunchAppForTabHelper(
AppLauncherTabHelper* tab_helper,
const GURL& url,
bool link_transition) {
// Don't open application if chrome is not active.
if ([[UIApplication sharedApplication] applicationState] !=
UIApplicationStateActive) {
return;
}
// Uses a Mailto Handler to open the appropriate app.
if (url.SchemeIs(url::kMailToScheme)) {
MailtoHandlerProvider* provider =
ios::GetChromeBrowserProvider()->GetMailtoHandlerProvider();
provider->HandleMailtoURL(net::NSURLWithGURL(url));
return;
}
// Show the a dialog for app store launches and external URL navigations that
// did not originate from a link tap.
bool show_dialog = UrlHasAppStoreScheme(url) || !link_transition;
if (show_dialog) {
std::unique_ptr<OverlayRequest> request =
OverlayRequest::CreateWithConfig<AppLauncherAlertOverlayRequestConfig>(
/*is_repeated_request=*/false);
request->GetCallbackManager()->AddCompletionCallback(base::BindOnce(
&AppLauncherOverlayCallback, base::BindOnce(&LaunchExternalApp, url),
/*repeated_request=*/false));
GetQueueForAppLaunchDialog(tab_helper->web_state())
->AddRequest(std::move(request));
} else {
LaunchExternalApp(url);
}
}
void AppLauncherBrowserAgent::TabHelperDelegate::ShowRepeatedAppLaunchAlert(
AppLauncherTabHelper* tab_helper,
base::OnceCallback<void(bool)> completion) {
std::unique_ptr<OverlayRequest> request =
OverlayRequest::CreateWithConfig<AppLauncherAlertOverlayRequestConfig>(
/*is_repeated_request=*/true);
request->GetCallbackManager()->AddCompletionCallback(
base::BindOnce(&AppLauncherOverlayCallback, std::move(completion),
/*is_repeated_request=*/true));
GetQueueForAppLaunchDialog(tab_helper->web_state())
->AddRequest(std::move(request));
}
#pragma mark Private
OverlayRequestQueue*
AppLauncherBrowserAgent::TabHelperDelegate::GetQueueForAppLaunchDialog(
web::WebState* web_state) {
web::WebState* queue_web_state = web_state;
// If an app launch navigation is occurring in a new tab, the tab will be
// closed immediately after the navigation fails, cancelling the app launcher
// dialog before it gets a chance to be shown. When this occurs, use the
// OverlayRequestQueue for the tab's opener instead.
if (!web_state->GetNavigationManager()->GetItemCount() &&
web_state->HasOpener()) {
int index = web_state_list_->GetIndexOfWebState(web_state);
queue_web_state =
web_state_list_->GetOpenerOfWebStateAt(index).opener ?: queue_web_state;
}
return OverlayRequestQueue::FromWebState(queue_web_state,
OverlayModality::kWebContentArea);
}
#pragma mark - AppLauncherBrowserAgent::TabHelperDelegateInstaller
AppLauncherBrowserAgent::TabHelperDelegateInstaller::TabHelperDelegateInstaller(
AppLauncherTabHelperDelegate* delegate,
WebStateList* web_state_list)
: delegate_(delegate) {
DCHECK(delegate_);
DCHECK(web_state_list);
for (int i = 0; i < web_state_list->count(); ++i) {
AppLauncherTabHelper::FromWebState(web_state_list->GetWebStateAt(i))
->SetDelegate(delegate_);
}
}
AppLauncherBrowserAgent::TabHelperDelegateInstaller::
~TabHelperDelegateInstaller() = default;
#pragma mark WebStateListObserver
void AppLauncherBrowserAgent::TabHelperDelegateInstaller::WebStateInsertedAt(
WebStateList* web_state_list,
web::WebState* web_state,
int index,
bool activating) {
AppLauncherTabHelper::FromWebState(web_state)->SetDelegate(delegate_);
}
void AppLauncherBrowserAgent::TabHelperDelegateInstaller::WebStateReplacedAt(
WebStateList* web_state_list,
web::WebState* old_web_state,
web::WebState* new_web_state,
int index) {
AppLauncherTabHelper::FromWebState(old_web_state)->SetDelegate(nullptr);
AppLauncherTabHelper::FromWebState(new_web_state)->SetDelegate(delegate_);
}
void AppLauncherBrowserAgent::TabHelperDelegateInstaller::WillDetachWebStateAt(
WebStateList* web_state_list,
web::WebState* web_state,
int index) {
AppLauncherTabHelper::FromWebState(web_state)->SetDelegate(nullptr);
}
#pragma mark - AppLauncherBrowserAgent::BrowserShutdownHelper
AppLauncherBrowserAgent::BrowserShutdownHelper::BrowserShutdownHelper(
Browser* browser,
WebStateListObserver* web_state_list_observer)
: web_state_list_observer_(web_state_list_observer) {
DCHECK(browser);
DCHECK(web_state_list_observer_);
scoped_observer_.Add(browser);
browser->GetWebStateList()->AddObserver(web_state_list_observer_);
}
AppLauncherBrowserAgent::BrowserShutdownHelper::~BrowserShutdownHelper() {
// The WebStateListObserver must be detached before destruction.
DCHECK(!web_state_list_observer_);
}
#pragma mark BrowserObserver
void AppLauncherBrowserAgent::BrowserShutdownHelper::BrowserDestroyed(
Browser* browser) {
scoped_observer_.Remove(browser);
browser->GetWebStateList()->RemoveObserver(web_state_list_observer_);
web_state_list_observer_ = nullptr;
}
......@@ -6,10 +6,11 @@
#define IOS_CHROME_BROWSER_APP_LAUNCHER_APP_LAUNCHER_TAB_HELPER_H_
#include "base/macros.h"
#import "ios/chrome/browser/app_launcher/app_launcher_abuse_detector.h"
#import "ios/web/public/navigation/web_state_policy_decider.h"
#import "ios/web/public/web_state_user_data.h"
@protocol AppLauncherTabHelperDelegate;
class AppLauncherTabHelperDelegate;
@class AppLauncherAbuseDetector;
class GURL;
......@@ -20,28 +21,27 @@ class AppLauncherTabHelper
public:
~AppLauncherTabHelper() override;
// Creates AppLauncherTabHelper and attaches to |web_state|. |web_state| must
// not be null. |policy_decider| provides policy for launching apps.
// |delegate| can launch applications and present UI and is not retained by
// TabHelper.
// Creates a tab helper for |web_state| that uses |abuse_detector| to make
// navigation policy decisions.
static void CreateForWebState(web::WebState* web_state,
AppLauncherAbuseDetector* abuse_detector,
id<AppLauncherTabHelperDelegate> delegate);
AppLauncherAbuseDetector* abuse_detector =
[[AppLauncherAbuseDetector alloc] init]);
// Returns true, if the |url| has a scheme for an external application
// (eg. twitter:// , calshow://).
static bool IsAppUrl(const GURL& url);
// Sets the delegate.
void SetDelegate(AppLauncherTabHelperDelegate* delegate);
// Requests to open the application with |url|.
// The method checks if the application for |url| has been opened repeatedly
// by the |source_page_url| page in a short time frame, in that case a prompt
// will appear to the user with an option to block the application from
// launching. Then the method also checks for user interaction and for schemes
// that require special handling (eg. facetime, mailto) and may present the
// user with a confirmation dialog to open the application. If there is no
// such application available or it's not possible to open the application the
// method returns NO.
bool RequestToLaunchApp(const GURL& url,
// user with a confirmation dialog to open the application.
void RequestToLaunchApp(const GURL& url,
const GURL& source_page_url,
bool link_transition);
......@@ -54,11 +54,12 @@ class AppLauncherTabHelper
friend class web::WebStateUserData<AppLauncherTabHelper>;
// Constructor for AppLauncherTabHelper. |abuse_detector| provides policy for
// launching apps. |delegate| can launch applications and present UI and is
// not retained by TabHelper.
// launching apps.
AppLauncherTabHelper(web::WebState* web_state,
AppLauncherAbuseDetector* abuse_detector,
id<AppLauncherTabHelperDelegate> delegate);
AppLauncherAbuseDetector* abuse_detector);
// Getter for the delegate.
AppLauncherTabHelperDelegate* delegate() const { return delegate_; }
// The WebState that this object is attached to.
web::WebState* web_state_ = nullptr;
......@@ -67,13 +68,13 @@ class AppLauncherTabHelper
AppLauncherAbuseDetector* abuse_detector_ = nil;
// Used to launch apps and present UI.
__weak id<AppLauncherTabHelperDelegate> delegate_ = nil;
AppLauncherTabHelperDelegate* delegate_ = nullptr;
// Returns whether there is a prompt shown by |RequestToOpenUrl| or not.
bool is_prompt_active_ = false;
// Must be last member to ensure it is destroyed last.
base::WeakPtrFactory<AppLauncherTabHelper> weak_factory_;
base::WeakPtrFactory<AppLauncherTabHelper> weak_factory_{this};
WEB_STATE_USER_DATA_KEY_DECL();
......
......@@ -10,7 +10,6 @@
#include "base/metrics/histogram_macros.h"
#import "base/strings/sys_string_conversions.h"
#include "components/reading_list/core/reading_list_model.h"
#import "ios/chrome/browser/app_launcher/app_launcher_abuse_detector.h"
#import "ios/chrome/browser/app_launcher/app_launcher_tab_helper_delegate.h"
#include "ios/chrome/browser/browser_state/chrome_browser_state.h"
#import "ios/chrome/browser/chrome_url_util.h"
......@@ -71,27 +70,26 @@ enum class ExternalURLRequestStatus {
} // namespace
// static
void AppLauncherTabHelper::CreateForWebState(
web::WebState* web_state,
AppLauncherAbuseDetector* abuse_detector,
id<AppLauncherTabHelperDelegate> delegate) {
DCHECK(web_state);
if (!FromWebState(web_state)) {
web_state->SetUserData(UserDataKey(),
base::WrapUnique(new AppLauncherTabHelper(
web_state, abuse_detector, delegate)));
}
AppLauncherAbuseDetector* abuse_detector) {
if (FromWebState(web_state))
return;
web_state->SetUserData(
UserDataKey(),
base::WrapUnique(new AppLauncherTabHelper(web_state, abuse_detector)));
}
AppLauncherTabHelper::AppLauncherTabHelper(
web::WebState* web_state,
AppLauncherAbuseDetector* abuse_detector,
id<AppLauncherTabHelperDelegate> delegate)
AppLauncherAbuseDetector* abuse_detector)
: web::WebStatePolicyDecider(web_state),
web_state_(web_state),
abuse_detector_(abuse_detector),
delegate_(delegate),
weak_factory_(this) {}
abuse_detector_(abuse_detector) {
DCHECK(abuse_detector_);
}
AppLauncherTabHelper::~AppLauncherTabHelper() = default;
......@@ -103,18 +101,22 @@ bool AppLauncherTabHelper::IsAppUrl(const GURL& url) {
url.SchemeIs(url::kBlobScheme));
}
bool AppLauncherTabHelper::RequestToLaunchApp(const GURL& url,
void AppLauncherTabHelper::SetDelegate(AppLauncherTabHelperDelegate* delegate) {
delegate_ = delegate;
}
void AppLauncherTabHelper::RequestToLaunchApp(const GURL& url,
const GURL& source_page_url,
bool link_transition) {
// Don't open external application if chrome is not active.
if ([[UIApplication sharedApplication] applicationState] !=
UIApplicationStateActive) {
return false;
return;
}
// Don't try to open external application if a prompt is already active.
if (is_prompt_active_)
return false;
return;
[abuse_detector_ didRequestLaunchExternalAppURL:url
fromSourcePageURL:source_page_url];
......@@ -123,12 +125,12 @@ bool AppLauncherTabHelper::RequestToLaunchApp(const GURL& url,
fromSourcePageURL:source_page_url];
switch (policy) {
case ExternalAppLaunchPolicyBlock: {
return false;
return;
}
case ExternalAppLaunchPolicyAllow: {
return [delegate_ appLauncherTabHelper:this
launchAppWithURL:url
linkTransition:link_transition];
if (delegate_)
delegate_->LaunchAppForTabHelper(this, url, link_transition);
return;
}
case ExternalAppLaunchPolicyPrompt: {
is_prompt_active_ = true;
......@@ -136,29 +138,22 @@ bool AppLauncherTabHelper::RequestToLaunchApp(const GURL& url,
weak_factory_.GetWeakPtr();
GURL copied_url = url;
GURL copied_source_page_url = source_page_url;
[delegate_ appLauncherTabHelper:this
showAlertOfRepeatedLaunchesWithCompletionHandler:^(
BOOL user_allowed) {
if (!delegate_)
return;
delegate_->ShowRepeatedAppLaunchAlert(
this, base::BindOnce(^(BOOL user_allowed) {
if (!weak_this.get())
return;
if (user_allowed) {
if (user_allowed && weak_this->delegate()) {
// By confirming that user wants to launch the application, there
// is no need to check for |link_tapped|.
[delegate_ appLauncherTabHelper:weak_this.get()
launchAppWithURL:copied_url
linkTransition:YES];
} else {
if (!base::FeatureList::IsEnabled(dialogs::kNonModalDialogs)) {
// Only block app launches if the app launch alert is being
// displayed modally since DOS attacks are not possible when the
// app launch alert is presented non-modally.
[abuse_detector_ blockLaunchingAppURL:copied_url
fromSourcePageURL:copied_source_page_url];
}
weak_this->delegate()->LaunchAppForTabHelper(
this, copied_url,
/*link_transition=*/true);
}
is_prompt_active_ = false;
}];
return true;
}));
return;
}
}
}
......
......@@ -5,29 +5,29 @@
#ifndef IOS_CHROME_BROWSER_APP_LAUNCHER_APP_LAUNCHER_TAB_HELPER_DELEGATE_H_
#define IOS_CHROME_BROWSER_APP_LAUNCHER_APP_LAUNCHER_TAB_HELPER_DELEGATE_H_
#include "ios/chrome/browser/procedural_block_types.h"
#include "base/callback.h"
class AppLauncherTabHelper;
class GURL;
// Protocol for handling application launching and presenting related UI.
@protocol AppLauncherTabHelperDelegate
// Launches application that has |URL| if possible (optionally after confirming
// via dialog). Returns NO if there is no such application available.
// TODO(crbug.com/850760): Change this method return to void, once the new
// AppLauncherRefresh logic is always enabled.
- (BOOL)appLauncherTabHelper:(AppLauncherTabHelper*)tabHelper
launchAppWithURL:(const GURL&)URL
linkTransition:(BOOL)linkTransition;
// Alerts the user that there have been repeated attempts to launch
// the application. |completionHandler| is called with the user's
// response on whether to launch the application.
- (void)appLauncherTabHelper:(AppLauncherTabHelper*)tabHelper
showAlertOfRepeatedLaunchesWithCompletionHandler:
(ProceduralBlockWithBool)completionHandler;
@end
// Interface for handling application launching from a tab helper.
class AppLauncherTabHelperDelegate {
public:
AppLauncherTabHelperDelegate() = default;
virtual ~AppLauncherTabHelperDelegate() = default;
// Launches application that has |URL| if possible (optionally after
// confirming via dialog).
virtual void LaunchAppForTabHelper(AppLauncherTabHelper* tab_helper,
const GURL& url,
bool link_transition) = 0;
// Alerts the user that there have been repeated attempts to launch
// the application. |completionHandler| is called with the user's
// response on whether to launch the application.
virtual void ShowRepeatedAppLaunchAlert(
AppLauncherTabHelper* tab_helper,
base::OnceCallback<void(bool)> completion) = 0;
};
#endif // IOS_CHROME_BROWSER_APP_LAUNCHER_APP_LAUNCHER_TAB_HELPER_DELEGATE_H_
......@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef IOS_CHROME_BROWSER_UI_APP_LAUNCHER_APP_LAUNCHER_UTIL_H_
#define IOS_CHROME_BROWSER_UI_APP_LAUNCHER_APP_LAUNCHER_UTIL_H_
#ifndef IOS_CHROME_BROWSER_APP_LAUNCHER_APP_LAUNCHER_UTIL_H_
#define IOS_CHROME_BROWSER_APP_LAUNCHER_APP_LAUNCHER_UTIL_H_
#import <Foundation/Foundation.h>
......@@ -15,4 +15,4 @@ NSSet<NSString*>* GetItmsSchemes();
// Returns whether |url| has an app store scheme.
bool UrlHasAppStoreScheme(const GURL& url);
#endif // IOS_CHROME_BROWSER_UI_APP_LAUNCHER_APP_LAUNCHER_UTIL_H_
#endif // IOS_CHROME_BROWSER_APP_LAUNCHER_APP_LAUNCHER_UTIL_H_
......@@ -2,11 +2,9 @@
// 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/app_launcher/app_launcher_util.h"
#import "ios/chrome/browser/app_launcher/app_launcher_util.h"
#include "base/strings/sys_string_conversions.h"
#include "ios/chrome/grit/ios_strings.h"
#include "ui/base/l10n/l10n_util.h"
#include "url/gurl.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
......
// Copyright 2020 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.
#ifndef IOS_CHROME_BROWSER_APP_LAUNCHER_FAKE_APP_LAUNCHER_ABUSE_DETECTOR_H_
#define IOS_CHROME_BROWSER_APP_LAUNCHER_FAKE_APP_LAUNCHER_ABUSE_DETECTOR_H_
#import "ios/chrome/browser/app_launcher/app_launcher_abuse_detector.h"
// An AppLauncherAbuseDetector for testing.
@interface FakeAppLauncherAbuseDetector : AppLauncherAbuseDetector
// The policy returned by |-launchPolicyforURL:fromSourcePageURL:|. Default
// value is ExternalAppLaunchPolicyAllow.
@property(nonatomic, assign) ExternalAppLaunchPolicy policy;
@end
#endif // IOS_CHROME_BROWSER_APP_LAUNCHER_FAKE_APP_LAUNCHER_ABUSE_DETECTOR_H_
// Copyright 2020 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/app_launcher/fake_app_launcher_abuse_detector.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
@implementation FakeAppLauncherAbuseDetector
- (instancetype)init {
if (self = [super init]) {
_policy = ExternalAppLaunchPolicyAllow;
}
return self;
}
- (ExternalAppLaunchPolicy)launchPolicyForURL:(const GURL&)URL
fromSourcePageURL:(const GURL&)sourcePageURL {
return self.policy;
}
@end
......@@ -41,6 +41,7 @@ source_set("main") {
deps = [
"//base",
"//components/keyed_service/ios",
"//ios/chrome/browser/app_launcher",
"//ios/chrome/browser/browser_state",
"//ios/chrome/browser/crash_report/breadcrumbs",
"//ios/chrome/browser/crash_report/breadcrumbs:feature_flags",
......
......@@ -5,6 +5,7 @@
#import "ios/chrome/browser/main/browser_agent_util.h"
#include "base/feature_list.h"
#import "ios/chrome/browser/app_launcher/app_launcher_browser_agent.h"
#include "ios/chrome/browser/browser_state/chrome_browser_state.h"
#include "ios/chrome/browser/crash_report/breadcrumbs/breadcrumb_manager_browser_agent.h"
#include "ios/chrome/browser/crash_report/breadcrumbs/features.h"
......@@ -36,6 +37,7 @@ void AttachBrowserAgents(Browser* browser) {
WebUsageEnablerBrowserAgent::CreateForBrowser(browser);
DeviceSharingBrowserAgent::CreateForBrowser(browser);
UrlLoadingNotifierBrowserAgent::CreateForBrowser(browser);
AppLauncherBrowserAgent::CreateForBrowser(browser);
// UrlLoadingBrowserAgent requires UrlLoadingNotifierBrowserAgent.
UrlLoadingBrowserAgent::CreateForBrowser(browser);
......
......@@ -56,6 +56,7 @@ source_set("tabs_internal") {
"//components/security_state/ios",
"//components/strings",
"//ios/chrome/browser",
"//ios/chrome/browser/app_launcher",
"//ios/chrome/browser/autofill",
"//ios/chrome/browser/autofill:autofill_internal",
"//ios/chrome/browser/browser_state",
......
......@@ -17,6 +17,7 @@
#include "components/safe_browsing/core/features.h"
#import "components/security_state/ios/insecure_input_tab_helper.h"
#import "components/ukm/ios/ukm_url_recorder.h"
#import "ios/chrome/browser/app_launcher/app_launcher_tab_helper.h"
#import "ios/chrome/browser/autofill/autofill_tab_helper.h"
#import "ios/chrome/browser/autofill/form_suggestion_tab_helper.h"
#include "ios/chrome/browser/browser_state/chrome_browser_state.h"
......@@ -97,6 +98,7 @@ void AttachTabHelpers(web::WebState* web_state, bool for_prerender) {
LoadTimingTabHelper::CreateForWebState(web_state);
OverscrollActionsTabHelper::CreateForWebState(web_state);
IOSTaskTabHelper::CreateForWebState(web_state);
AppLauncherTabHelper::CreateForWebState(web_state);
if (base::FeatureList::IsEnabled(kInfobarOverlayUI)) {
InfobarOverlayRequestInserter::CreateForWebState(web_state);
......
# Copyright 2018 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("//build/config/ios/rules.gni")
source_set("app_launcher") {
configs += [ "//build/config/compiler:enable_arc" ]
sources = [
"app_launcher_coordinator.h",
"app_launcher_coordinator.mm",
"app_launcher_util.h",
"app_launcher_util.mm",
]
deps = [
"//base",
"//components/strings:components_strings_grit",
"//ios/chrome/app/strings:ios_strings_grit",
"//ios/chrome/browser",
"//ios/chrome/browser/app_launcher",
"//ios/chrome/browser/main:public",
"//ios/chrome/browser/overlays",
"//ios/chrome/browser/overlays/public/web_content_area",
"//ios/chrome/browser/ui/coordinators:chrome_coordinators",
"//ios/chrome/browser/ui/dialogs:feature_flags",
"//ios/chrome/browser/web_state_list",
"//ios/public/provider/chrome/browser",
"//ios/public/provider/chrome/browser/mailto",
"//ios/web/public",
"//ios/web/public/navigation",
"//net",
"//ui/base",
"//url",
]
}
source_set("unit_tests") {
configs += [ "//build/config/compiler:enable_arc" ]
testonly = true
sources = [ "app_launcher_coordinator_unittest.mm" ]
deps = [
":app_launcher",
"//base",
"//base/test:test_support",
"//ios/chrome/app/strings:ios_strings_grit",
"//ios/chrome/browser/app_launcher",
"//ios/chrome/browser/main:test_support",
"//ios/chrome/browser/overlays",
"//ios/chrome/browser/overlays/public/web_content_area",
"//ios/chrome/browser/ui/dialogs:feature_flags",
"//ios/chrome/test:test_support",
"//ios/web/public/test/fakes",
"//testing/gtest",
"//third_party/ocmock",
"//ui/base",
"//url",
]
}
// Copyright 2017 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.
#ifndef IOS_CHROME_BROWSER_UI_APP_LAUNCHER_APP_LAUNCHER_COORDINATOR_H_
#define IOS_CHROME_BROWSER_UI_APP_LAUNCHER_APP_LAUNCHER_COORDINATOR_H_
#import <UIKit/UIKit.h>
#import "ios/chrome/browser/app_launcher/app_launcher_tab_helper_delegate.h"
#import "ios/chrome/browser/ui/coordinators/chrome_coordinator.h"
// A coordinator that handles UI related to launching apps.
@interface AppLauncherCoordinator
: ChromeCoordinator <AppLauncherTabHelperDelegate>
// Unavailable, use -initWithBaseViewController:browser:.
- (instancetype)initWithBaseViewController:(UIViewController*)viewController
NS_UNAVAILABLE;
// Unavailable, use -initWithBaseViewController:browser:.
- (instancetype)initWithBaseViewController:(UIViewController*)viewController
browserState:(ChromeBrowserState*)browserState
NS_UNAVAILABLE;
@end
#endif // IOS_CHROME_BROWSER_UI_APP_LAUNCHER_APP_LAUNCHER_COORDINATOR_H_
// Copyright 2017 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/app_launcher/app_launcher_coordinator.h"
#import <UIKit/UIKit.h>
#include "base/bind.h"
#include "base/logging.h"
#include "base/metrics/histogram_macros.h"
#include "components/strings/grit/components_strings.h"
#import "ios/chrome/browser/app_launcher/app_launcher_tab_helper.h"
#import "ios/chrome/browser/main/browser.h"
#include "ios/chrome/browser/overlays/public/overlay_callback_manager.h"
#import "ios/chrome/browser/overlays/public/overlay_request.h"
#import "ios/chrome/browser/overlays/public/overlay_request_queue.h"
#import "ios/chrome/browser/overlays/public/overlay_response.h"
#import "ios/chrome/browser/overlays/public/web_content_area/app_launcher_alert_overlay.h"
#include "ios/chrome/browser/procedural_block_types.h"
#import "ios/chrome/browser/ui/app_launcher/app_launcher_util.h"
#import "ios/chrome/browser/ui/dialogs/dialog_features.h"
#import "ios/chrome/browser/web_state_list/web_state_list.h"
#import "ios/chrome/browser/web_state_list/web_state_opener.h"
#include "ios/chrome/grit/ios_strings.h"
#include "ios/public/provider/chrome/browser/chrome_browser_provider.h"
#include "ios/public/provider/chrome/browser/mailto/mailto_handler_provider.h"
#import "ios/web/public/navigation/navigation_manager.h"
#import "ios/web/public/web_state.h"
#import "net/base/mac/url_conversions.h"
#include "ui/base/l10n/l10n_util.h"
#include "url/gurl.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
namespace {
// Records histogram metric on the user's response when prompted to open another
// application. |user_accepted| should be YES if the user accepted the prompt to
// launch another application. This call is extracted to a separate function to
// reduce macro code expansion.
void RecordUserAcceptedAppLaunchMetric(BOOL user_accepted) {
UMA_HISTOGRAM_BOOLEAN("Tab.ExternalApplicationOpened", user_accepted);
}
// Callback for the app launcher alert overlay.
void AppLauncherOverlayCallback(ProceduralBlockWithBool app_launch_completion,
OverlayResponse* response) {
DCHECK(app_launch_completion);
if (!response) {
app_launch_completion(NO);
return;
}
AppLauncherAlertOverlayResponseInfo* info =
response->GetInfo<AppLauncherAlertOverlayResponseInfo>();
bool allow_navigation = info ? info->allow_navigation() : false;
app_launch_completion(allow_navigation);
}
} // namespace
@implementation AppLauncherCoordinator
@synthesize baseViewController = _baseViewController;
#pragma mark - Private methods
// Alerts the user with |message| and buttons with titles
// |acceptActionTitle| and |rejectActionTitle|. |completionHandler| is called
// with a BOOL indicating whether the user has tapped the accept button.
- (void)showAlertWithMessage:(NSString*)message
acceptActionTitle:(NSString*)acceptActionTitle
rejectActionTitle:(NSString*)rejectActionTitle
completionHandler:(ProceduralBlockWithBool)completionHandler {
DCHECK(!base::FeatureList::IsEnabled(dialogs::kNonModalDialogs));
UIAlertController* alertController =
[UIAlertController alertControllerWithTitle:nil
message:message
preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction* acceptAction =
[UIAlertAction actionWithTitle:acceptActionTitle
style:UIAlertActionStyleDefault
handler:^(UIAlertAction* action) {
completionHandler(YES);
}];
UIAlertAction* rejectAction =
[UIAlertAction actionWithTitle:rejectActionTitle
style:UIAlertActionStyleCancel
handler:^(UIAlertAction* action) {
completionHandler(NO);
}];
[alertController addAction:rejectAction];
[alertController addAction:acceptAction];
[self.baseViewController presentViewController:alertController
animated:YES
completion:nil];
}
// Shows an alert that the app will open in another application. If the user
// accepts, the |URL| is launched.
- (void)showAlertAndLaunchAppURL:(const GURL&)URL
webState:(web::WebState*)webState {
NSString* prompt = l10n_util::GetNSString(IDS_IOS_OPEN_IN_ANOTHER_APP);
NSString* openLabel =
l10n_util::GetNSString(IDS_IOS_APP_LAUNCHER_OPEN_APP_BUTTON_LABEL);
NSString* cancelLabel = l10n_util::GetNSString(IDS_CANCEL);
NSURL* copiedURL = net::NSURLWithGURL(URL);
ProceduralBlockWithBool completion = ^(BOOL userAccepted) {
RecordUserAcceptedAppLaunchMetric(userAccepted);
if (userAccepted) {
[[UIApplication sharedApplication] openURL:copiedURL
options:@{}
completionHandler:nil];
}
};
if (base::FeatureList::IsEnabled(dialogs::kNonModalDialogs)) {
std::unique_ptr<OverlayRequest> request =
OverlayRequest::CreateWithConfig<AppLauncherAlertOverlayRequestConfig>(
/*is_repeated_request=*/false);
request->GetCallbackManager()->AddCompletionCallback(
base::BindOnce(&AppLauncherOverlayCallback, completion));
[self overlayRequestQueueForWebState:webState]->AddRequest(
std::move(request));
} else {
[self showAlertWithMessage:prompt
acceptActionTitle:openLabel
rejectActionTitle:cancelLabel
completionHandler:completion];
}
}
// Returns the OverlayRequestQueue to use when displaying the app launcher
// dialog when requested by |webState|.
- (OverlayRequestQueue*)overlayRequestQueueForWebState:
(web::WebState*)webState {
web::WebState* queueWebState = webState;
// If an app launch navigation is occurring in a new tab, the tab will be
// closed immediately after the navigation fails, cancelling the app launcher
// dialog before it gets a chance to be shown. When this occurs, use the
// OverlayRequestQueue for the tab's opener instead.
if (!webState->GetNavigationManager()->GetItemCount() &&
webState->HasOpener()) {
WebStateList* webStateList = self.browser->GetWebStateList();
int index = webStateList->GetIndexOfWebState(webState);
queueWebState =
webStateList->GetOpenerOfWebStateAt(index).opener ?: queueWebState;
}
return OverlayRequestQueue::FromWebState(queueWebState,
OverlayModality::kWebContentArea);
}
#pragma mark - AppLauncherTabHelperDelegate
- (BOOL)appLauncherTabHelper:(AppLauncherTabHelper*)tabHelper
launchAppWithURL:(const GURL&)URL
linkTransition:(BOOL)linkTransition {
// Don't open application if chrome is not active.
if ([[UIApplication sharedApplication] applicationState] !=
UIApplicationStateActive) {
return NO;
}
web::WebState* webState = tabHelper->web_state();
if (UrlHasAppStoreScheme(URL)) {
[self showAlertAndLaunchAppURL:URL webState:webState];
return YES;
}
// Uses a Mailto Handler to open the appropriate app, if available.
if (URL.SchemeIs(url::kMailToScheme)) {
MailtoHandlerProvider* provider =
ios::GetChromeBrowserProvider()->GetMailtoHandlerProvider();
provider->HandleMailtoURL(net::NSURLWithGURL(URL));
return YES;
}
// For all other apps other than AppStore, show a prompt if there was no
// link transition.
if (linkTransition) {
[[UIApplication sharedApplication] openURL:net::NSURLWithGURL(URL)
options:@{}
completionHandler:nil];
} else {
[self showAlertAndLaunchAppURL:URL webState:webState];
}
return YES;
}
- (void)appLauncherTabHelper:(AppLauncherTabHelper*)tabHelper
showAlertOfRepeatedLaunchesWithCompletionHandler:
(ProceduralBlockWithBool)completionHandler {
NSString* message =
l10n_util::GetNSString(IDS_IOS_OPEN_REPEATEDLY_ANOTHER_APP);
NSString* allowLaunchTitle =
l10n_util::GetNSString(IDS_IOS_OPEN_REPEATEDLY_ANOTHER_APP_ALLOW);
NSString* blockLaunchTitle =
l10n_util::GetNSString(IDS_IOS_OPEN_REPEATEDLY_ANOTHER_APP_BLOCK);
ProceduralBlockWithBool completion = ^(BOOL userAllowed) {
UMA_HISTOGRAM_BOOLEAN("IOS.RepeatedExternalAppPromptResponse", userAllowed);
completionHandler(userAllowed);
};
if (base::FeatureList::IsEnabled(dialogs::kNonModalDialogs)) {
std::unique_ptr<OverlayRequest> request =
OverlayRequest::CreateWithConfig<AppLauncherAlertOverlayRequestConfig>(
/* is_repeated_request= */ true);
request->GetCallbackManager()->AddCompletionCallback(
base::BindOnce(&AppLauncherOverlayCallback, completion));
OverlayRequestQueue::FromWebState(tabHelper->web_state(),
OverlayModality::kWebContentArea)
->AddRequest(std::move(request));
} else {
[self showAlertWithMessage:message
acceptActionTitle:allowLaunchTitle
rejectActionTitle:blockLaunchTitle
completionHandler:completion];
}
}
@end
// Copyright 2018 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/app_launcher/app_launcher_coordinator.h"
#import <UIKit/UIKit.h>
#include "base/mac/foundation_util.h"
#include "base/test/task_environment.h"
#import "ios/chrome/browser/app_launcher/app_launcher_tab_helper.h"
#include "ios/chrome/browser/main/test_browser.h"
#include "ios/chrome/browser/overlays/public/overlay_request.h"
#import "ios/chrome/browser/overlays/public/overlay_request_queue.h"
#import "ios/chrome/browser/overlays/public/web_content_area/app_launcher_alert_overlay.h"
#import "ios/chrome/browser/ui/dialogs/dialog_features.h"
#include "ios/chrome/grit/ios_strings.h"
#import "ios/chrome/test/scoped_key_window.h"
#import "ios/web/public/test/fakes/test_navigation_manager.h"
#import "ios/web/public/test/fakes/test_web_state.h"
#include "testing/gtest/include/gtest/gtest.h"
#import "testing/gtest_mac.h"
#include "testing/platform_test.h"
#import "third_party/ocmock/OCMock/OCMock.h"
#include "ui/base/l10n/l10n_util.h"
#include "url/gurl.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
// Test fixture for AppLauncherCoordinator class.
class AppLauncherCoordinatorTest : public PlatformTest {
protected:
AppLauncherCoordinatorTest() {
base_view_controller_ = [[UIViewController alloc] init];
[scoped_key_window_.Get() setRootViewController:base_view_controller_];
browser_ = std::make_unique<TestBrowser>();
coordinator_ = [[AppLauncherCoordinator alloc]
initWithBaseViewController:base_view_controller_
browser:browser_.get()];
application_ = OCMClassMock([UIApplication class]);
OCMStub([application_ sharedApplication]).andReturn(application_);
AppLauncherTabHelper::CreateForWebState(&web_state_, nil, nil);
std::unique_ptr<web::TestNavigationManager> navigation_manager =
std::make_unique<web::TestNavigationManager>();
navigation_manager->AddItem(GURL("http://www.chromium.org"),
ui::PAGE_TRANSITION_LINK);
web_state_.SetNavigationManager(std::move(navigation_manager));
}
~AppLauncherCoordinatorTest() override {
[application_ stopMocking];
OverlayRequestQueue::FromWebState(&web_state_,
OverlayModality::kWebContentArea)
->CancelAllRequests();
}
AppLauncherTabHelper* tab_helper() {
return AppLauncherTabHelper::FromWebState(&web_state_);
}
bool IsShowingDialog(bool is_repeated_request) {
if (base::FeatureList::IsEnabled(dialogs::kNonModalDialogs)) {
OverlayRequest* request =
OverlayRequestQueue::FromWebState(&web_state_,
OverlayModality::kWebContentArea)
->front_request();
if (!request)
return false;
AppLauncherAlertOverlayRequestConfig* config =
request->GetConfig<AppLauncherAlertOverlayRequestConfig>();
return config && config->is_repeated_request() == is_repeated_request;
} else {
UIAlertController* alert_controller =
base::mac::ObjCCastStrict<UIAlertController>(
base_view_controller_.presentedViewController);
NSString* message =
is_repeated_request
? l10n_util::GetNSString(IDS_IOS_OPEN_REPEATEDLY_ANOTHER_APP)
: l10n_util::GetNSString(IDS_IOS_OPEN_IN_ANOTHER_APP);
return alert_controller.message == message;
}
}
base::test::TaskEnvironment task_environment_;
web::TestWebState web_state_;
UIViewController* base_view_controller_ = nil;
ScopedKeyWindow scoped_key_window_;
std::unique_ptr<Browser> browser_;
AppLauncherCoordinator* coordinator_ = nil;
id application_ = nil;
};
// Tests that an itunes URL shows an alert.
TEST_F(AppLauncherCoordinatorTest, ItmsUrlShowsAlert) {
BOOL app_exists = [coordinator_ appLauncherTabHelper:tab_helper()
launchAppWithURL:GURL("itms://1234")
linkTransition:NO];
EXPECT_TRUE(app_exists);
EXPECT_TRUE(IsShowingDialog(/*is_repeated_request=*/false));
}
// Tests that in the new AppLauncher, an app URL attempts to launch the
// application.
TEST_F(AppLauncherCoordinatorTest, AppUrlLaunchesApp) {
OCMExpect([application_ openURL:[NSURL URLWithString:@"some-app://1234"]
options:@{}
completionHandler:nil]);
[coordinator_ appLauncherTabHelper:tab_helper()
launchAppWithURL:GURL("some-app://1234")
linkTransition:YES];
[application_ verify];
}
// Tests that in the new AppLauncher, an app URL shows a prompt if there was no
// link transition.
TEST_F(AppLauncherCoordinatorTest, AppUrlShowsPrompt) {
[coordinator_ appLauncherTabHelper:tab_helper()
launchAppWithURL:GURL("some-app://1234")
linkTransition:NO];
EXPECT_TRUE(IsShowingDialog(/*is_repeated_request=*/false));
}
......@@ -76,7 +76,6 @@ source_set("browser_view") {
"//ios/chrome/browser/ui/activity_services:coordinator",
"//ios/chrome/browser/ui/activity_services/requirements",
"//ios/chrome/browser/ui/alert_coordinator",
"//ios/chrome/browser/ui/app_launcher",
"//ios/chrome/browser/ui/authentication",
"//ios/chrome/browser/ui/autofill:autofill",
"//ios/chrome/browser/ui/autofill/form_input_accessory",
......
......@@ -22,7 +22,6 @@
#import "ios/chrome/browser/store_kit/store_kit_tab_helper.h"
#import "ios/chrome/browser/tabs/tab_title_util.h"
#import "ios/chrome/browser/ui/alert_coordinator/repost_form_coordinator.h"
#import "ios/chrome/browser/ui/app_launcher/app_launcher_coordinator.h"
#import "ios/chrome/browser/ui/autofill/form_input_accessory/form_input_accessory_coordinator.h"
#import "ios/chrome/browser/ui/autofill/manual_fill/manual_fill_all_password_coordinator.h"
#import "ios/chrome/browser/ui/autofill/manual_fill/manual_fill_injection_handler.h"
......@@ -107,9 +106,6 @@
// Child Coordinators, listed in alphabetical order.
// =================================================
// Coordinator for UI related to launching external apps.
@property(nonatomic, strong) AppLauncherCoordinator* appLauncherCoordinator;
// Presents a QLPreviewController in order to display USDZ format 3D models.
@property(nonatomic, strong) ARQuickLookCoordinator* ARQuickLookCoordinator;
......@@ -334,11 +330,6 @@
// coordinators.
DCHECK(self.dispatcher);
self.appLauncherCoordinator = [[AppLauncherCoordinator alloc]
initWithBaseViewController:self.viewController
browser:self.browser];
[self.appLauncherCoordinator start];
self.ARQuickLookCoordinator = [[ARQuickLookCoordinator alloc]
initWithBaseViewController:self.viewController
browser:self.browser];
......@@ -424,9 +415,6 @@
[self.allPasswordCoordinator stop];
self.allPasswordCoordinator = nil;
[self.appLauncherCoordinator stop];
self.appLauncherCoordinator = nil;
[self.ARQuickLookCoordinator stop];
self.ARQuickLookCoordinator = nil;
......@@ -956,10 +944,6 @@
// Install delegates for |webState|.
- (void)installDelegatesForWebState:(web::WebState*)webState {
AppLauncherTabHelper::CreateForWebState(
webState, [[AppLauncherAbuseDetector alloc] init],
self.appLauncherCoordinator);
if (AutofillTabHelper::FromWebState(webState)) {
AutofillTabHelper::FromWebState(webState)->SetBaseViewController(
self.viewController);
......
......@@ -116,7 +116,6 @@ source_set("main") {
"//ios/chrome/browser/tabs",
"//ios/chrome/browser/tabs:tabs_internal",
"//ios/chrome/browser/ui/alert_coordinator",
"//ios/chrome/browser/ui/app_launcher",
"//ios/chrome/browser/ui/autofill",
"//ios/chrome/browser/ui/browser_container",
"//ios/chrome/browser/ui/browser_view",
......
......@@ -226,7 +226,6 @@ test("ios_chrome_unittests") {
"//ios/chrome/browser/ui/activity_services:unit_tests",
"//ios/chrome/browser/ui/alert_coordinator:unit_tests",
"//ios/chrome/browser/ui/alert_view:unit_tests",
"//ios/chrome/browser/ui/app_launcher:unit_tests",
"//ios/chrome/browser/ui/authentication:unit_tests",
"//ios/chrome/browser/ui/authentication/cells:unit_tests",
"//ios/chrome/browser/ui/authentication/signin:unit_tests",
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment