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

Side by Side Diff: chrome/browser/chromeos/arc/intent_helper/arc_external_protocol_dialog.cc

Issue 2432013002: Improve intent: URI handling (Closed)
Patch Set: rebase onto Luis' directory reorganize CL Created 4 years, 1 month 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 unified diff | Download patch
OLDNEW
1 // Copyright 2016 The Chromium Authors. All rights reserved. 1 // Copyright 2016 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "chrome/browser/chromeos/arc/intent_helper/arc_external_protocol_dialog .h" 5 #include "chrome/browser/chromeos/arc/intent_helper/arc_external_protocol_dialog .h"
6 6
7 #include <memory> 7 #include <memory>
8 #include <string>
9 #include <utility> 8 #include <utility>
10 #include <vector> 9 #include <vector>
11 10
12 #include "base/bind.h" 11 #include "base/bind.h"
13 #include "base/memory/ref_counted.h" 12 #include "base/memory/ref_counted.h"
14 #include "base/metrics/histogram_macros.h" 13 #include "base/metrics/histogram_macros.h"
15 #include "chrome/browser/chromeos/arc/intent_helper/arc_navigation_throttle.h" 14 #include "chrome/browser/chromeos/arc/intent_helper/arc_navigation_throttle.h"
16 #include "chrome/browser/chromeos/external_protocol_dialog.h" 15 #include "chrome/browser/chromeos/external_protocol_dialog.h"
17 #include "chrome/browser/tab_contents/tab_util.h" 16 #include "chrome/browser/tab_contents/tab_util.h"
18 #include "chrome/browser/ui/browser_dialogs.h" 17 #include "chrome/browser/ui/browser_dialogs.h"
19 #include "components/arc/arc_bridge_service.h" 18 #include "components/arc/arc_bridge_service.h"
20 #include "components/arc/arc_service_manager.h" 19 #include "components/arc/arc_service_manager.h"
21 #include "components/arc/intent_helper/activity_icon_loader.h" 20 #include "components/arc/intent_helper/activity_icon_loader.h"
22 #include "components/arc/intent_helper/arc_intent_helper_bridge.h" 21 #include "components/arc/intent_helper/arc_intent_helper_bridge.h"
23 #include "components/arc/intent_helper/page_transition_util.h" 22 #include "components/arc/intent_helper/page_transition_util.h"
24 #include "content/public/browser/browser_context.h" 23 #include "content/public/browser/browser_context.h"
25 #include "content/public/browser/browser_thread.h" 24 #include "content/public/browser/browser_thread.h"
25 #include "content/public/browser/page_navigator.h"
26 #include "content/public/browser/web_contents.h" 26 #include "content/public/browser/web_contents.h"
27 #include "content/public/common/referrer.h"
28 #include "ui/base/page_transition_types.h"
29 #include "ui/base/window_open_disposition.h"
27 #include "ui/gfx/image/image.h" 30 #include "ui/gfx/image/image.h"
28 #include "url/gurl.h" 31 #include "url/gurl.h"
29 32
30 using content::WebContents; 33 using content::WebContents;
31 34
32 namespace arc { 35 namespace arc {
33 36
34 namespace { 37 namespace {
35 38
36 constexpr uint32_t kMinVersionForHandleUrl = 2; 39 constexpr uint32_t kMinVersionForHandleUrl = 2;
(...skipping 21 matching lines...) Expand all
58 return arc_service_manager ? arc_service_manager->icon_loader() : nullptr; 61 return arc_service_manager ? arc_service_manager->icon_loader() : nullptr;
59 } 62 }
60 63
61 void CloseTabIfNeeded(int render_process_host_id, int routing_id) { 64 void CloseTabIfNeeded(int render_process_host_id, int routing_id) {
62 WebContents* web_contents = 65 WebContents* web_contents =
63 tab_util::GetWebContentsByID(render_process_host_id, routing_id); 66 tab_util::GetWebContentsByID(render_process_host_id, routing_id);
64 if (web_contents && web_contents->GetController().IsInitialNavigation()) 67 if (web_contents && web_contents->GetController().IsInitialNavigation())
65 web_contents->Close(); 68 web_contents->Close();
66 } 69 }
67 70
71 // Shows |url| in the current tab.
72 void OpenUrlInChrome(int render_process_host_id,
73 int routing_id,
74 const GURL& url) {
75 WebContents* web_contents =
76 tab_util::GetWebContentsByID(render_process_host_id, routing_id);
77 if (!web_contents)
78 return;
79
80 // Use the PAGE_TRANSITION_FROM_API qualifier so that this nativation won't
81 // end up showing the disambig dialog.
82 const ui::PageTransition page_transition_type = ui::PageTransitionFromInt(
83 ui::PAGE_TRANSITION_LINK | ui::PAGE_TRANSITION_FROM_API);
84 constexpr bool kIsRendererInitiated = false;
85 const content::OpenURLParams params(
86 // TODO(yusukes): Send a non-empty referrer.
87 url, content::Referrer(), WindowOpenDisposition::CURRENT_TAB,
88 page_transition_type, kIsRendererInitiated);
89 web_contents->OpenURL(params);
90 }
91
92 // Sends |url| to ARC.
93 void HandleUrlInArc(int render_process_host_id,
94 int routing_id,
95 const std::pair<GURL, std::string>& url_and_package) {
96 auto* instance = ArcIntentHelperBridge::GetIntentHelperInstance(
97 "HandleUrl", kMinVersionForHandleUrl);
98 if (!instance)
99 return;
100
101 instance->HandleUrl(url_and_package.first.spec(), url_and_package.second);
102 CloseTabIfNeeded(render_process_host_id, routing_id);
103 }
104
105 // A helper function called by GetAction().
106 GetActionResult GetActionInternal(
107 const GURL& original_url,
108 const mojom::IntentHandlerInfoPtr& handler,
109 std::pair<GURL, std::string>* out_url_and_package) {
110 if (!handler->fallback_url.is_null()) {
111 *out_url_and_package = std::make_pair(GURL(handler->fallback_url.get()),
112 handler->package_name.get());
113 if (ArcIntentHelperBridge::IsIntentHelperPackage(handler->package_name)) {
114 // Since |package_name| is "Chrome", and |fallback_url| is not null, the
115 // URL must be either http or https. Check it just in case, and if not,
116 // fallback to HANDLE_URL_IN_ARC;
117 if (out_url_and_package->first.SchemeIsHTTPOrHTTPS())
118 return GetActionResult::OPEN_URL_IN_CHROME;
119
120 LOG(WARNING) << "Failed to handle " << out_url_and_package->first
121 << " in Chrome. Falling back to ARC...";
122 }
123 // |fallback_url| which Chrome doesn't support is passed (e.g. market:).
124 return GetActionResult::HANDLE_URL_IN_ARC;
125 }
126
127 // Unlike |handler->fallback_url|, the |original_url| should always be handled
128 // in ARC since it's external to Chrome.
129 *out_url_and_package =
130 std::make_pair(original_url, handler->package_name.get());
131 return GetActionResult::HANDLE_URL_IN_ARC;
132 }
133
134 // Gets an action that should be done when ARC has the |handlers| for the
135 // |original_url| and the user selects |selected_app_index|. When the user
136 // hasn't selected any app, |selected_app_index| must be set to
137 // |handlers.size()|.
138 //
139 // When the returned action is either OPEN_URL_IN_CHROME or HANDLE_URL_IN_ARC,
140 // |out_url_and_package| is filled accordingly.
141 GetActionResult GetAction(
142 const GURL& original_url,
143 const mojo::Array<mojom::IntentHandlerInfoPtr>& handlers,
144 size_t selected_app_index,
145 std::pair<GURL, std::string>* out_url_and_package) {
146 DCHECK(out_url_and_package);
147 if (!handlers.size())
148 return GetActionResult::SHOW_CHROME_OS_DIALOG; // no apps found.
149
150 if (selected_app_index == handlers.size()) {
151 // The user hasn't made the selection yet.
152
153 // If |handlers| has only one element and its package is "Chrome", open
154 // the fallback URL in the current tab without showing the dialog.
155 if (handlers.size() == 1) {
156 if (GetActionInternal(original_url, handlers[0], out_url_and_package) ==
157 GetActionResult::OPEN_URL_IN_CHROME) {
158 return GetActionResult::OPEN_URL_IN_CHROME;
159 }
160 }
161
162 // If one of the apps is marked as preferred, use it right away without
163 // showing the UI. |is_preferred| will never be true unless the user
164 // explicitly makes it the default with the "always" button.
165 for (size_t i = 0; i < handlers.size(); ++i) {
166 const mojom::IntentHandlerInfoPtr& handler = handlers[i];
167 if (!handler->is_preferred)
168 continue;
169 // A preferred activity is found. Decide how to open it, either in Chrome
170 // or ARC.
171 return GetActionInternal(original_url, handler, out_url_and_package);
172 }
173 // Ask the user to pick one.
174 return GetActionResult::ASK_USER;
175 }
176
177 // The user has already made the selection. Decide how to open it, either in
178 // Chrome or ARC.
179 return GetActionInternal(original_url, handlers[selected_app_index],
180 out_url_and_package);
181 }
182
183 // Handles |url| if possible. Returns true if it is actually handled.
184 bool HandleUrl(int render_process_host_id,
185 int routing_id,
186 const GURL& url,
187 const mojo::Array<mojom::IntentHandlerInfoPtr>& handlers,
188 size_t selected_app_index,
189 GetActionResult* out_result) {
190 std::pair<GURL, std::string> url_and_package;
191 const GetActionResult result =
192 GetAction(url, handlers, selected_app_index, &url_and_package);
193 if (out_result)
194 *out_result = result;
195
196 switch (result) {
197 case GetActionResult::SHOW_CHROME_OS_DIALOG:
198 ShowFallbackExternalProtocolDialog(render_process_host_id, routing_id,
199 url);
200 return true;
201 case GetActionResult::OPEN_URL_IN_CHROME:
202 OpenUrlInChrome(render_process_host_id, routing_id,
203 url_and_package.first);
204 return true;
205 case GetActionResult::HANDLE_URL_IN_ARC:
206 HandleUrlInArc(render_process_host_id, routing_id, url_and_package);
207 return true;
208 case GetActionResult::ASK_USER:
209 break;
210 }
211 return false;
212 }
213
68 // Called when the dialog is closed. 214 // Called when the dialog is closed.
69 void OnIntentPickerClosed(int render_process_host_id, 215 void OnIntentPickerClosed(int render_process_host_id,
70 int routing_id, 216 int routing_id,
71 const GURL& url, 217 const GURL& url,
72 mojo::Array<mojom::IntentHandlerInfoPtr> handlers, 218 mojo::Array<mojom::IntentHandlerInfoPtr> handlers,
73 const std::string& selected_app_package, 219 const std::string& selected_app_package,
74 ArcNavigationThrottle::CloseReason close_reason) { 220 ArcNavigationThrottle::CloseReason close_reason) {
75 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); 221 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
76 222
77 // If the user selected an app to continue the navigation, confirm that the 223 // If the user selected an app to continue the navigation, confirm that the
(...skipping 17 matching lines...) Expand all
95 case ArcNavigationThrottle::CloseReason::ALWAYS_PRESSED: { 241 case ArcNavigationThrottle::CloseReason::ALWAYS_PRESSED: {
96 if (ArcIntentHelperBridge::GetIntentHelperInstance( 242 if (ArcIntentHelperBridge::GetIntentHelperInstance(
97 "AddPreferredPackage", kMinVersionForAddPreferredPackage)) { 243 "AddPreferredPackage", kMinVersionForAddPreferredPackage)) {
98 instance->AddPreferredPackage( 244 instance->AddPreferredPackage(
99 handlers[selected_app_index]->package_name); 245 handlers[selected_app_index]->package_name);
100 } 246 }
101 // fall through. 247 // fall through.
102 } 248 }
103 case ArcNavigationThrottle::CloseReason::JUST_ONCE_PRESSED: { 249 case ArcNavigationThrottle::CloseReason::JUST_ONCE_PRESSED: {
104 // Launch the selected app. 250 // Launch the selected app.
105 instance->HandleUrl(url.spec(), 251 HandleUrl(render_process_host_id, routing_id, url, handlers,
106 handlers[selected_app_index]->package_name); 252 selected_app_index, nullptr);
107 CloseTabIfNeeded(render_process_host_id, routing_id);
108 break; 253 break;
109 } 254 }
110 case ArcNavigationThrottle::CloseReason::PREFERRED_ACTIVITY_FOUND: 255 case ArcNavigationThrottle::CloseReason::PREFERRED_ACTIVITY_FOUND:
111 case ArcNavigationThrottle::CloseReason::INVALID: { 256 case ArcNavigationThrottle::CloseReason::INVALID: {
112 NOTREACHED(); 257 NOTREACHED();
113 return; // no UMA recording. 258 return; // no UMA recording.
114 } 259 }
115 case ArcNavigationThrottle::CloseReason::ERROR: { 260 case ArcNavigationThrottle::CloseReason::ERROR: {
116 LOG(ERROR) << "IntentPickerBubbleView returned CloseReason::ERROR: " 261 LOG(ERROR) << "IntentPickerBubbleView returned CloseReason::ERROR: "
117 << "instance=" << instance 262 << "instance=" << instance
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after
162 void OnUrlHandlerList(int render_process_host_id, 307 void OnUrlHandlerList(int render_process_host_id,
163 int routing_id, 308 int routing_id,
164 const GURL& url, 309 const GURL& url,
165 mojo::Array<mojom::IntentHandlerInfoPtr> handlers) { 310 mojo::Array<mojom::IntentHandlerInfoPtr> handlers) {
166 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); 311 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
167 312
168 auto* instance = ArcIntentHelperBridge::GetIntentHelperInstance( 313 auto* instance = ArcIntentHelperBridge::GetIntentHelperInstance(
169 "HandleUrl", kMinVersionForHandleUrl); 314 "HandleUrl", kMinVersionForHandleUrl);
170 scoped_refptr<ActivityIconLoader> icon_loader = GetIconLoader(); 315 scoped_refptr<ActivityIconLoader> icon_loader = GetIconLoader();
171 316
172 if (!instance || !icon_loader || !handlers.size()) { 317 if (!instance || !icon_loader) {
173 // No handler is available on ARC side. Show the Chrome OS dialog. 318 // ARC is not running anymore. Show the Chrome OS dialog.
174 ShowFallbackExternalProtocolDialog(render_process_host_id, routing_id, url); 319 ShowFallbackExternalProtocolDialog(render_process_host_id, routing_id, url);
175 return; 320 return;
176 } 321 }
177 322
178 // If one of the apps is marked as preferred, use it right away without 323 // Check if the |url| should be handled right away without showing the UI.
179 // showing the UI. |is_preferred| will never be true unless the user 324 GetActionResult result;
180 // explicitly makes it the default with the "always" button. 325 if (HandleUrl(render_process_host_id, routing_id, url, handlers,
181 for (size_t i = 0; i < handlers.size(); ++i) { 326 handlers.size(), &result)) {
182 if (!handlers[i]->is_preferred) 327 if (result == GetActionResult::HANDLE_URL_IN_ARC)
183 continue; 328 RecordUma(ArcNavigationThrottle::CloseReason::PREFERRED_ACTIVITY_FOUND);
184 instance->HandleUrl(url.spec(), handlers[i]->package_name); 329 return; // the |url| has been handled.
185 CloseTabIfNeeded(render_process_host_id, routing_id);
186 RecordUma(ArcNavigationThrottle::CloseReason::PREFERRED_ACTIVITY_FOUND);
187 return;
188 } 330 }
189 331
190 // Otherwise, retrieve icons of the activities. 332 // Otherwise, retrieve icons of the activities.
191 std::vector<ActivityIconLoader::ActivityName> activities; 333 std::vector<ActivityIconLoader::ActivityName> activities;
192 for (const auto& handler : handlers) { 334 for (const auto& handler : handlers) {
193 activities.emplace_back(handler->package_name, handler->activity_name); 335 activities.emplace_back(handler->package_name, handler->activity_name);
194 } 336 }
195 icon_loader->GetActivityIcons( 337 icon_loader->GetActivityIcons(
196 activities, base::Bind(OnAppIconsReceived, render_process_host_id, 338 activities, base::Bind(OnAppIconsReceived, render_process_host_id,
197 routing_id, url, base::Passed(&handlers))); 339 routing_id, url, base::Passed(&handlers)));
198 } 340 }
199 341
200 } // namespace 342 } // namespace
201 343
202 bool RunArcExternalProtocolDialog(const GURL& url, 344 bool RunArcExternalProtocolDialog(const GURL& url,
203 int render_process_host_id, 345 int render_process_host_id,
204 int routing_id, 346 int routing_id,
205 ui::PageTransition page_transition, 347 ui::PageTransition page_transition,
206 bool has_user_gesture) { 348 bool has_user_gesture) {
349 // This function is for external protocols that Chrome cannot handle.
350 DCHECK(!url.SchemeIsHTTPOrHTTPS()) << url;
351
207 // Try to forward <form> submissions to ARC when possible. 352 // Try to forward <form> submissions to ARC when possible.
208 constexpr bool kAllowFormSubmit = true; 353 constexpr bool kAllowFormSubmit = true;
209 if (ShouldIgnoreNavigation(page_transition, kAllowFormSubmit)) 354 if (ShouldIgnoreNavigation(page_transition, kAllowFormSubmit))
210 return false; 355 return false;
211 356
212 auto* instance = ArcIntentHelperBridge::GetIntentHelperInstance( 357 auto* instance = ArcIntentHelperBridge::GetIntentHelperInstance(
213 "RequestUrlHandlerList", kMinVersionForRequestUrlHandlerList); 358 "RequestUrlHandlerList", kMinVersionForRequestUrlHandlerList);
214 if (!instance) 359 if (!instance)
215 return false; // ARC is either not supported or not yet ready. 360 return false; // ARC is either not supported or not yet ready.
216 361
217 WebContents* web_contents = 362 WebContents* web_contents =
218 tab_util::GetWebContentsByID(render_process_host_id, routing_id); 363 tab_util::GetWebContentsByID(render_process_host_id, routing_id);
219 if (!web_contents || !web_contents->GetBrowserContext() || 364 if (!web_contents || !web_contents->GetBrowserContext() ||
220 web_contents->GetBrowserContext()->IsOffTheRecord()) { 365 web_contents->GetBrowserContext()->IsOffTheRecord()) {
221 return false; 366 return false;
222 } 367 }
223 368
224 // Show ARC version of the dialog, which is IntentPickerBubbleView. To show 369 // Show ARC version of the dialog, which is IntentPickerBubbleView. To show
225 // the bubble view, we need to ask ARC for a handler list first. 370 // the bubble view, we need to ask ARC for a handler list first.
226 instance->RequestUrlHandlerList( 371 instance->RequestUrlHandlerList(
227 url.spec(), 372 url.spec(),
228 base::Bind(OnUrlHandlerList, render_process_host_id, routing_id, url)); 373 base::Bind(OnUrlHandlerList, render_process_host_id, routing_id, url));
229 return true; 374 return true;
230 } 375 }
231 376
377 GetActionResult GetActionForTesting(
378 const GURL& original_url,
379 const mojo::Array<mojom::IntentHandlerInfoPtr>& handlers,
380 size_t selected_app_index,
381 std::pair<GURL, std::string>* out_url_and_package) {
382 return GetAction(original_url, handlers, selected_app_index,
383 out_url_and_package);
384 }
385
232 } // namespace arc 386 } // namespace arc
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698