blob: d4de2f797eb30f4d8ea47ac9cd93bb45865b39c9 [file] [log] [blame]
// 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.
#ifndef CHROME_BROWSER_APPS_INTENT_HELPER_APPS_NAVIGATION_THROTTLE_H_
#define CHROME_BROWSER_APPS_INTENT_HELPER_APPS_NAVIGATION_THROTTLE_H_
#include <memory>
#include <string>
#include <vector>
#include "base/callback_forward.h"
#include "base/gtest_prod_util.h"
#include "base/macros.h"
#include "base/memory/weak_ptr.h"
#include "chrome/browser/apps/intent_helper/apps_navigation_types.h"
#include "chrome/services/app_service/public/mojom/types.mojom.h"
#include "content/public/browser/navigation_throttle.h"
#include "url/gurl.h"
namespace content {
class NavigationHandle;
class WebContents;
} // namespace content
namespace chromeos {
FORWARD_DECLARE_TEST(ChromeOsAppsNavigationThrottleTest,
TestGetDestinationPlatform);
} // namespace chromeos
class IntentPickerAutoDisplayService;
namespace apps {
// Allows navigation to be routed to an installed app on Chrome OS, and provides
// a static method for showing an intent picker for the current URL to display
// any handling apps.
class AppsNavigationThrottle : public content::NavigationThrottle {
public:
// Restricts the amount of apps displayed to the user without the need of a
// ScrollView.
enum { kMaxAppResults = 3 };
// Possibly creates a navigation throttle that checks if any installed apps
// can handle the URL being navigated to. The user is prompted if they wish to
// open the app or remain in the browser.
static std::unique_ptr<content::NavigationThrottle> MaybeCreate(
content::NavigationHandle* handle);
// Queries for installed apps which can handle |url|, and displays the intent
// picker bubble for |web_contents|.
static void ShowIntentPickerBubble(
content::WebContents* web_contents,
IntentPickerAutoDisplayService* ui_auto_display_service,
const GURL& url);
// Called when the intent picker is closed for |url|, in |web_contents|, with
// |launch_name| as the (possibly empty) action to be triggered based on
// |entry_type|. |close_reason| gives the reason for the picker being closed,
// and |should_persist| is true if the user indicated they wish to remember
// the choice made. |ui_auto_display_service| keeps track of whether or not
// the user dismissed the ui without engaging with it.
static void OnIntentPickerClosed(
content::WebContents* web_contents,
IntentPickerAutoDisplayService* ui_auto_display_service,
const GURL& url,
const std::string& launch_name,
PickerEntryType entry_type,
IntentPickerCloseReason close_reason,
bool should_persist);
static bool IsGoogleRedirectorUrlForTesting(const GURL& url);
static bool ShouldOverrideUrlLoadingForTesting(const GURL& previous_url,
const GURL& current_url);
static void ShowIntentPickerBubbleForApps(
content::WebContents* web_contents,
std::vector<IntentPickerAppInfo> apps,
bool show_stay_in_chrome,
bool show_remember_selection,
IntentPickerResponse callback);
explicit AppsNavigationThrottle(content::NavigationHandle* navigation_handle);
~AppsNavigationThrottle() override;
// content::NavigationHandle overrides
const char* GetNameForLogging() override;
content::NavigationThrottle::ThrottleCheckResult WillStartRequest() override;
content::NavigationThrottle::ThrottleCheckResult WillRedirectRequest()
override;
// These enums are used to define the buckets for an enumerated UMA histogram
// and need to be synced with the ArcIntentHandlerAction enum in enums.xml.
// This enum class should also be treated as append-only.
enum class PickerAction : int {
// Picker errors occurring after the picker is shown.
ERROR_AFTER_PICKER = 0,
// DIALOG_DEACTIVATED keeps track of the user dismissing the UI via clicking
// the close button or clicking outside of the IntentPickerBubbleView
// surface. As with CHROME_PRESSED, the user stays in Chrome, however we
// keep both options since CHROME_PRESSED is tied to an explicit intent of
// staying in Chrome, not only just getting rid of the
// IntentPickerBubbleView UI.
DIALOG_DEACTIVATED = 1,
OBSOLETE_ALWAYS_PRESSED = 2,
OBSOLETE_JUST_ONCE_PRESSED = 3,
PREFERRED_ACTIVITY_FOUND = 4,
// The prefix "CHROME"/"ARC_APP"/"PWA_APP" determines whether the user
// pressed [Stay in Chrome] or [Use app] at IntentPickerBubbleView.
// "PREFERRED" denotes when the user decides to save this selection, whether
// an app or Chrome was selected.
CHROME_PRESSED = 5,
CHROME_PREFERRED_PRESSED = 6,
ARC_APP_PRESSED = 7,
ARC_APP_PREFERRED_PRESSED = 8,
PWA_APP_PRESSED = 9,
// Picker errors occurring before the picker is shown.
ERROR_BEFORE_PICKER = 10,
INVALID = 11,
DEVICE_PRESSED = 12,
MAC_NATIVE_APP_PRESSED = 13,
kMaxValue = MAC_NATIVE_APP_PRESSED,
};
// As for PickerAction, these define the buckets for an UMA histogram, so this
// must be treated in an append-only fashion. This helps specify where a
// navigation will continue. Must be kept in sync with the
// ArcIntentHandlerDestinationPlatform enum in enums.xml.
enum class Platform : int {
ARC = 0,
CHROME = 1,
PWA = 2,
DEVICE = 3,
MAC_NATIVE = 4,
kMaxValue = MAC_NATIVE,
};
// TODO(ajlinker): move these two functions below to IntentHandlingMetrics.
// Determines the destination of the current navigation. We know that if the
// |picker_action| is either ERROR or DIALOG_DEACTIVATED the navigation MUST
// stay in Chrome, and when |picker_action| is PWA_APP_PRESSED the navigation
// goes to a PWA. Otherwise we can assume the navigation goes to ARC with the
// exception of the |selected_launch_name| being Chrome.
static Platform GetDestinationPlatform(
const std::string& selected_launch_name,
PickerAction picker_action);
// Converts the provided |entry_type|, |close_reason| and |should_persist|
// boolean to a PickerAction value for recording in UMA.
static PickerAction GetPickerAction(PickerEntryType entry_type,
IntentPickerCloseReason close_reason,
bool should_persist);
protected:
// These enums are used to define the intent picker show state, whether the
// picker is popped out or just displayed as a clickable omnibox icon.
enum class PickerShowState {
kOmnibox = 1, // Only show the intent icon in the omnibox
kPopOut = 2, // show the intent picker icon and pop out bubble
};
// Checks whether we can create the apps_navigation_throttle.
static bool CanCreate(content::WebContents* web_contents);
// This is a wrapper method for querying apps for a URL. Normally this
// method will simply querying PWAs that can handle the URL from. If we are
// using App Service Intent Handling to support intent picker (currently not
// feature complete), this method will query all types of apps that that could
// handle the URL from CommonAppsNavigationThrottle.
virtual std::vector<IntentPickerAppInfo> FindAppsForUrl(
content::WebContents* web_contents,
const GURL& url,
std::vector<IntentPickerAppInfo> apps);
// If an installed PWA exists that can handle |url|, prepends it to |apps| and
// returns the new list.
static std::vector<IntentPickerAppInfo> FindPwaForUrl(
content::WebContents* web_contents,
const GURL& url,
std::vector<IntentPickerAppInfo> apps);
static void CloseOrGoBack(content::WebContents* web_contents);
static bool ContainsOnlyPwasAndMacApps(
const std::vector<apps::IntentPickerAppInfo>& apps);
static bool ShouldShowPersistenceOptions(
std::vector<apps::IntentPickerAppInfo>& apps);
// Overrides for Chrome OS to allow ARC handling.
virtual void MaybeRemoveComingFromArcFlag(content::WebContents* web_contents,
const GURL& previous_url,
const GURL& current_url) {}
virtual bool ShouldDeferNavigation(content::NavigationHandle* handle);
void ShowIntentPickerForApps(
content::WebContents* web_contents,
IntentPickerAutoDisplayService* ui_auto_display_service,
const GURL& url,
std::vector<IntentPickerAppInfo> apps,
IntentPickerResponse callback);
virtual PickerShowState GetPickerShowState(
const std::vector<IntentPickerAppInfo>& apps_for_picker,
content::WebContents* web_contents,
const GURL& url);
virtual IntentPickerResponse GetOnPickerClosedCallback(
content::WebContents* web_contents,
IntentPickerAutoDisplayService* ui_auto_display_service,
const GURL& url);
bool navigate_from_link();
// Keeps track of whether we already shown the UI or preferred app. Since
// AppsNavigationThrottle cannot wait for the user (due to the non-blocking
// nature of the feature) the best we can do is check if we launched a
// preferred app or asked the UI to be shown, this flag ensures we never
// trigger the UI twice for the same throttle.
bool ui_displayed_;
// Points to the service in charge of controlling auto-display for the related
// UI.
IntentPickerAutoDisplayService* ui_auto_display_service_;
private:
FRIEND_TEST_ALL_PREFIXES(AppsNavigationThrottleTest, TestGetPickerAction);
FRIEND_TEST_ALL_PREFIXES(AppsNavigationThrottleTest,
TestGetDestinationPlatform);
FRIEND_TEST_ALL_PREFIXES(chromeos::ChromeOsAppsNavigationThrottleTest,
TestGetDestinationPlatform);
content::NavigationThrottle::ThrottleCheckResult HandleRequest();
// A reference to the starting GURL.
GURL starting_url_;
// Keeps track of whether the navigation is coming from a link or not. If the
// navigation is not from a link, we will not show the pop up for the intent
// picker bubble.
bool navigate_from_link_;
DISALLOW_COPY_AND_ASSIGN(AppsNavigationThrottle);
};
} // namespace apps
#endif // CHROME_BROWSER_APPS_INTENT_HELPER_APPS_NAVIGATION_THROTTLE_H_