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

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

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

Powered by Google App Engine
This is Rietveld 408576698