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

Unified Diff: chrome/browser/chromeos/arc/arc_external_protocol_dialog.cc

Issue 2432013002: Improve intent: URI handling (Closed)
Patch Set: review Created 4 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: chrome/browser/chromeos/arc/arc_external_protocol_dialog.cc
diff --git a/chrome/browser/chromeos/arc/arc_external_protocol_dialog.cc b/chrome/browser/chromeos/arc/arc_external_protocol_dialog.cc
index 35157fd48b9e967d486ac1c389132da9e8239b80..50c2cfa2ad05515fcea9b5ec543ca8696b54f4d1 100644
--- a/chrome/browser/chromeos/arc/arc_external_protocol_dialog.cc
+++ b/chrome/browser/chromeos/arc/arc_external_protocol_dialog.cc
@@ -5,7 +5,6 @@
#include "chrome/browser/chromeos/arc/arc_external_protocol_dialog.h"
#include <memory>
-#include <string>
#include <utility>
#include <vector>
@@ -23,7 +22,11 @@
#include "components/arc/intent_helper/page_transition_util.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/page_navigator.h"
#include "content/public/browser/web_contents.h"
+#include "content/public/common/referrer.h"
+#include "ui/base/page_transition_types.h"
+#include "ui/base/window_open_disposition.h"
#include "ui/gfx/image/image.h"
#include "url/gurl.h"
@@ -65,6 +68,161 @@ void CloseTabIfNeeded(int render_process_host_id, int routing_id) {
web_contents->Close();
}
+// Shows |url| in the current tab.
+void OpenUrlInChrome(int render_process_host_id,
+ int routing_id,
+ const GURL& url) {
+ WebContents* web_contents =
+ tab_util::GetWebContentsByID(render_process_host_id, routing_id);
+ if (!web_contents)
+ return;
+
+ // Use the PAGE_TRANSITION_FROM_API qualifier so that this nativation won't
+ // end up showing the disambig dialog.
+ const ui::PageTransition page_transition_type = ui::PageTransitionFromInt(
+ ui::PAGE_TRANSITION_LINK | ui::PAGE_TRANSITION_FROM_API);
+ constexpr bool kIsRendererInitiated = false;
+ const content::OpenURLParams params(
+ url, content::Referrer(), WindowOpenDisposition::CURRENT_TAB,
djacobo_ 2016/10/24 18:17:36 qq: does using content::Referrer() in here means w
Yusuke Sato 2016/10/24 19:30:11 Let me make this a TODO. Sending a non-empty refer
djacobo_ 2016/10/24 20:23:47 Sounds fine to me, thanks.
+ page_transition_type, kIsRendererInitiated);
+ web_contents->OpenURL(params);
+}
+
+// Sends |url| to ARC.
+void HandleUrlInArc(int render_process_host_id,
+ int routing_id,
+ const std::pair<GURL, std::string>& url_and_package) {
+ auto* instance = ArcIntentHelperBridge::GetIntentHelperInstance(
+ "HandleUrl", kMinVersionForHandleUrl);
+ if (!instance)
+ return;
+
+ instance->HandleUrl(url_and_package.first.spec(), url_and_package.second);
+ CloseTabIfNeeded(render_process_host_id, routing_id);
+}
+
+// Finds |selected_app_package| from the |handlers| array and returns the index.
+// If the app is not found, returns |handlers.size()|.
+size_t GetAppIndex(const mojo::Array<mojom::IntentHandlerInfoPtr>& handlers,
+ const std::string& selected_app_package) {
+ // If the user selected an app to continue the navigation, confirm that the
+ // |package_name| matches a valid option and return the index.
djacobo_ 2016/10/24 18:17:36 nit: kind of duplicated comment? I would keep the
Yusuke Sato 2016/10/24 19:30:11 Thanks, this is a copy-and-paste mistake. Moved th
+ for (size_t i = 0; i < handlers.size(); ++i) {
+ if (handlers[i]->package_name == selected_app_package)
+ return i;
+ }
+ return handlers.size();
+}
+
+// A helper function called by GetAction().
+GetActionResult GetActionInternal(
+ const GURL& original_url,
+ const mojom::IntentHandlerInfoPtr& handler,
+ std::pair<GURL, std::string>* out_url_and_package) {
+ if (!handler->fallback_url.is_null()) {
+ *out_url_and_package = std::make_pair(GURL(handler->fallback_url.get()),
+ handler->package_name.get());
+ if (ArcIntentHelperBridge::IsIntentHelperPackage(handler->package_name)) {
+ // Since |package_name| is "Chrome", and |fallback_url| is not null, the
+ // URL must be either http or https. Check it just in case, and if not,
+ // fallback to HANDLE_URL_IN_ARC;
+ if (out_url_and_package->first.SchemeIsHTTPOrHTTPS())
+ return GetActionResult::OPEN_URL_IN_CHROME;
+
+ LOG(WARNING) << "Failed to handle " << out_url_and_package->first
+ << " in Chrome. Falling back to ARC...";
+ }
+ // |fallback_url| which Chrome doesn't support is passed (e.g. market:).
+ return GetActionResult::HANDLE_URL_IN_ARC;
+ }
+
+ // Unlike |handler->fallback_url|, the |original_url| should always be handled
+ // in ARC since it's external to Chrome.
+ *out_url_and_package =
+ std::make_pair(original_url, handler->package_name.get());
+ return GetActionResult::HANDLE_URL_IN_ARC;
+}
+
+// Gets an action that should be done when ARC has the |handlers| for the
+// |original_url| and the user selects |selected_app_index|. When the user
+// hasn't selected any app, |selected_app_index| must be set to
+// |handlers.size()|.
+//
+// When the returned action is either OPEN_URL_IN_CHROME or HANDLE_URL_IN_ARC,
+// |out_url_and_package| is filled accordingly.
+GetActionResult GetAction(
+ const GURL& original_url,
+ const mojo::Array<mojom::IntentHandlerInfoPtr>& handlers,
+ size_t selected_app_index,
+ std::pair<GURL, std::string>* out_url_and_package) {
+ DCHECK(out_url_and_package);
+ if (!handlers.size())
+ return GetActionResult::SHOW_CHROME_OS_DIALOG; // no apps found.
+
+ if (selected_app_index == handlers.size()) {
+ // The user hasn't made the selection yet.
+
+ // If |handlers| has only one element and its package is "Chrome", open
+ // the fallback URL in the current tab without showing the dialog.
+ if (handlers.size() == 1) {
+ if (GetActionInternal(original_url, handlers[0], out_url_and_package) ==
+ GetActionResult::OPEN_URL_IN_CHROME) {
+ return GetActionResult::OPEN_URL_IN_CHROME;
+ }
+ }
+
+ // If one of the apps is marked as preferred, use it right away without
+ // showing the UI. |is_preferred| will never be true unless the user
+ // explicitly makes it the default with the "always" button.
+ for (size_t i = 0; i < handlers.size(); ++i) {
+ const mojom::IntentHandlerInfoPtr& handler = handlers[i];
+ if (!handler->is_preferred)
+ continue;
+ // A preferred activity is found. Decide how to open it, either in Chrome
+ // or ARC.
+ return GetActionInternal(original_url, handler, out_url_and_package);
+ }
+ // Ask the user to pick one.
+ return GetActionResult::ASK_USER;
+ }
+
+ // The user has already made the selection. Decide how to open it, either in
+ // Chrome or ARC.
+ return GetActionInternal(original_url, handlers[selected_app_index],
+ out_url_and_package);
+}
+
+// Handles |url| if possible. Returns true if it is actually handled.
+bool HandleUrl(int render_process_host_id,
+ int routing_id,
+ const GURL& url,
+ const mojo::Array<mojom::IntentHandlerInfoPtr>& handlers,
+ size_t selected_app_index,
+ GetActionResult* out_result) {
+ std::pair<GURL, std::string> url_and_package;
+ const GetActionResult result =
+ GetAction(url, handlers, selected_app_index, &url_and_package);
+ if (out_result)
+ *out_result = result;
+
+ switch (result) {
+ case GetActionResult::SHOW_CHROME_OS_DIALOG:
+ ShowFallbackExternalProtocolDialog(render_process_host_id, routing_id,
+ url);
+ return true;
+ case GetActionResult::OPEN_URL_IN_CHROME:
+ OpenUrlInChrome(render_process_host_id, routing_id,
+ url_and_package.first);
+ return true;
+ case GetActionResult::HANDLE_URL_IN_ARC:
+ HandleUrlInArc(render_process_host_id, routing_id, url_and_package);
+ return true;
+ case GetActionResult::ASK_USER:
+ break;
+ }
+ return false;
+}
+
// Called when the dialog is closed.
void OnIntentPickerClosed(int render_process_host_id,
int routing_id,
@@ -74,7 +232,7 @@ void OnIntentPickerClosed(int render_process_host_id,
ArcNavigationThrottle::CloseReason close_reason) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
- size_t selected_app_index = handlers.size();
+ const size_t selected_app_index = GetAppIndex(handlers, selected_app_package);
// Make sure that the instance at least supports HandleUrl.
auto* instance = ArcIntentHelperBridge::GetIntentHelperInstance(
"HandleUrl", kMinVersionForHandleUrl);
@@ -84,15 +242,6 @@ void OnIntentPickerClosed(int render_process_host_id,
ArcNavigationThrottle::CloseReason::JUST_ONCE_PRESSED ||
close_reason ==
ArcNavigationThrottle::CloseReason::ALWAYS_PRESSED) {
- // If the user selected an app to continue the navigation, confirm that the
- // |package_name| matches a valid option and return the index.
- for (size_t i = 0; i < handlers.size(); ++i) {
- if (handlers[i]->package_name == selected_app_package) {
- selected_app_index = i;
- break;
- }
- }
-
if (selected_app_index == handlers.size())
close_reason = ArcNavigationThrottle::CloseReason::ERROR;
}
@@ -108,9 +257,8 @@ void OnIntentPickerClosed(int render_process_host_id,
}
case ArcNavigationThrottle::CloseReason::JUST_ONCE_PRESSED: {
// Launch the selected app.
- instance->HandleUrl(url.spec(),
- handlers[selected_app_index]->package_name);
- CloseTabIfNeeded(render_process_host_id, routing_id);
+ HandleUrl(render_process_host_id, routing_id, url, handlers,
+ selected_app_index, nullptr);
break;
}
case ArcNavigationThrottle::CloseReason::PREFERRED_ACTIVITY_FOUND:
@@ -175,22 +323,19 @@ void OnUrlHandlerList(int render_process_host_id,
"HandleUrl", kMinVersionForHandleUrl);
scoped_refptr<ActivityIconLoader> icon_loader = GetIconLoader();
- if (!instance || !icon_loader || !handlers.size()) {
- // No handler is available on ARC side. Show the Chrome OS dialog.
+ if (!instance || !icon_loader) {
+ // ARC is not running anymore. Show the Chrome OS dialog.
ShowFallbackExternalProtocolDialog(render_process_host_id, routing_id, url);
return;
}
- // If one of the apps is marked as preferred, use it right away without
- // showing the UI. |is_preferred| will never be true unless the user
- // explicitly makes it the default with the "always" button.
- for (size_t i = 0; i < handlers.size(); ++i) {
- if (!handlers[i]->is_preferred)
- continue;
- instance->HandleUrl(url.spec(), handlers[i]->package_name);
- CloseTabIfNeeded(render_process_host_id, routing_id);
- RecordUma(ArcNavigationThrottle::CloseReason::PREFERRED_ACTIVITY_FOUND);
- return;
+ // Check if the |url| should be handled right away without showing the UI.
+ GetActionResult result;
+ if (HandleUrl(render_process_host_id, routing_id, url, handlers,
+ handlers.size(), &result)) {
+ if (result == GetActionResult::HANDLE_URL_IN_ARC)
+ RecordUma(ArcNavigationThrottle::CloseReason::PREFERRED_ACTIVITY_FOUND);
+ return; // the |url| has been handled.
}
// Otherwise, retrieve icons of the activities.
@@ -210,6 +355,9 @@ bool RunArcExternalProtocolDialog(const GURL& url,
int routing_id,
ui::PageTransition page_transition,
bool has_user_gesture) {
+ // This function is for external protocols that Chrome cannot handle.
+ DCHECK(!url.SchemeIsHTTPOrHTTPS()) << url;
+
// Try to forward <form> submissions to ARC when possible.
constexpr bool kAllowFormSubmit = true;
if (ShouldIgnoreNavigation(page_transition, kAllowFormSubmit))
@@ -235,4 +383,19 @@ bool RunArcExternalProtocolDialog(const GURL& url,
return true;
}
+size_t GetAppIndexForTesting(
+ const mojo::Array<mojom::IntentHandlerInfoPtr>& handlers,
+ const std::string& selected_app_package) {
+ return GetAppIndex(handlers, selected_app_package);
+}
+
+GetActionResult GetActionForTesting(
+ const GURL& original_url,
+ const mojo::Array<mojom::IntentHandlerInfoPtr>& handlers,
+ size_t selected_app_index,
+ std::pair<GURL, std::string>* out_url_and_package) {
+ return GetAction(original_url, handlers, selected_app_index,
+ out_url_and_package);
+}
+
} // namespace arc

Powered by Google App Engine
This is Rietveld 408576698