Commit 0e2cbe01 authored by Dominick Ng's avatar Dominick Ng Committed by Commit Bot

Make ArcNavigationThrottle a content::WebContentsObserver.

This CL ensures that querying for ARC apps is gracefully aborted if the
owning WebContents is destroyed during the asynchronous process.

ArcNavigationThrottle is converted to be a self-owning object with a
private constructor and destructor. When the static app query methods
are invoked, an instance of the object is instantiated to handle the
querying. By overriding WebContentsDestroyed(), it can cancel all
in-flight callbacks upon WebContents destruction and safely bail out of
querying.

BUG=824598
TBR=sky@chromium.org

Change-Id: I2ba6d391b9babec46b277cbcebe567786215bf5a
Reviewed-on: https://chromium-review.googlesource.com/1003432
Commit-Queue: Dominick Ng <dominickn@chromium.org>
Reviewed-by: default avatarYusuke Sato <yusukes@chromium.org>
Reviewed-by: default avatarDavid Jacobo <djacobo@chromium.org>
Cr-Commit-Position: refs/heads/master@{#551537}
parent a7554261
...@@ -133,19 +133,13 @@ AppsNavigationThrottle::MaybeCreate(content::NavigationHandle* handle) { ...@@ -133,19 +133,13 @@ AppsNavigationThrottle::MaybeCreate(content::NavigationHandle* handle) {
// static // static
void AppsNavigationThrottle::ShowIntentPickerBubble( void AppsNavigationThrottle::ShowIntentPickerBubble(
const Browser* browser,
content::WebContents* web_contents, content::WebContents* web_contents,
const GURL& url) { const GURL& url) {
// TODO(crbug.com/824598): remove |browser| as a parameter for this method, arc::ArcNavigationThrottle::GetArcAppsForPicker(
// and instead factor out asychronous querying to a helper object that is web_contents, url,
// a WebContentsObserver and self-deletes when querying is complete or if the
// WebContents is destroyed. Always lookup |browser| via the WebContents. This
// will avoid lifetime issues.
arc::ArcNavigationThrottle::QueryArcApps(
browser, url,
base::BindOnce( base::BindOnce(
&AppsNavigationThrottle::FindPwaForUrlAndShowIntentPickerForApps, &AppsNavigationThrottle::FindPwaForUrlAndShowIntentPickerForApps,
browser, web_contents, url)); web_contents, url));
} }
// static // static
...@@ -219,8 +213,7 @@ AppsNavigationThrottle::AppsNavigationThrottle( ...@@ -219,8 +213,7 @@ AppsNavigationThrottle::AppsNavigationThrottle(
content::NavigationHandle* navigation_handle, content::NavigationHandle* navigation_handle,
bool arc_enabled) bool arc_enabled)
: content::NavigationThrottle(navigation_handle), : content::NavigationThrottle(navigation_handle),
arc_throttle_(arc_enabled ? std::make_unique<arc::ArcNavigationThrottle>() arc_enabled_(arc_enabled),
: nullptr),
ui_displayed_(false), ui_displayed_(false),
weak_factory_(this) {} weak_factory_(this) {}
...@@ -317,15 +310,13 @@ AppsNavigationThrottle::PickerAction AppsNavigationThrottle::GetPickerAction( ...@@ -317,15 +310,13 @@ AppsNavigationThrottle::PickerAction AppsNavigationThrottle::GetPickerAction(
// static // static
void AppsNavigationThrottle::FindPwaForUrlAndShowIntentPickerForApps( void AppsNavigationThrottle::FindPwaForUrlAndShowIntentPickerForApps(
const Browser* browser,
content::WebContents* web_contents, content::WebContents* web_contents,
const GURL& url, const GURL& url,
std::vector<IntentPickerAppInfo> apps) { std::vector<IntentPickerAppInfo> apps) {
std::vector<IntentPickerAppInfo> apps_for_picker = std::vector<IntentPickerAppInfo> apps_for_picker =
FindPwaForUrl(web_contents, url, std::move(apps)); FindPwaForUrl(web_contents, url, std::move(apps));
ShowIntentPickerBubbleForApps(browser, web_contents, url, ShowIntentPickerBubbleForApps(web_contents, url, std::move(apps_for_picker));
std::move(apps_for_picker));
} }
// static // static
...@@ -357,15 +348,17 @@ std::vector<IntentPickerAppInfo> AppsNavigationThrottle::FindPwaForUrl( ...@@ -357,15 +348,17 @@ std::vector<IntentPickerAppInfo> AppsNavigationThrottle::FindPwaForUrl(
// static // static
void AppsNavigationThrottle::ShowIntentPickerBubbleForApps( void AppsNavigationThrottle::ShowIntentPickerBubbleForApps(
const Browser* browser,
content::WebContents* web_contents, content::WebContents* web_contents,
const GURL& url, const GURL& url,
std::vector<IntentPickerAppInfo> apps) { std::vector<IntentPickerAppInfo> apps) {
if (apps.empty()) if (apps.empty())
return; return;
// It should be safe to bind |web_contents| since closing the current tab will
// close the intent picker and run the callback prior to the WebContents being
// deallocated.
chrome::ShowIntentPickerBubble( chrome::ShowIntentPickerBubble(
browser, std::move(apps), chrome::FindBrowserWithWebContents(web_contents), std::move(apps),
base::BindOnce(&AppsNavigationThrottle::OnIntentPickerClosed, base::BindOnce(&AppsNavigationThrottle::OnIntentPickerClosed,
web_contents, url)); web_contents, url));
} }
...@@ -378,7 +371,7 @@ void AppsNavigationThrottle::CancelNavigation() { ...@@ -378,7 +371,7 @@ void AppsNavigationThrottle::CancelNavigation() {
CancelDeferredNavigation(content::NavigationThrottle::CANCEL_AND_IGNORE); CancelDeferredNavigation(content::NavigationThrottle::CANCEL_AND_IGNORE);
} }
void AppsNavigationThrottle::OnDeferredRequestProcessed( void AppsNavigationThrottle::OnDeferredNavigationProcessed(
AppsNavigationAction action, AppsNavigationAction action,
std::vector<IntentPickerAppInfo> apps) { std::vector<IntentPickerAppInfo> apps) {
if (action == AppsNavigationAction::CANCEL) { if (action == AppsNavigationAction::CANCEL) {
...@@ -399,9 +392,7 @@ void AppsNavigationThrottle::OnDeferredRequestProcessed( ...@@ -399,9 +392,7 @@ void AppsNavigationThrottle::OnDeferredRequestProcessed(
if (apps_for_picker.empty()) if (apps_for_picker.empty())
ui_displayed_ = false; ui_displayed_ = false;
ShowIntentPickerBubbleForApps( ShowIntentPickerBubbleForApps(web_contents, url, std::move(apps_for_picker));
chrome::FindBrowserWithWebContents(web_contents), web_contents, url,
std::move(apps_for_picker));
// We are about to resume the navigation, which may destroy this object. // We are about to resume the navigation, which may destroy this object.
Resume(); Resume();
...@@ -435,16 +426,16 @@ AppsNavigationThrottle::HandleRequest() { ...@@ -435,16 +426,16 @@ AppsNavigationThrottle::HandleRequest() {
if (!ShouldOverrideUrlLoading(starting_url_, url)) if (!ShouldOverrideUrlLoading(starting_url_, url))
return content::NavigationThrottle::PROCEED; return content::NavigationThrottle::PROCEED;
if (arc_throttle_ && if (arc_enabled_ &&
arc_throttle_->ShouldDeferRequest( arc::ArcNavigationThrottle::WillGetArcAppsForNavigation(
handle, handle,
base::BindOnce(&AppsNavigationThrottle::OnDeferredRequestProcessed, base::BindOnce(&AppsNavigationThrottle::OnDeferredNavigationProcessed,
weak_factory_.GetWeakPtr()))) { weak_factory_.GetWeakPtr()))) {
// Handling is now deferred to |arc_throttle_|, which asynchronously queries // Handling is now deferred to ArcNavigationThrottle, which asynchronously
// ARC for apps, and runs OnDeferredRequestProcessed() with an action based // queries ARC for apps, and runs OnDeferredNavigationProcessed() with an
// on whether an acceptable app was found and user consent to open received. // action based on whether an acceptable app was found and user consent to
// We assume the UI is shown or a preferred app was found; reset to false if // open received. We assume the UI is shown or a preferred app was found;
// we resume the navigation. // reset to false if we resume the navigation.
ui_displayed_ = true; ui_displayed_ = true;
return content::NavigationThrottle::DEFER; return content::NavigationThrottle::DEFER;
} }
...@@ -459,9 +450,7 @@ AppsNavigationThrottle::HandleRequest() { ...@@ -459,9 +450,7 @@ AppsNavigationThrottle::HandleRequest() {
if (!apps.empty()) if (!apps.empty())
ui_displayed_ = true; ui_displayed_ = true;
ShowIntentPickerBubbleForApps( ShowIntentPickerBubbleForApps(web_contents, url, std::move(apps));
chrome::FindBrowserWithWebContents(web_contents), web_contents, url,
std::move(apps));
} }
return content::NavigationThrottle::PROCEED; return content::NavigationThrottle::PROCEED;
......
...@@ -15,12 +15,6 @@ ...@@ -15,12 +15,6 @@
#include "content/public/browser/navigation_throttle.h" #include "content/public/browser/navigation_throttle.h"
#include "url/gurl.h" #include "url/gurl.h"
class Browser;
namespace arc {
class ArcNavigationThrottle;
}
namespace content { namespace content {
class NavigationHandle; class NavigationHandle;
class WebContents; class WebContents;
...@@ -43,10 +37,9 @@ class AppsNavigationThrottle : public content::NavigationThrottle { ...@@ -43,10 +37,9 @@ class AppsNavigationThrottle : public content::NavigationThrottle {
static std::unique_ptr<content::NavigationThrottle> MaybeCreate( static std::unique_ptr<content::NavigationThrottle> MaybeCreate(
content::NavigationHandle* handle); content::NavigationHandle* handle);
// Queries for installed app which can handle |url|, and displays the intent // Queries for installed apps which can handle |url|, and displays the intent
// picker bubble in |browser|. // picker bubble for |web_contents|.
static void ShowIntentPickerBubble(const Browser* browser, static void ShowIntentPickerBubble(content::WebContents* web_contents,
content::WebContents* web_contents,
const GURL& url); const GURL& url);
// Called when the intent picker is closed for |url|, in |web_contents|, with // Called when the intent picker is closed for |url|, in |web_contents|, with
...@@ -139,7 +132,6 @@ class AppsNavigationThrottle : public content::NavigationThrottle { ...@@ -139,7 +132,6 @@ class AppsNavigationThrottle : public content::NavigationThrottle {
bool should_persist); bool should_persist);
static void FindPwaForUrlAndShowIntentPickerForApps( static void FindPwaForUrlAndShowIntentPickerForApps(
const Browser* browser,
content::WebContents* web_contents, content::WebContents* web_contents,
const GURL& url, const GURL& url,
std::vector<IntentPickerAppInfo> apps); std::vector<IntentPickerAppInfo> apps);
...@@ -152,7 +144,6 @@ class AppsNavigationThrottle : public content::NavigationThrottle { ...@@ -152,7 +144,6 @@ class AppsNavigationThrottle : public content::NavigationThrottle {
std::vector<IntentPickerAppInfo> apps); std::vector<IntentPickerAppInfo> apps);
static void ShowIntentPickerBubbleForApps( static void ShowIntentPickerBubbleForApps(
const Browser* browser,
content::WebContents* web_contents, content::WebContents* web_contents,
const GURL& url, const GURL& url,
std::vector<IntentPickerAppInfo> apps); std::vector<IntentPickerAppInfo> apps);
...@@ -161,17 +152,18 @@ class AppsNavigationThrottle : public content::NavigationThrottle { ...@@ -161,17 +152,18 @@ class AppsNavigationThrottle : public content::NavigationThrottle {
content::NavigationThrottle::ThrottleCheckResult HandleRequest(); content::NavigationThrottle::ThrottleCheckResult HandleRequest();
// Passed as a callback to allow app-platform-specific code to asychronously // Passed as a callback to allow ARC-specific code to asynchronously inform
// inform this object of the apps which can handle this URL, and optionally // this object of the apps which can handle this URL, and optionally request
// request that the navigation be completely cancelled (e.g. if a preferred // that the navigation be completely cancelled (e.g. if a preferred app has
// app has been opened). // been opened).
void OnDeferredRequestProcessed(AppsNavigationAction action, void OnDeferredNavigationProcessed(AppsNavigationAction action,
std::vector<IntentPickerAppInfo> apps); std::vector<IntentPickerAppInfo> apps);
// A reference to the starting GURL. // A reference to the starting GURL.
GURL starting_url_; GURL starting_url_;
std::unique_ptr<arc::ArcNavigationThrottle> arc_throttle_; // True if ARC is enabled, false otherwise.
const bool arc_enabled_;
// Keeps track of whether we already shown the UI or preferred app. Since // Keeps track of whether we already shown the UI or preferred app. Since
// AppsNavigationThrottle cannot wait for the user (due to the non-blocking // AppsNavigationThrottle cannot wait for the user (due to the non-blocking
......
...@@ -87,7 +87,7 @@ using AppsNavigationCallback = ...@@ -87,7 +87,7 @@ using AppsNavigationCallback =
// Callback to allow app-platform-specific code to asynchronously provide a list // Callback to allow app-platform-specific code to asynchronously provide a list
// of apps which can handle the navigation. // of apps which can handle the navigation.
using QueryAppsCallback = using GetAppsCallback =
base::OnceCallback<void(std::vector<IntentPickerAppInfo> apps)>; base::OnceCallback<void(std::vector<IntentPickerAppInfo> apps)>;
} // namespace chromeos } // namespace chromeos
......
...@@ -12,11 +12,11 @@ ...@@ -12,11 +12,11 @@
#include "base/macros.h" #include "base/macros.h"
#include "base/memory/weak_ptr.h" #include "base/memory/weak_ptr.h"
#include "chrome/browser/chromeos/apps/intent_helper/apps_navigation_types.h" #include "chrome/browser/chromeos/apps/intent_helper/apps_navigation_types.h"
#include "components/arc/arc_bridge_service.h"
#include "components/arc/intent_helper/arc_intent_helper_bridge.h" #include "components/arc/intent_helper/arc_intent_helper_bridge.h"
#include "content/public/browser/web_contents_observer.h"
#include "url/gurl.h" #include "url/gurl.h"
class Browser;
namespace content { namespace content {
class NavigationHandle; class NavigationHandle;
class WebContents; class WebContents;
...@@ -24,19 +24,31 @@ class WebContents; ...@@ -24,19 +24,31 @@ class WebContents;
namespace arc { namespace arc {
// A class that allow us to retrieve ARC app's information and handle URL // A class that allow us to retrieve installed ARC apps which can handle
// traffic initiated on Chrome browser, either on Chrome or an ARC's app. // a particular URL.
class ArcNavigationThrottle { class ArcNavigationThrottle : content::WebContentsObserver {
public: public:
ArcNavigationThrottle(); // Retrieves ARC apps which can handle |url| for |web_contents|, and runs
~ArcNavigationThrottle(); // |callback| when complete. Does not attempt to open preferred apps.
static void GetArcAppsForPicker(content::WebContents* web_contents,
const GURL& url,
chromeos::GetAppsCallback callback);
// Returns true if the navigation request represented by |handle| should be // Returns true if the navigation request represented by |handle| should be
// deferred while ARC is queried for apps, and if so, |callback| will be run // deferred while ARC is queried for apps, and if so, |callback| will be run
// asynchronously with the action for the navigation. |callback| will not be // asynchronously with the action for the navigation. |callback| will not be
// run if false is returned. // run if false is returned.
bool ShouldDeferRequest(content::NavigationHandle* handle, static bool WillGetArcAppsForNavigation(
chromeos::AppsNavigationCallback callback); content::NavigationHandle* handle,
chromeos::AppsNavigationCallback callback);
// Called to launch an ARC app if it was selected by the user, and persist the
// preference to launch or stay in Chrome if |should_persist| is true. Returns
// true if an app was launched, and false otherwise.
static bool MaybeLaunchOrPersistArcApp(const GURL& url,
const std::string& package_name,
bool should_launch,
bool should_persist);
// Finds |selected_app_package| from the |app_candidates| array and returns // Finds |selected_app_package| from the |app_candidates| array and returns
// the index. If the app is not found, returns |app_candidates.size()|. // the index. If the app is not found, returns |app_candidates.size()|.
...@@ -57,56 +69,65 @@ class ArcNavigationThrottle { ...@@ -57,56 +69,65 @@ class ArcNavigationThrottle {
const std::vector<mojom::IntentHandlerInfoPtr>& app_candidates); const std::vector<mojom::IntentHandlerInfoPtr>& app_candidates);
static size_t FindPreferredAppForTesting( static size_t FindPreferredAppForTesting(
const std::vector<mojom::IntentHandlerInfoPtr>& app_candidates); const std::vector<mojom::IntentHandlerInfoPtr>& app_candidates);
static void QueryArcApps(const Browser* browser,
const GURL& url,
chromeos::QueryAppsCallback callback);
// Called to launch an ARC app if it was selected by the user, and persist the ~ArcNavigationThrottle() override;
// preference to launch or stay in Chrome if |should_persist| is true. Returns
// true if an app was launched, and false otherwise.
static bool MaybeLaunchOrPersistArcApp(const GURL& url,
const std::string& package_name,
bool should_launch,
bool should_persist);
private: private:
// Determines whether we should open a preferred app or show the intent explicit ArcNavigationThrottle(content::WebContents* web_contents);
// picker. Resume/Cancel the navigation which was put in DEFER. Close the
// current tab only if we continue the navigation on ARC and the current tab // Asychronously queries ARC for apps which can handle |url|. Runs |callback|
// was explicitly generated for this navigation. // with RESUME/CANCEL for the deferred navigation and (if applicable) the list
void OnAppCandidatesReceived( // of handling apps.
content::NavigationHandle* handle, void GetArcAppsForNavigation(mojom::IntentHelperInstance* instance,
const GURL& url,
chromeos::AppsNavigationCallback callback);
// Asychronously queries ARC for apps which can handle |url|. Runs |callback|
// with the list of handling apps.
void GetArcAppsForPicker(mojom::IntentHelperInstance* instance,
const GURL& url,
chromeos::GetAppsCallback callback);
// Determines if there are apps to show the intent picker, or if we should
// open a preferred app. Runs |callback| to RESUME/CANCEL the navigation which
// was deferred prior to calling this method, and (if applicable) the list of
// apps to show in the picker. The current tab is only closed if we continue
// the navigation on ARC and the current tab was explicitly generated for this
// navigation.
void OnAppCandidatesReceivedForNavigation(
const GURL& url,
chromeos::AppsNavigationCallback callback, chromeos::AppsNavigationCallback callback,
std::vector<mojom::IntentHandlerInfoPtr> app_candidates); std::vector<mojom::IntentHandlerInfoPtr> app_candidates);
// Determines if there are apps to show the intent picker. Runs |callback|
// with the list of apps to show in the picker.
void OnAppCandidatesReceivedForPicker(
const GURL& url,
chromeos::GetAppsCallback callback,
std::vector<arc::mojom::IntentHandlerInfoPtr> app_candidates);
// Returns true if an app in |app_candidates| is preferred for handling the // Returns true if an app in |app_candidates| is preferred for handling the
// navigation represented by |handle|, and we are successfully able to launch // navigation represented by |handle|, and we are successfully able to launch
// it. // it.
bool DidLaunchPreferredArcApp( bool DidLaunchPreferredArcApp(
const GURL& url, const GURL& url,
content::WebContents* web_contents,
const std::vector<mojom::IntentHandlerInfoPtr>& app_candidates); const std::vector<mojom::IntentHandlerInfoPtr>& app_candidates);
// Queries the ArcIntentHelperBridge for ARC app icons for the apps in // Queries the ArcIntentHelperBridge for ARC app icons for the apps in
// |app_candidates|. Calls OnAppIconsReceived() when finished. // |app_candidates|. Calls OnAppIconsReceived() when finished.
void ArcAppIconQuery(const GURL& url, void GetArcAppIcons(const GURL& url,
content::WebContents* web_contents, std::vector<mojom::IntentHandlerInfoPtr> app_candidates,
std::vector<mojom::IntentHandlerInfoPtr> app_candidates, chromeos::GetAppsCallback callback);
chromeos::QueryAppsCallback callback);
static void AsyncOnAppCandidatesReceived( void OnAppIconsReceived(
const Browser* browser,
const GURL& url, const GURL& url,
chromeos::QueryAppsCallback callback,
std::vector<arc::mojom::IntentHandlerInfoPtr> app_candidates);
static void OnAppIconsReceived(
const Browser* browser,
std::vector<arc::mojom::IntentHandlerInfoPtr> app_candidates, std::vector<arc::mojom::IntentHandlerInfoPtr> app_candidates,
const GURL& url, chromeos::GetAppsCallback callback,
chromeos::QueryAppsCallback callback,
std::unique_ptr<arc::ArcIntentHelperBridge::ActivityToIconsMap> icons); std::unique_ptr<arc::ArcIntentHelperBridge::ActivityToIconsMap> icons);
// content::WebContentsObserver overrides.
void WebContentsDestroyed() override;
// This has to be the last member of the class. // This has to be the last member of the class.
base::WeakPtrFactory<ArcNavigationThrottle> weak_ptr_factory_; base::WeakPtrFactory<ArcNavigationThrottle> weak_ptr_factory_;
......
...@@ -47,8 +47,7 @@ void IntentPickerView::OnExecuting( ...@@ -47,8 +47,7 @@ void IntentPickerView::OnExecuting(
browser_->tab_strip_model()->GetActiveWebContents(); browser_->tab_strip_model()->GetActiveWebContents();
const GURL& url = chrome::GetURLToBookmark(web_contents); const GURL& url = chrome::GetURLToBookmark(web_contents);
chromeos::AppsNavigationThrottle::ShowIntentPickerBubble(browser_, chromeos::AppsNavigationThrottle::ShowIntentPickerBubble(web_contents, url);
web_contents, url);
} else { } else {
SetVisible(false); SetVisible(false);
} }
......
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