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

Side by Side 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 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/arc_external_protocol_dialog.h" 5 #include "chrome/browser/chromeos/arc/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/arc_navigation_throttle.h" 14 #include "chrome/browser/chromeos/arc/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 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.
87 page_transition_type, kIsRendererInitiated);
88 web_contents->OpenURL(params);
89 }
90
91 // Sends |url| to ARC.
92 void HandleUrlInArc(int render_process_host_id,
93 int routing_id,
94 const std::pair<GURL, std::string>& url_and_package) {
95 auto* instance = ArcIntentHelperBridge::GetIntentHelperInstance(
96 "HandleUrl", kMinVersionForHandleUrl);
97 if (!instance)
98 return;
99
100 instance->HandleUrl(url_and_package.first.spec(), url_and_package.second);
101 CloseTabIfNeeded(render_process_host_id, routing_id);
102 }
103
104 // Finds |selected_app_package| from the |handlers| array and returns the index.
105 // If the app is not found, returns |handlers.size()|.
106 size_t GetAppIndex(const mojo::Array<mojom::IntentHandlerInfoPtr>& handlers,
107 const std::string& selected_app_package) {
108 // If the user selected an app to continue the navigation, confirm that the
109 // |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
110 for (size_t i = 0; i < handlers.size(); ++i) {
111 if (handlers[i]->package_name == selected_app_package)
112 return i;
113 }
114 return handlers.size();
115 }
116
117 // A helper function called by GetAction().
118 GetActionResult GetActionInternal(
119 const GURL& original_url,
120 const mojom::IntentHandlerInfoPtr& handler,
121 std::pair<GURL, std::string>* out_url_and_package) {
122 if (!handler->fallback_url.is_null()) {
123 *out_url_and_package = std::make_pair(GURL(handler->fallback_url.get()),
124 handler->package_name.get());
125 if (ArcIntentHelperBridge::IsIntentHelperPackage(handler->package_name)) {
126 // Since |package_name| is "Chrome", and |fallback_url| is not null, the
127 // URL must be either http or https. Check it just in case, and if not,
128 // fallback to HANDLE_URL_IN_ARC;
129 if (out_url_and_package->first.SchemeIsHTTPOrHTTPS())
130 return GetActionResult::OPEN_URL_IN_CHROME;
131
132 LOG(WARNING) << "Failed to handle " << out_url_and_package->first
133 << " in Chrome. Falling back to ARC...";
134 }
135 // |fallback_url| which Chrome doesn't support is passed (e.g. market:).
136 return GetActionResult::HANDLE_URL_IN_ARC;
137 }
138
139 // Unlike |handler->fallback_url|, the |original_url| should always be handled
140 // in ARC since it's external to Chrome.
141 *out_url_and_package =
142 std::make_pair(original_url, handler->package_name.get());
143 return GetActionResult::HANDLE_URL_IN_ARC;
144 }
145
146 // Gets an action that should be done when ARC has the |handlers| for the
147 // |original_url| and the user selects |selected_app_index|. When the user
148 // hasn't selected any app, |selected_app_index| must be set to
149 // |handlers.size()|.
150 //
151 // When the returned action is either OPEN_URL_IN_CHROME or HANDLE_URL_IN_ARC,
152 // |out_url_and_package| is filled accordingly.
153 GetActionResult GetAction(
154 const GURL& original_url,
155 const mojo::Array<mojom::IntentHandlerInfoPtr>& handlers,
156 size_t selected_app_index,
157 std::pair<GURL, std::string>* out_url_and_package) {
158 DCHECK(out_url_and_package);
159 if (!handlers.size())
160 return GetActionResult::SHOW_CHROME_OS_DIALOG; // no apps found.
161
162 if (selected_app_index == handlers.size()) {
163 // The user hasn't made the selection yet.
164
165 // If |handlers| has only one element and its package is "Chrome", open
166 // the fallback URL in the current tab without showing the dialog.
167 if (handlers.size() == 1) {
168 if (GetActionInternal(original_url, handlers[0], out_url_and_package) ==
169 GetActionResult::OPEN_URL_IN_CHROME) {
170 return GetActionResult::OPEN_URL_IN_CHROME;
171 }
172 }
173
174 // If one of the apps is marked as preferred, use it right away without
175 // showing the UI. |is_preferred| will never be true unless the user
176 // explicitly makes it the default with the "always" button.
177 for (size_t i = 0; i < handlers.size(); ++i) {
178 const mojom::IntentHandlerInfoPtr& handler = handlers[i];
179 if (!handler->is_preferred)
180 continue;
181 // A preferred activity is found. Decide how to open it, either in Chrome
182 // or ARC.
183 return GetActionInternal(original_url, handler, out_url_and_package);
184 }
185 // Ask the user to pick one.
186 return GetActionResult::ASK_USER;
187 }
188
189 // The user has already made the selection. Decide how to open it, either in
190 // Chrome or ARC.
191 return GetActionInternal(original_url, handlers[selected_app_index],
192 out_url_and_package);
193 }
194
195 // Handles |url| if possible. Returns true if it is actually handled.
196 bool HandleUrl(int render_process_host_id,
197 int routing_id,
198 const GURL& url,
199 const mojo::Array<mojom::IntentHandlerInfoPtr>& handlers,
200 size_t selected_app_index,
201 GetActionResult* out_result) {
202 std::pair<GURL, std::string> url_and_package;
203 const GetActionResult result =
204 GetAction(url, handlers, selected_app_index, &url_and_package);
205 if (out_result)
206 *out_result = result;
207
208 switch (result) {
209 case GetActionResult::SHOW_CHROME_OS_DIALOG:
210 ShowFallbackExternalProtocolDialog(render_process_host_id, routing_id,
211 url);
212 return true;
213 case GetActionResult::OPEN_URL_IN_CHROME:
214 OpenUrlInChrome(render_process_host_id, routing_id,
215 url_and_package.first);
216 return true;
217 case GetActionResult::HANDLE_URL_IN_ARC:
218 HandleUrlInArc(render_process_host_id, routing_id, url_and_package);
219 return true;
220 case GetActionResult::ASK_USER:
221 break;
222 }
223 return false;
224 }
225
68 // Called when the dialog is closed. 226 // Called when the dialog is closed.
69 void OnIntentPickerClosed(int render_process_host_id, 227 void OnIntentPickerClosed(int render_process_host_id,
70 int routing_id, 228 int routing_id,
71 const GURL& url, 229 const GURL& url,
72 mojo::Array<mojom::IntentHandlerInfoPtr> handlers, 230 mojo::Array<mojom::IntentHandlerInfoPtr> handlers,
73 std::string selected_app_package, 231 std::string selected_app_package,
74 ArcNavigationThrottle::CloseReason close_reason) { 232 ArcNavigationThrottle::CloseReason close_reason) {
75 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); 233 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
76 234
77 size_t selected_app_index = handlers.size(); 235 const size_t selected_app_index = GetAppIndex(handlers, selected_app_package);
78 // Make sure that the instance at least supports HandleUrl. 236 // Make sure that the instance at least supports HandleUrl.
79 auto* instance = ArcIntentHelperBridge::GetIntentHelperInstance( 237 auto* instance = ArcIntentHelperBridge::GetIntentHelperInstance(
80 "HandleUrl", kMinVersionForHandleUrl); 238 "HandleUrl", kMinVersionForHandleUrl);
81 if (!instance) { 239 if (!instance) {
82 close_reason = ArcNavigationThrottle::CloseReason::ERROR; 240 close_reason = ArcNavigationThrottle::CloseReason::ERROR;
83 } else if (close_reason == 241 } else if (close_reason ==
84 ArcNavigationThrottle::CloseReason::JUST_ONCE_PRESSED || 242 ArcNavigationThrottle::CloseReason::JUST_ONCE_PRESSED ||
85 close_reason == 243 close_reason ==
86 ArcNavigationThrottle::CloseReason::ALWAYS_PRESSED) { 244 ArcNavigationThrottle::CloseReason::ALWAYS_PRESSED) {
87 // If the user selected an app to continue the navigation, confirm that the
88 // |package_name| matches a valid option and return the index.
89 for (size_t i = 0; i < handlers.size(); ++i) {
90 if (handlers[i]->package_name == selected_app_package) {
91 selected_app_index = i;
92 break;
93 }
94 }
95
96 if (selected_app_index == handlers.size()) 245 if (selected_app_index == handlers.size())
97 close_reason = ArcNavigationThrottle::CloseReason::ERROR; 246 close_reason = ArcNavigationThrottle::CloseReason::ERROR;
98 } 247 }
99 248
100 switch (close_reason) { 249 switch (close_reason) {
101 case ArcNavigationThrottle::CloseReason::ALWAYS_PRESSED: { 250 case ArcNavigationThrottle::CloseReason::ALWAYS_PRESSED: {
102 if (ArcIntentHelperBridge::GetIntentHelperInstance( 251 if (ArcIntentHelperBridge::GetIntentHelperInstance(
103 "AddPreferredPackage", kMinVersionForAddPreferredPackage)) { 252 "AddPreferredPackage", kMinVersionForAddPreferredPackage)) {
104 instance->AddPreferredPackage( 253 instance->AddPreferredPackage(
105 handlers[selected_app_index]->package_name); 254 handlers[selected_app_index]->package_name);
106 } 255 }
107 // fall through. 256 // fall through.
108 } 257 }
109 case ArcNavigationThrottle::CloseReason::JUST_ONCE_PRESSED: { 258 case ArcNavigationThrottle::CloseReason::JUST_ONCE_PRESSED: {
110 // Launch the selected app. 259 // Launch the selected app.
111 instance->HandleUrl(url.spec(), 260 HandleUrl(render_process_host_id, routing_id, url, handlers,
112 handlers[selected_app_index]->package_name); 261 selected_app_index, nullptr);
113 CloseTabIfNeeded(render_process_host_id, routing_id);
114 break; 262 break;
115 } 263 }
116 case ArcNavigationThrottle::CloseReason::PREFERRED_ACTIVITY_FOUND: 264 case ArcNavigationThrottle::CloseReason::PREFERRED_ACTIVITY_FOUND:
117 case ArcNavigationThrottle::CloseReason::INVALID: { 265 case ArcNavigationThrottle::CloseReason::INVALID: {
118 NOTREACHED(); 266 NOTREACHED();
119 return; // no UMA recording. 267 return; // no UMA recording.
120 } 268 }
121 case ArcNavigationThrottle::CloseReason::ERROR: { 269 case ArcNavigationThrottle::CloseReason::ERROR: {
122 LOG(ERROR) << "IntentPickerBubbleView returned CloseReason::ERROR: " 270 LOG(ERROR) << "IntentPickerBubbleView returned CloseReason::ERROR: "
123 << "instance=" << instance 271 << "instance=" << instance
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after
168 void OnUrlHandlerList(int render_process_host_id, 316 void OnUrlHandlerList(int render_process_host_id,
169 int routing_id, 317 int routing_id,
170 const GURL& url, 318 const GURL& url,
171 mojo::Array<mojom::IntentHandlerInfoPtr> handlers) { 319 mojo::Array<mojom::IntentHandlerInfoPtr> handlers) {
172 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); 320 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
173 321
174 auto* instance = ArcIntentHelperBridge::GetIntentHelperInstance( 322 auto* instance = ArcIntentHelperBridge::GetIntentHelperInstance(
175 "HandleUrl", kMinVersionForHandleUrl); 323 "HandleUrl", kMinVersionForHandleUrl);
176 scoped_refptr<ActivityIconLoader> icon_loader = GetIconLoader(); 324 scoped_refptr<ActivityIconLoader> icon_loader = GetIconLoader();
177 325
178 if (!instance || !icon_loader || !handlers.size()) { 326 if (!instance || !icon_loader) {
179 // No handler is available on ARC side. Show the Chrome OS dialog. 327 // ARC is not running anymore. Show the Chrome OS dialog.
180 ShowFallbackExternalProtocolDialog(render_process_host_id, routing_id, url); 328 ShowFallbackExternalProtocolDialog(render_process_host_id, routing_id, url);
181 return; 329 return;
182 } 330 }
183 331
184 // If one of the apps is marked as preferred, use it right away without 332 // Check if the |url| should be handled right away without showing the UI.
185 // showing the UI. |is_preferred| will never be true unless the user 333 GetActionResult result;
186 // explicitly makes it the default with the "always" button. 334 if (HandleUrl(render_process_host_id, routing_id, url, handlers,
187 for (size_t i = 0; i < handlers.size(); ++i) { 335 handlers.size(), &result)) {
188 if (!handlers[i]->is_preferred) 336 if (result == GetActionResult::HANDLE_URL_IN_ARC)
189 continue; 337 RecordUma(ArcNavigationThrottle::CloseReason::PREFERRED_ACTIVITY_FOUND);
190 instance->HandleUrl(url.spec(), handlers[i]->package_name); 338 return; // the |url| has been handled.
191 CloseTabIfNeeded(render_process_host_id, routing_id);
192 RecordUma(ArcNavigationThrottle::CloseReason::PREFERRED_ACTIVITY_FOUND);
193 return;
194 } 339 }
195 340
196 // Otherwise, retrieve icons of the activities. 341 // Otherwise, retrieve icons of the activities.
197 std::vector<ActivityIconLoader::ActivityName> activities; 342 std::vector<ActivityIconLoader::ActivityName> activities;
198 for (const auto& handler : handlers) { 343 for (const auto& handler : handlers) {
199 activities.emplace_back(handler->package_name, handler->activity_name); 344 activities.emplace_back(handler->package_name, handler->activity_name);
200 } 345 }
201 icon_loader->GetActivityIcons( 346 icon_loader->GetActivityIcons(
202 activities, base::Bind(OnAppIconsReceived, render_process_host_id, 347 activities, base::Bind(OnAppIconsReceived, render_process_host_id,
203 routing_id, url, base::Passed(&handlers))); 348 routing_id, url, base::Passed(&handlers)));
204 } 349 }
205 350
206 } // namespace 351 } // namespace
207 352
208 bool RunArcExternalProtocolDialog(const GURL& url, 353 bool RunArcExternalProtocolDialog(const GURL& url,
209 int render_process_host_id, 354 int render_process_host_id,
210 int routing_id, 355 int routing_id,
211 ui::PageTransition page_transition, 356 ui::PageTransition page_transition,
212 bool has_user_gesture) { 357 bool has_user_gesture) {
358 // This function is for external protocols that Chrome cannot handle.
359 DCHECK(!url.SchemeIsHTTPOrHTTPS()) << url;
360
213 // Try to forward <form> submissions to ARC when possible. 361 // Try to forward <form> submissions to ARC when possible.
214 constexpr bool kAllowFormSubmit = true; 362 constexpr bool kAllowFormSubmit = true;
215 if (ShouldIgnoreNavigation(page_transition, kAllowFormSubmit)) 363 if (ShouldIgnoreNavigation(page_transition, kAllowFormSubmit))
216 return false; 364 return false;
217 365
218 auto* instance = ArcIntentHelperBridge::GetIntentHelperInstance( 366 auto* instance = ArcIntentHelperBridge::GetIntentHelperInstance(
219 "RequestUrlHandlerList", kMinVersionForRequestUrlHandlerList); 367 "RequestUrlHandlerList", kMinVersionForRequestUrlHandlerList);
220 if (!instance) 368 if (!instance)
221 return false; // ARC is either not supported or not yet ready. 369 return false; // ARC is either not supported or not yet ready.
222 370
223 WebContents* web_contents = 371 WebContents* web_contents =
224 tab_util::GetWebContentsByID(render_process_host_id, routing_id); 372 tab_util::GetWebContentsByID(render_process_host_id, routing_id);
225 if (!web_contents || !web_contents->GetBrowserContext() || 373 if (!web_contents || !web_contents->GetBrowserContext() ||
226 web_contents->GetBrowserContext()->IsOffTheRecord()) { 374 web_contents->GetBrowserContext()->IsOffTheRecord()) {
227 return false; 375 return false;
228 } 376 }
229 377
230 // Show ARC version of the dialog, which is IntentPickerBubbleView. To show 378 // Show ARC version of the dialog, which is IntentPickerBubbleView. To show
231 // the bubble view, we need to ask ARC for a handler list first. 379 // the bubble view, we need to ask ARC for a handler list first.
232 instance->RequestUrlHandlerList( 380 instance->RequestUrlHandlerList(
233 url.spec(), 381 url.spec(),
234 base::Bind(OnUrlHandlerList, render_process_host_id, routing_id, url)); 382 base::Bind(OnUrlHandlerList, render_process_host_id, routing_id, url));
235 return true; 383 return true;
236 } 384 }
237 385
386 size_t GetAppIndexForTesting(
387 const mojo::Array<mojom::IntentHandlerInfoPtr>& handlers,
388 const std::string& selected_app_package) {
389 return GetAppIndex(handlers, selected_app_package);
390 }
391
392 GetActionResult GetActionForTesting(
393 const GURL& original_url,
394 const mojo::Array<mojom::IntentHandlerInfoPtr>& handlers,
395 size_t selected_app_index,
396 std::pair<GURL, std::string>* out_url_and_package) {
397 return GetAction(original_url, handlers, selected_app_index,
398 out_url_and_package);
399 }
400
238 } // namespace arc 401 } // namespace arc
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698