Commit 1b711a96 authored by yusukes's avatar yusukes Committed by Commit bot

Add an external protocol dialog for ARC

BUG=647485
TEST=try, manually tested the case in b:31406784

Review-Url: https://codereview.chromium.org/2348673002
Cr-Commit-Position: refs/heads/master@{#419286}
parent cca690a0
...@@ -212,6 +212,8 @@ source_set("chromeos") { ...@@ -212,6 +212,8 @@ source_set("chromeos") {
"arc/arc_downloads_watcher_service.h", "arc/arc_downloads_watcher_service.h",
"arc/arc_enterprise_reporting_service.cc", "arc/arc_enterprise_reporting_service.cc",
"arc/arc_enterprise_reporting_service.h", "arc/arc_enterprise_reporting_service.h",
"arc/arc_external_protocol_dialog.cc",
"arc/arc_external_protocol_dialog.h",
"arc/arc_navigation_throttle.cc", "arc/arc_navigation_throttle.cc",
"arc/arc_navigation_throttle.h", "arc/arc_navigation_throttle.h",
"arc/arc_optin_uma.cc", "arc/arc_optin_uma.cc",
...@@ -236,6 +238,8 @@ source_set("chromeos") { ...@@ -236,6 +238,8 @@ source_set("chromeos") {
"arc/arc_wallpaper_service.h", "arc/arc_wallpaper_service.h",
"arc/gpu_arc_video_service_host.cc", "arc/gpu_arc_video_service_host.cc",
"arc/gpu_arc_video_service_host.h", "arc/gpu_arc_video_service_host.h",
"arc/page_transition_util.cc",
"arc/page_transition_util.h",
"attestation/attestation_ca_client.cc", "attestation/attestation_ca_client.cc",
"attestation/attestation_ca_client.h", "attestation/attestation_ca_client.h",
"attestation/attestation_policy_observer.cc", "attestation/attestation_policy_observer.cc",
......
// 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.
#include "chrome/browser/chromeos/arc/arc_external_protocol_dialog.h"
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "base/bind.h"
#include "base/memory/ref_counted.h"
#include "chrome/browser/chromeos/arc/arc_navigation_throttle.h"
#include "chrome/browser/chromeos/arc/page_transition_util.h"
#include "chrome/browser/chromeos/external_protocol_dialog.h"
#include "chrome/browser/tab_contents/tab_util.h"
#include "chrome/browser/ui/browser_dialogs.h"
#include "components/arc/arc_bridge_service.h"
#include "components/arc/arc_service_manager.h"
#include "components/arc/intent_helper/activity_icon_loader.h"
#include "components/arc/intent_helper/arc_intent_helper_bridge.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/web_contents.h"
#include "ui/gfx/image/image.h"
#include "url/gurl.h"
using content::WebContents;
namespace arc {
namespace {
constexpr uint32_t kMinInstanceVersion = 3; // RequestActivityIcons' MinVersion
// Shows the Chrome OS' original external protocol dialog as a fallback.
void ShowFallbackExternalProtocolDialog(int render_process_host_id,
int routing_id,
const GURL& url) {
WebContents* web_contents =
tab_util::GetWebContentsByID(render_process_host_id, routing_id);
new ExternalProtocolDialog(web_contents, url);
}
mojom::IntentHelperInstance* GetIntentHelper() {
return ArcIntentHelperBridge::GetIntentHelperInstance(kMinInstanceVersion);
}
scoped_refptr<ActivityIconLoader> GetIconLoader() {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
ArcServiceManager* arc_service_manager = ArcServiceManager::Get();
return arc_service_manager ? arc_service_manager->icon_loader() : nullptr;
}
// Called when the dialog is closed.
void OnIntentPickerClosed(int render_process_host_id,
int routing_id,
const GURL& url,
mojo::Array<mojom::UrlHandlerInfoPtr> handlers,
size_t selected_app_index,
ArcNavigationThrottle::CloseReason close_reason) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
mojom::IntentHelperInstance* intent_helper = GetIntentHelper();
if (!intent_helper || selected_app_index >= handlers.size())
close_reason = ArcNavigationThrottle::CloseReason::ERROR;
switch (close_reason) {
case ArcNavigationThrottle::CloseReason::ALWAYS_PRESSED: {
intent_helper->AddPreferredPackage(
handlers[selected_app_index]->package_name);
// fall through.
}
case ArcNavigationThrottle::CloseReason::JUST_ONCE_PRESSED:
case ArcNavigationThrottle::CloseReason::PREFERRED_ACTIVITY_FOUND: {
// Launch the selected app.
intent_helper->HandleUrl(url.spec(),
handlers[selected_app_index]->package_name);
break;
}
case ArcNavigationThrottle::CloseReason::ERROR:
case ArcNavigationThrottle::CloseReason::INVALID: {
LOG(ERROR) << "IntentPickerBubbleView returned unexpected close_reason: "
<< static_cast<int>(close_reason);
// fall through.
}
case ArcNavigationThrottle::CloseReason::DIALOG_DEACTIVATED: {
// The user didn't select any ARC activity. Show the Chrome OS dialog.
ShowFallbackExternalProtocolDialog(render_process_host_id, routing_id,
url);
break;
}
}
}
// Called when ARC returned activity icons for the |handlers|.
void OnAppIconsReceived(
int render_process_host_id,
int routing_id,
const GURL& url,
mojo::Array<mojom::UrlHandlerInfoPtr> handlers,
std::unique_ptr<ActivityIconLoader::ActivityToIconsMap> icons) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
using NameAndIcon = std::pair<std::string, gfx::Image>;
std::vector<NameAndIcon> app_info;
for (const auto& handler : handlers) {
const ActivityIconLoader::ActivityName activity(handler->package_name,
handler->activity_name);
const auto it = icons->find(activity);
app_info.emplace_back(
handler->name, it != icons->end() ? it->second.icon20 : gfx::Image());
}
auto show_bubble_cb = base::Bind(ShowIntentPickerBubble());
WebContents* web_contents =
tab_util::GetWebContentsByID(render_process_host_id, routing_id);
show_bubble_cb.Run(web_contents, app_info,
base::Bind(OnIntentPickerClosed, render_process_host_id,
routing_id, url, base::Passed(&handlers)));
}
// Called when ARC returned a handler list for the |url|.
void OnUrlHandlerList(int render_process_host_id,
int routing_id,
const GURL& url,
mojo::Array<mojom::UrlHandlerInfoPtr> handlers) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
mojom::IntentHelperInstance* intent_helper = GetIntentHelper();
scoped_refptr<ActivityIconLoader> icon_loader = GetIconLoader();
if (!intent_helper || !icon_loader || !handlers.size()) {
// No handler is available on ARC side. Show the Chrome OS dialog.
ShowFallbackExternalProtocolDialog(render_process_host_id, routing_id, url);
return;
}
if (handlers.size() == 1) {
// Special case. When ARC has only one activity for the |url|, silently
// launch the app to be consistent with Android's behavior. No UI needed
// in this case.
intent_helper->HandleUrl(url.spec(), handlers[0]->package_name);
return;
}
// Otherwise, retrieve icons of the activities.
std::vector<ActivityIconLoader::ActivityName> activities;
for (const auto& handler : handlers) {
activities.emplace_back(handler->package_name, handler->activity_name);
}
icon_loader->GetActivityIcons(
activities, base::Bind(OnAppIconsReceived, render_process_host_id,
routing_id, url, base::Passed(&handlers)));
}
} // namespace
bool RunArcExternalProtocolDialog(const GURL& url,
int render_process_host_id,
int routing_id,
ui::PageTransition page_transition,
bool has_user_gesture) {
if (ShouldIgnoreNavigation(page_transition))
return false;
mojom::IntentHelperInstance* intent_helper = GetIntentHelper();
if (!intent_helper)
return false; // ARC is either not supported or not yet ready.
WebContents* web_contents =
tab_util::GetWebContentsByID(render_process_host_id, routing_id);
if (!web_contents || !web_contents->GetBrowserContext() ||
web_contents->GetBrowserContext()->IsOffTheRecord()) {
return false;
}
// Show ARC version of the dialog, which is IntentPickerBubbleView. To show
// the bubble view, we need to ask ARC for a handler list first.
intent_helper->RequestUrlHandlerList(
url.spec(),
base::Bind(OnUrlHandlerList, render_process_host_id, routing_id, url));
return true;
}
} // namespace arc
// 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.
#ifndef CHROME_BROWSER_CHROMEOS_ARC_ARC_EXTERNAL_PROTOCOL_DIALOG_H_
#define CHROME_BROWSER_CHROMEOS_ARC_ARC_EXTERNAL_PROTOCOL_DIALOG_H_
#include "ui/base/page_transition_types.h"
class GURL;
namespace arc {
// Shows ARC version of the dialog. Returns true if ARC is supported, running,
// and in a context where it is allowed to handle external protocol.
bool RunArcExternalProtocolDialog(const GURL& url,
int render_process_host_id,
int routing_id,
ui::PageTransition page_transition,
bool has_user_gesture);
} // namespace arc
#endif // CHROME_BROWSER_CHROMEOS_ARC_ARC_EXTERNAL_PROTOCOL_DIALOG_H_
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
#include "base/logging.h" #include "base/logging.h"
#include "base/memory/ref_counted.h" #include "base/memory/ref_counted.h"
#include "base/metrics/histogram_macros.h" #include "base/metrics/histogram_macros.h"
#include "chrome/browser/chromeos/arc/page_transition_util.h"
#include "components/arc/arc_bridge_service.h" #include "components/arc/arc_bridge_service.h"
#include "components/arc/arc_service_manager.h" #include "components/arc/arc_service_manager.h"
#include "components/arc/intent_helper/arc_intent_helper_bridge.h" #include "components/arc/intent_helper/arc_intent_helper_bridge.h"
...@@ -78,25 +79,8 @@ content::NavigationThrottle::ThrottleCheckResult ...@@ -78,25 +79,8 @@ content::NavigationThrottle::ThrottleCheckResult
ArcNavigationThrottle::HandleRequest() { ArcNavigationThrottle::HandleRequest() {
const GURL& url = navigation_handle()->GetURL(); const GURL& url = navigation_handle()->GetURL();
// Mask out any redirect qualifiers - this method handles navigation from if (ShouldIgnoreNavigation(navigation_handle()->GetPageTransition()))
// redirect and non-redirect navigations equivalently.
const ui::PageTransition transition =
ui::PageTransitionFromInt(navigation_handle()->GetPageTransition() &
~ui::PAGE_TRANSITION_IS_REDIRECT_MASK);
if (!ui::PageTransitionCoreTypeIs(transition, ui::PAGE_TRANSITION_LINK)) {
// Allow navigation to proceed if this event wasn't spawned by the user
// clicking on a link.
return content::NavigationThrottle::PROCEED; return content::NavigationThrottle::PROCEED;
}
if (ui::PageTransitionGetQualifier(transition) != 0) {
// Qualifiers indicate that this navigation was the result of a click on a
// forward/back button, or typing in the URL bar, etc. Don't pass any of
// those types of navigations to the intent helper (see crbug.com/630072).
// Note that redirects, which we do pass on, are masked out above.
return content::NavigationThrottle::PROCEED;
}
if (!ShouldOverrideUrlLoading(navigation_handle())) if (!ShouldOverrideUrlLoading(navigation_handle()))
return content::NavigationThrottle::PROCEED; return content::NavigationThrottle::PROCEED;
......
// 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.
#include "chrome/browser/chromeos/arc/page_transition_util.h"
namespace arc {
bool ShouldIgnoreNavigation(ui::PageTransition page_transition) {
// Mask out any redirect qualifiers - this method handles navigation from
// redirect and non-redirect navigations equivalently.
page_transition = ui::PageTransitionFromInt(
page_transition & ~ui::PAGE_TRANSITION_IS_REDIRECT_MASK);
if (!ui::PageTransitionCoreTypeIs(page_transition,
ui::PAGE_TRANSITION_LINK)) {
// Do not handle the |url| if this event wasn't spawned by the user clicking
// on a link.
return true;
}
if (ui::PageTransitionGetQualifier(page_transition) != 0) {
// Qualifiers indicate that this navigation was the result of a click on a
// forward/back button, or typing in the URL bar, etc. Don't handle any of
// those types of navigations.
return true;
}
return false;
}
} // namespace arc
// 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.
#ifndef CHROME_BROWSER_CHROMEOS_ARC_PAGE_TRANSITION_UTIL_H_
#define CHROME_BROWSER_CHROMEOS_ARC_PAGE_TRANSITION_UTIL_H_
#include "ui/base/page_transition_types.h"
namespace arc {
// Returns true if ARC should ignore the navigation with the |page_transition|.
bool ShouldIgnoreNavigation(ui::PageTransition page_transition);
} // namespace arc
#endif // CHROME_BROWSER_CHROMEOS_ARC_PAGE_TRANSITION_UTIL_H_
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
#include "base/metrics/histogram_macros.h" #include "base/metrics/histogram_macros.h"
#include "base/strings/string_util.h" #include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h" #include "base/strings/utf_string_conversions.h"
#include "chrome/browser/chromeos/arc/arc_external_protocol_dialog.h"
#include "chrome/browser/external_protocol/external_protocol_handler.h" #include "chrome/browser/external_protocol/external_protocol_handler.h"
#include "chrome/browser/tab_contents/tab_util.h" #include "chrome/browser/tab_contents/tab_util.h"
#include "chrome/grit/chromium_strings.h" #include "chrome/grit/chromium_strings.h"
...@@ -36,6 +37,12 @@ void ExternalProtocolHandler::RunExternalProtocolDialog( ...@@ -36,6 +37,12 @@ void ExternalProtocolHandler::RunExternalProtocolDialog(
int routing_id, int routing_id,
ui::PageTransition page_transition, ui::PageTransition page_transition,
bool has_user_gesture) { bool has_user_gesture) {
// First, check if ARC version of the dialog is available and run ARC version
// when possible.
if (arc::RunArcExternalProtocolDialog(url, render_process_host_id, routing_id,
page_transition, has_user_gesture)) {
return;
}
WebContents* web_contents = tab_util::GetWebContentsByID( WebContents* web_contents = tab_util::GetWebContentsByID(
render_process_host_id, routing_id); render_process_host_id, routing_id);
new ExternalProtocolDialog(web_contents, url); new ExternalProtocolDialog(web_contents, url);
......
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