Chromium Code Reviews| OLD | NEW |
|---|---|
| 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 | 8 |
| 9 #include "base/bind.h" | 9 #include "base/bind.h" |
| 10 #include "base/lazy_instance.h" | |
| 10 #include "base/memory/ref_counted.h" | 11 #include "base/memory/ref_counted.h" |
| 11 #include "chrome/browser/chromeos/arc/intent_helper/arc_navigation_throttle.h" | 12 #include "chrome/browser/chromeos/arc/intent_helper/arc_navigation_throttle.h" |
| 12 #include "chrome/browser/chromeos/external_protocol_dialog.h" | 13 #include "chrome/browser/chromeos/external_protocol_dialog.h" |
| 13 #include "chrome/browser/tab_contents/tab_util.h" | 14 #include "chrome/browser/tab_contents/tab_util.h" |
| 14 #include "chrome/browser/ui/browser_dialogs.h" | 15 #include "chrome/browser/ui/browser_dialogs.h" |
| 15 #include "components/arc/arc_bridge_service.h" | 16 #include "components/arc/arc_bridge_service.h" |
| 16 #include "components/arc/arc_service_manager.h" | 17 #include "components/arc/arc_service_manager.h" |
| 17 #include "components/arc/intent_helper/activity_icon_loader.h" | 18 #include "components/arc/intent_helper/activity_icon_loader.h" |
| 18 #include "components/arc/intent_helper/arc_intent_helper_bridge.h" | 19 #include "components/arc/intent_helper/arc_intent_helper_bridge.h" |
| 19 #include "components/arc/intent_helper/page_transition_util.h" | 20 #include "components/arc/intent_helper/page_transition_util.h" |
| (...skipping 10 matching lines...) Expand all Loading... | |
| 30 using content::WebContents; | 31 using content::WebContents; |
| 31 | 32 |
| 32 namespace arc { | 33 namespace arc { |
| 33 | 34 |
| 34 namespace { | 35 namespace { |
| 35 | 36 |
| 36 constexpr uint32_t kMinVersionForHandleUrl = 2; | 37 constexpr uint32_t kMinVersionForHandleUrl = 2; |
| 37 constexpr uint32_t kMinVersionForRequestUrlHandlerList = 2; | 38 constexpr uint32_t kMinVersionForRequestUrlHandlerList = 2; |
| 38 constexpr uint32_t kMinVersionForAddPreferredPackage = 7; | 39 constexpr uint32_t kMinVersionForAddPreferredPackage = 7; |
| 39 | 40 |
| 41 // TODO(yusukes|djacobo): Find a better way to detect a request loop and remove | |
| 42 // the global variables. | |
| 43 base::LazyInstance<GURL> g_last_url = LAZY_INSTANCE_INITIALIZER; | |
| 44 ui::PageTransition g_last_page_transition; | |
| 45 | |
| 40 // Shows the Chrome OS' original external protocol dialog as a fallback. | 46 // Shows the Chrome OS' original external protocol dialog as a fallback. |
| 41 void ShowFallbackExternalProtocolDialog(int render_process_host_id, | 47 void ShowFallbackExternalProtocolDialog(int render_process_host_id, |
| 42 int routing_id, | 48 int routing_id, |
| 43 const GURL& url) { | 49 const GURL& url) { |
| 44 WebContents* web_contents = | 50 WebContents* web_contents = |
| 45 tab_util::GetWebContentsByID(render_process_host_id, routing_id); | 51 tab_util::GetWebContentsByID(render_process_host_id, routing_id); |
| 46 new ExternalProtocolDialog(web_contents, url); | 52 new ExternalProtocolDialog(web_contents, url); |
| 47 } | 53 } |
| 48 | 54 |
| 49 scoped_refptr<ActivityIconLoader> GetIconLoader() { | 55 scoped_refptr<ActivityIconLoader> GetIconLoader() { |
| (...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 89 if (!instance) | 95 if (!instance) |
| 90 return; | 96 return; |
| 91 | 97 |
| 92 instance->HandleUrl(url_and_package.first.spec(), url_and_package.second); | 98 instance->HandleUrl(url_and_package.first.spec(), url_and_package.second); |
| 93 CloseTabIfNeeded(render_process_host_id, routing_id); | 99 CloseTabIfNeeded(render_process_host_id, routing_id); |
| 94 } | 100 } |
| 95 | 101 |
| 96 // A helper function called by GetAction(). | 102 // A helper function called by GetAction(). |
| 97 GetActionResult GetActionInternal( | 103 GetActionResult GetActionInternal( |
| 98 const GURL& original_url, | 104 const GURL& original_url, |
| 105 bool always_ask_user, | |
| 99 const mojom::IntentHandlerInfoPtr& handler, | 106 const mojom::IntentHandlerInfoPtr& handler, |
| 100 std::pair<GURL, std::string>* out_url_and_package) { | 107 std::pair<GURL, std::string>* out_url_and_package) { |
| 101 if (handler->fallback_url.has_value()) { | 108 if (handler->fallback_url.has_value()) { |
| 102 *out_url_and_package = | 109 *out_url_and_package = |
| 103 std::make_pair(GURL(*handler->fallback_url), handler->package_name); | 110 std::make_pair(GURL(*handler->fallback_url), handler->package_name); |
| 104 if (ArcIntentHelperBridge::IsIntentHelperPackage(handler->package_name)) { | 111 if (ArcIntentHelperBridge::IsIntentHelperPackage(handler->package_name)) { |
| 105 // Since |package_name| is "Chrome", and |fallback_url| is not null, the | 112 // Since |package_name| is "Chrome", and |fallback_url| is not null, the |
| 106 // URL must be either http or https. Check it just in case, and if not, | 113 // URL must be either http or https. Check it just in case, and if not, |
| 107 // fallback to HANDLE_URL_IN_ARC; | 114 // fallback to HANDLE_URL_IN_ARC; |
| 108 if (out_url_and_package->first.SchemeIsHTTPOrHTTPS()) | 115 if (out_url_and_package->first.SchemeIsHTTPOrHTTPS()) |
| 109 return GetActionResult::OPEN_URL_IN_CHROME; | 116 return GetActionResult::OPEN_URL_IN_CHROME; |
| 110 | 117 |
| 111 LOG(WARNING) << "Failed to handle " << out_url_and_package->first | 118 LOG(WARNING) << "Failed to handle " << out_url_and_package->first |
| 112 << " in Chrome. Falling back to ARC..."; | 119 << " in Chrome. Falling back to ARC..."; |
| 113 } | 120 } |
| 114 // |fallback_url| which Chrome doesn't support is passed (e.g. market:). | 121 // |fallback_url| which Chrome doesn't support is passed (e.g. market:). |
| 115 return GetActionResult::HANDLE_URL_IN_ARC; | 122 return always_ask_user ? GetActionResult::ASK_USER |
| 123 : GetActionResult::HANDLE_URL_IN_ARC; | |
| 116 } | 124 } |
| 117 | 125 |
| 118 // Unlike |handler->fallback_url|, the |original_url| should always be handled | 126 // Unlike |handler->fallback_url|, the |original_url| should always be handled |
| 119 // in ARC since it's external to Chrome. | 127 // in ARC since it's external to Chrome. |
| 120 *out_url_and_package = std::make_pair(original_url, handler->package_name); | 128 *out_url_and_package = std::make_pair(original_url, handler->package_name); |
| 121 return GetActionResult::HANDLE_URL_IN_ARC; | 129 return always_ask_user ? GetActionResult::ASK_USER |
| 130 : GetActionResult::HANDLE_URL_IN_ARC; | |
| 122 } | 131 } |
| 123 | 132 |
| 124 // Gets an action that should be done when ARC has the |handlers| for the | 133 // Gets an action that should be done when ARC has the |handlers| for the |
| 125 // |original_url| and the user selects |selected_app_index|. When the user | 134 // |original_url| and the user selects |selected_app_index|. When the user |
| 126 // hasn't selected any app, |selected_app_index| must be set to | 135 // hasn't selected any app, |selected_app_index| must be set to |
| 127 // |handlers.size()|. | 136 // |handlers.size()|. When |always_ask_user| is true, the function never |
| 137 // returns HANDLE_URL_IN_ARC. | |
| 128 // | 138 // |
| 129 // When the returned action is either OPEN_URL_IN_CHROME or HANDLE_URL_IN_ARC, | 139 // When the returned action is either OPEN_URL_IN_CHROME or HANDLE_URL_IN_ARC, |
| 130 // |out_url_and_package| is filled accordingly. | 140 // |out_url_and_package| is filled accordingly. |
| 131 GetActionResult GetAction( | 141 GetActionResult GetAction( |
| 132 const GURL& original_url, | 142 const GURL& original_url, |
| 143 bool always_ask_user, | |
| 133 const std::vector<mojom::IntentHandlerInfoPtr>& handlers, | 144 const std::vector<mojom::IntentHandlerInfoPtr>& handlers, |
| 134 size_t selected_app_index, | 145 size_t selected_app_index, |
| 135 std::pair<GURL, std::string>* out_url_and_package) { | 146 std::pair<GURL, std::string>* out_url_and_package) { |
| 136 DCHECK(out_url_and_package); | 147 DCHECK(out_url_and_package); |
| 137 if (!handlers.size()) | 148 if (!handlers.size()) |
| 138 return GetActionResult::SHOW_CHROME_OS_DIALOG; // no apps found. | 149 return GetActionResult::SHOW_CHROME_OS_DIALOG; // no apps found. |
| 139 | 150 |
| 140 if (selected_app_index == handlers.size()) { | 151 if (selected_app_index == handlers.size()) { |
| 141 // The user hasn't made the selection yet. | 152 // The user hasn't made the selection yet. |
| 142 | 153 |
| 143 // If |handlers| has only one element and its package is "Chrome", open | 154 // If |handlers| has only one element and its package is "Chrome", open |
| 144 // the fallback URL in the current tab without showing the dialog. | 155 // the fallback URL in the current tab without showing the dialog. |
| 145 if (handlers.size() == 1) { | 156 if (handlers.size() == 1) { |
| 146 if (GetActionInternal(original_url, handlers[0], out_url_and_package) == | 157 if (GetActionInternal(original_url, always_ask_user, handlers[0], |
| 158 out_url_and_package) == | |
| 147 GetActionResult::OPEN_URL_IN_CHROME) { | 159 GetActionResult::OPEN_URL_IN_CHROME) { |
| 148 return GetActionResult::OPEN_URL_IN_CHROME; | 160 return GetActionResult::OPEN_URL_IN_CHROME; |
| 149 } | 161 } |
| 150 } | 162 } |
| 151 | 163 |
| 152 // If one of the apps is marked as preferred, use it right away without | 164 // If one of the apps is marked as preferred, use it right away without |
| 153 // showing the UI. |is_preferred| will never be true unless the user | 165 // showing the UI. |is_preferred| will never be true unless the user |
| 154 // explicitly makes it the default with the "always" button. | 166 // explicitly makes it the default with the "always" button. |
| 155 for (size_t i = 0; i < handlers.size(); ++i) { | 167 for (size_t i = 0; i < handlers.size(); ++i) { |
| 156 const mojom::IntentHandlerInfoPtr& handler = handlers[i]; | 168 const mojom::IntentHandlerInfoPtr& handler = handlers[i]; |
| 157 if (!handler->is_preferred) | 169 if (!handler->is_preferred) |
| 158 continue; | 170 continue; |
| 159 // A preferred activity is found. Decide how to open it, either in Chrome | 171 // A preferred activity is found. Decide how to open it, either in Chrome |
| 160 // or ARC. | 172 // or ARC. |
| 161 return GetActionInternal(original_url, handler, out_url_and_package); | 173 return GetActionInternal(original_url, always_ask_user, handler, |
| 174 out_url_and_package); | |
| 162 } | 175 } |
| 163 // Ask the user to pick one. | 176 // Ask the user to pick one. |
| 164 return GetActionResult::ASK_USER; | 177 return GetActionResult::ASK_USER; |
| 165 } | 178 } |
| 166 | 179 |
| 167 // The user has already made the selection. Decide how to open it, either in | 180 // The user has already made the selection. Decide how to open it, either in |
| 168 // Chrome or ARC. | 181 // Chrome or ARC. |
| 169 return GetActionInternal(original_url, handlers[selected_app_index], | 182 DCHECK(!always_ask_user) |
| 183 << "|always_ask_user| must be false when |selected_app_index| is valid."; | |
| 184 return GetActionInternal(original_url, false, handlers[selected_app_index], | |
| 170 out_url_and_package); | 185 out_url_and_package); |
| 171 } | 186 } |
| 172 | 187 |
| 173 // Handles |url| if possible. Returns true if it is actually handled. | 188 // Handles |url| if possible. Returns true if it is actually handled. |
| 174 bool HandleUrl(int render_process_host_id, | 189 bool HandleUrl(int render_process_host_id, |
| 175 int routing_id, | 190 int routing_id, |
| 176 const GURL& url, | 191 const GURL& url, |
| 192 bool always_ask_user, | |
| 177 const std::vector<mojom::IntentHandlerInfoPtr>& handlers, | 193 const std::vector<mojom::IntentHandlerInfoPtr>& handlers, |
| 178 size_t selected_app_index, | 194 size_t selected_app_index, |
| 179 GetActionResult* out_result) { | 195 GetActionResult* out_result) { |
| 180 std::pair<GURL, std::string> url_and_package; | 196 std::pair<GURL, std::string> url_and_package; |
| 181 const GetActionResult result = | 197 const GetActionResult result = GetAction( |
| 182 GetAction(url, handlers, selected_app_index, &url_and_package); | 198 url, always_ask_user, handlers, selected_app_index, &url_and_package); |
| 183 if (out_result) | 199 if (out_result) |
| 184 *out_result = result; | 200 *out_result = result; |
| 185 | 201 |
| 186 switch (result) { | 202 switch (result) { |
| 187 case GetActionResult::SHOW_CHROME_OS_DIALOG: | 203 case GetActionResult::SHOW_CHROME_OS_DIALOG: |
| 188 ShowFallbackExternalProtocolDialog(render_process_host_id, routing_id, | 204 ShowFallbackExternalProtocolDialog(render_process_host_id, routing_id, |
| 189 url); | 205 url); |
| 190 return true; | 206 return true; |
| 191 case GetActionResult::OPEN_URL_IN_CHROME: | 207 case GetActionResult::OPEN_URL_IN_CHROME: |
| 192 OpenUrlInChrome(render_process_host_id, routing_id, | 208 OpenUrlInChrome(render_process_host_id, routing_id, |
| 193 url_and_package.first); | 209 url_and_package.first); |
| 194 return true; | 210 return true; |
| 195 case GetActionResult::HANDLE_URL_IN_ARC: | 211 case GetActionResult::HANDLE_URL_IN_ARC: |
| 196 HandleUrlInArc(render_process_host_id, routing_id, url_and_package); | 212 HandleUrlInArc(render_process_host_id, routing_id, url_and_package); |
| 197 return true; | 213 return true; |
| 198 case GetActionResult::ASK_USER: | 214 case GetActionResult::ASK_USER: |
| 199 break; | 215 break; |
| 200 } | 216 } |
| 201 return false; | 217 return false; |
| 202 } | 218 } |
| 203 | 219 |
| 204 // Returns a fallback http(s) in |handlers| which Chrome can handle. Returns | 220 // Returns a fallback http(s) in |handlers| which Chrome can handle. Returns |
| 205 // an empty GURL if none found. | 221 // an empty GURL if none found. |
| 206 GURL GetUrlToNavigateOnDeactivate( | 222 GURL GetUrlToNavigateOnDeactivate( |
| 207 const std::vector<mojom::IntentHandlerInfoPtr>& handlers) { | 223 const std::vector<mojom::IntentHandlerInfoPtr>& handlers) { |
| 208 const GURL empty_url; | 224 const GURL empty_url; |
| 209 for (size_t i = 0; i < handlers.size(); ++i) { | 225 for (size_t i = 0; i < handlers.size(); ++i) { |
| 210 std::pair<GURL, std::string> url_and_package; | 226 std::pair<GURL, std::string> url_and_package; |
| 211 if (GetActionInternal(empty_url, handlers[i], &url_and_package) == | 227 if (GetActionInternal(empty_url, false, handlers[i], &url_and_package) == |
| 212 GetActionResult::OPEN_URL_IN_CHROME) { | 228 GetActionResult::OPEN_URL_IN_CHROME) { |
| 213 DCHECK(url_and_package.first.SchemeIsHTTPOrHTTPS()); | 229 DCHECK(url_and_package.first.SchemeIsHTTPOrHTTPS()); |
| 214 return url_and_package.first; | 230 return url_and_package.first; |
| 215 } | 231 } |
| 216 } | 232 } |
| 217 return empty_url; // nothing found. | 233 return empty_url; // nothing found. |
| 218 } | 234 } |
| 219 | 235 |
| 220 // Called when the dialog is just deactivated without pressing one of the | 236 // Called when the dialog is just deactivated without pressing one of the |
| 221 // buttons. | 237 // buttons. |
| (...skipping 24 matching lines...) Expand all Loading... | |
| 246 ArcNavigationThrottle::GetAppIndex(handlers, selected_app_package); | 262 ArcNavigationThrottle::GetAppIndex(handlers, selected_app_package); |
| 247 // Make sure that the instance at least supports HandleUrl. | 263 // Make sure that the instance at least supports HandleUrl. |
| 248 auto* instance = ArcIntentHelperBridge::GetIntentHelperInstance( | 264 auto* instance = ArcIntentHelperBridge::GetIntentHelperInstance( |
| 249 "HandleUrl", kMinVersionForHandleUrl); | 265 "HandleUrl", kMinVersionForHandleUrl); |
| 250 if (!instance) { | 266 if (!instance) { |
| 251 close_reason = ArcNavigationThrottle::CloseReason::ERROR; | 267 close_reason = ArcNavigationThrottle::CloseReason::ERROR; |
| 252 } else if (close_reason == | 268 } else if (close_reason == |
| 253 ArcNavigationThrottle::CloseReason::JUST_ONCE_PRESSED || | 269 ArcNavigationThrottle::CloseReason::JUST_ONCE_PRESSED || |
| 254 close_reason == | 270 close_reason == |
| 255 ArcNavigationThrottle::CloseReason::ALWAYS_PRESSED) { | 271 ArcNavigationThrottle::CloseReason::ALWAYS_PRESSED) { |
| 256 if (selected_app_index == handlers.size()) | 272 if (selected_app_index == handlers.size()) { |
| 257 close_reason = ArcNavigationThrottle::CloseReason::ERROR; | 273 close_reason = ArcNavigationThrottle::CloseReason::ERROR; |
| 274 } else { | |
| 275 // The user has made a selection. Clear g_last_* variables. | |
| 276 g_last_url.Get() = GURL(); | |
| 277 g_last_page_transition = ui::PageTransition(); | |
| 278 } | |
| 258 } | 279 } |
| 259 | 280 |
| 260 switch (close_reason) { | 281 switch (close_reason) { |
| 261 case ArcNavigationThrottle::CloseReason::ALWAYS_PRESSED: { | 282 case ArcNavigationThrottle::CloseReason::ALWAYS_PRESSED: { |
| 262 if (ArcIntentHelperBridge::GetIntentHelperInstance( | 283 if (ArcIntentHelperBridge::GetIntentHelperInstance( |
| 263 "AddPreferredPackage", kMinVersionForAddPreferredPackage)) { | 284 "AddPreferredPackage", kMinVersionForAddPreferredPackage)) { |
| 264 instance->AddPreferredPackage( | 285 instance->AddPreferredPackage( |
| 265 handlers[selected_app_index]->package_name); | 286 handlers[selected_app_index]->package_name); |
| 266 } | 287 } |
| 267 // fall through. | 288 // fall through. |
| 268 } | 289 } |
| 269 case ArcNavigationThrottle::CloseReason::JUST_ONCE_PRESSED: { | 290 case ArcNavigationThrottle::CloseReason::JUST_ONCE_PRESSED: { |
| 270 // Launch the selected app. | 291 // Launch the selected app. |
| 271 HandleUrl(render_process_host_id, routing_id, url, handlers, | 292 HandleUrl(render_process_host_id, routing_id, url, false, handlers, |
| 272 selected_app_index, nullptr); | 293 selected_app_index, nullptr); |
| 273 break; | 294 break; |
| 274 } | 295 } |
| 275 case ArcNavigationThrottle::CloseReason::PREFERRED_ACTIVITY_FOUND: | 296 case ArcNavigationThrottle::CloseReason::PREFERRED_ACTIVITY_FOUND: |
| 276 case ArcNavigationThrottle::CloseReason::INVALID: { | 297 case ArcNavigationThrottle::CloseReason::INVALID: { |
| 277 NOTREACHED(); | 298 NOTREACHED(); |
| 278 return; // no UMA recording. | 299 return; // no UMA recording. |
| 279 } | 300 } |
| 280 case ArcNavigationThrottle::CloseReason::ERROR: { | 301 case ArcNavigationThrottle::CloseReason::ERROR: { |
| 281 LOG(ERROR) << "IntentPickerBubbleView returned CloseReason::ERROR: " | 302 LOG(ERROR) << "IntentPickerBubbleView returned CloseReason::ERROR: " |
| (...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 324 tab_util::GetWebContentsByID(render_process_host_id, routing_id); | 345 tab_util::GetWebContentsByID(render_process_host_id, routing_id); |
| 325 show_bubble_cb.Run(web_contents, app_info, | 346 show_bubble_cb.Run(web_contents, app_info, |
| 326 base::Bind(OnIntentPickerClosed, render_process_host_id, | 347 base::Bind(OnIntentPickerClosed, render_process_host_id, |
| 327 routing_id, url, base::Passed(&handlers))); | 348 routing_id, url, base::Passed(&handlers))); |
| 328 } | 349 } |
| 329 | 350 |
| 330 // Called when ARC returned a handler list for the |url|. | 351 // Called when ARC returned a handler list for the |url|. |
| 331 void OnUrlHandlerList(int render_process_host_id, | 352 void OnUrlHandlerList(int render_process_host_id, |
| 332 int routing_id, | 353 int routing_id, |
| 333 const GURL& url, | 354 const GURL& url, |
| 355 bool always_ask_user, | |
| 334 std::vector<mojom::IntentHandlerInfoPtr> handlers) { | 356 std::vector<mojom::IntentHandlerInfoPtr> handlers) { |
| 335 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); | 357 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| 336 | 358 |
| 337 auto* instance = ArcIntentHelperBridge::GetIntentHelperInstance( | 359 auto* instance = ArcIntentHelperBridge::GetIntentHelperInstance( |
| 338 "HandleUrl", kMinVersionForHandleUrl); | 360 "HandleUrl", kMinVersionForHandleUrl); |
| 339 scoped_refptr<ActivityIconLoader> icon_loader = GetIconLoader(); | 361 scoped_refptr<ActivityIconLoader> icon_loader = GetIconLoader(); |
| 340 | 362 |
| 341 if (!instance || !icon_loader) { | 363 if (!instance || !icon_loader) { |
| 342 // ARC is not running anymore. Show the Chrome OS dialog. | 364 // ARC is not running anymore. Show the Chrome OS dialog. |
| 343 ShowFallbackExternalProtocolDialog(render_process_host_id, routing_id, url); | 365 ShowFallbackExternalProtocolDialog(render_process_host_id, routing_id, url); |
| 344 return; | 366 return; |
| 345 } | 367 } |
| 346 | 368 |
| 347 // Check if the |url| should be handled right away without showing the UI. | 369 // Check if the |url| should be handled right away without showing the UI. |
| 348 GetActionResult result; | 370 GetActionResult result; |
| 349 if (HandleUrl(render_process_host_id, routing_id, url, handlers, | 371 if (HandleUrl(render_process_host_id, routing_id, url, always_ask_user, |
| 350 handlers.size(), &result)) { | 372 handlers, handlers.size(), &result)) { |
| 351 if (result == GetActionResult::HANDLE_URL_IN_ARC) { | 373 if (result == GetActionResult::HANDLE_URL_IN_ARC) { |
| 352 ArcNavigationThrottle::RecordUma( | 374 ArcNavigationThrottle::RecordUma( |
| 353 ArcNavigationThrottle::CloseReason::PREFERRED_ACTIVITY_FOUND, | 375 ArcNavigationThrottle::CloseReason::PREFERRED_ACTIVITY_FOUND, |
| 354 ArcNavigationThrottle::Platform::ARC); | 376 ArcNavigationThrottle::Platform::ARC); |
| 355 } | 377 } |
| 356 return; // the |url| has been handled. | 378 return; // the |url| has been handled. |
| 357 } | 379 } |
| 358 | 380 |
| 359 // Otherwise, retrieve icons of the activities. First, swap |handler| elements | 381 // Otherwise, retrieve icons of the activities. First, swap |handler| elements |
| 360 // to ensure Chrome is visible in the UI by default. Since this function is | 382 // to ensure Chrome is visible in the UI by default. Since this function is |
| 361 // for handling external protocols, Chrome is rarely in the list, but if the | 383 // for handling external protocols, Chrome is rarely in the list, but if the |
| 362 // |url| is intent: with fallback or geo:, for example, it may be. | 384 // |url| is intent: with fallback or geo:, for example, it may be. |
| 363 std::pair<size_t, size_t> indices; | 385 std::pair<size_t, size_t> indices; |
| 364 if (ArcNavigationThrottle::IsSwapElementsNeeded(handlers, &indices)) | 386 if (ArcNavigationThrottle::IsSwapElementsNeeded(handlers, &indices)) |
| 365 std::swap(handlers[indices.first], handlers[indices.second]); | 387 std::swap(handlers[indices.first], handlers[indices.second]); |
| 366 | 388 |
| 367 // Then request the icons. | 389 // Then request the icons. |
| 368 std::vector<ActivityIconLoader::ActivityName> activities; | 390 std::vector<ActivityIconLoader::ActivityName> activities; |
| 369 for (const auto& handler : handlers) { | 391 for (const auto& handler : handlers) { |
| 370 activities.emplace_back(handler->package_name, handler->activity_name); | 392 activities.emplace_back(handler->package_name, handler->activity_name); |
| 371 } | 393 } |
| 372 icon_loader->GetActivityIcons( | 394 icon_loader->GetActivityIcons( |
| 373 activities, base::Bind(OnAppIconsReceived, render_process_host_id, | 395 activities, base::Bind(OnAppIconsReceived, render_process_host_id, |
| 374 routing_id, url, base::Passed(&handlers))); | 396 routing_id, url, base::Passed(&handlers))); |
| 375 } | 397 } |
| 376 | 398 |
| 399 // Returns true if the |url| is safe to be forwarded to ARC without showing the | |
| 400 // disambig dialog when there is a preferred app on ARC for the |url|. Note that | |
| 401 // this function almost always returns true (i.e. "safe") except for very rare | |
| 402 // situations mentioned below. | |
| 403 // TODO(yusukes|djacobo): Find a better way to detect a request loop and remove | |
| 404 // this heuristics. | |
|
djacobo
2016/12/01 23:47:23
nit: these*
Yusuke Sato
2016/12/02 00:44:37
Done.
| |
| 405 bool IsSafeToRedirectToArcWithoutUserConfirmation( | |
| 406 const GURL& url, | |
| 407 ui::PageTransition page_transition, | |
| 408 const GURL& last_url, | |
| 409 ui::PageTransition last_page_transition) { | |
| 410 // Return "safe" unless both transition flags are FROM_API because the only | |
| 411 // unsafe situation we know is infinite tab creation loop with FROM_API | |
| 412 // (b/30125340). | |
| 413 if (!(page_transition & ui::PAGE_TRANSITION_FROM_API) || | |
| 414 !(last_page_transition & ui::PAGE_TRANSITION_FROM_API)) { | |
| 415 return true; | |
| 416 } | |
| 417 | |
| 418 // Return "safe" unless both URLs are for the same app. | |
| 419 return url.scheme() != last_url.scheme(); | |
|
djacobo
2016/12/01 23:47:23
qq: Does this means that two consecutive calls to
Yusuke Sato
2016/12/02 00:24:17
Yes, but it's considered "possibly unsafe" only wh
| |
| 420 } | |
| 421 | |
| 377 } // namespace | 422 } // namespace |
| 378 | 423 |
| 379 bool RunArcExternalProtocolDialog(const GURL& url, | 424 bool RunArcExternalProtocolDialog(const GURL& url, |
| 380 int render_process_host_id, | 425 int render_process_host_id, |
| 381 int routing_id, | 426 int routing_id, |
| 382 ui::PageTransition page_transition, | 427 ui::PageTransition page_transition, |
| 383 bool has_user_gesture) { | 428 bool has_user_gesture) { |
| 384 // This function is for external protocols that Chrome cannot handle. | 429 // This function is for external protocols that Chrome cannot handle. |
| 385 DCHECK(!url.SchemeIsHTTPOrHTTPS()) << url; | 430 DCHECK(!url.SchemeIsHTTPOrHTTPS()) << url; |
| 386 | 431 |
| 387 if (ShouldIgnoreNavigation(page_transition, true /* allow_form_submit */, | 432 const bool always_ask_user = !IsSafeToRedirectToArcWithoutUserConfirmation( |
| 433 url, page_transition, g_last_url.Get(), g_last_page_transition); | |
| 434 LOG_IF(WARNING, always_ask_user) | |
| 435 << "RunArcExternalProtocolDialog: repeatedly handling external protocol " | |
| 436 << "redirection to " << url | |
| 437 << " started from API: last_url=" << g_last_url.Get(); | |
| 438 | |
| 439 // This function is called only on the UI thread. Updating g_last_* variables | |
| 440 // without a lock is safe. | |
| 441 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); | |
| 442 g_last_url.Get() = url; | |
| 443 g_last_page_transition = page_transition; | |
| 444 | |
| 445 // For external protocol navigation, always ignore the FROM_API qualifier. | |
| 446 // We sometimes do need to forward a request with FROM_API to ARC, or | |
| 447 // AppAuth may not work (b/33208965). This is safe as long as we properly | |
| 448 // use |always_ask_user|. | |
| 449 const ui::PageTransition masked_page_transition = | |
| 450 MaskOutPageTransition(page_transition, ui::PAGE_TRANSITION_FROM_API); | |
| 451 | |
| 452 if (ShouldIgnoreNavigation(masked_page_transition, | |
| 453 true /* allow_form_submit */, | |
| 388 true /* allow_client_redirect */)) { | 454 true /* allow_client_redirect */)) { |
| 389 LOG(WARNING) << "RunArcExternalProtocolDialog: ignoring " << url | 455 LOG(WARNING) << "RunArcExternalProtocolDialog: ignoring " << url |
| 390 << " with PageTransition=" << page_transition; | 456 << " with PageTransition=" << masked_page_transition; |
| 391 return false; | 457 return false; |
| 392 } | 458 } |
| 393 | 459 |
| 394 auto* instance = ArcIntentHelperBridge::GetIntentHelperInstance( | 460 auto* instance = ArcIntentHelperBridge::GetIntentHelperInstance( |
| 395 "RequestUrlHandlerList", kMinVersionForRequestUrlHandlerList); | 461 "RequestUrlHandlerList", kMinVersionForRequestUrlHandlerList); |
| 396 if (!instance) | 462 if (!instance) |
| 397 return false; // ARC is either not supported or not yet ready. | 463 return false; // ARC is either not supported or not yet ready. |
| 398 | 464 |
| 399 WebContents* web_contents = | 465 WebContents* web_contents = |
| 400 tab_util::GetWebContentsByID(render_process_host_id, routing_id); | 466 tab_util::GetWebContentsByID(render_process_host_id, routing_id); |
| 401 if (!web_contents || !web_contents->GetBrowserContext() || | 467 if (!web_contents || !web_contents->GetBrowserContext() || |
| 402 web_contents->GetBrowserContext()->IsOffTheRecord()) { | 468 web_contents->GetBrowserContext()->IsOffTheRecord()) { |
| 403 return false; | 469 return false; |
| 404 } | 470 } |
| 405 | 471 |
| 406 // Show ARC version of the dialog, which is IntentPickerBubbleView. To show | 472 // Show ARC version of the dialog, which is IntentPickerBubbleView. To show |
| 407 // the bubble view, we need to ask ARC for a handler list first. | 473 // the bubble view, we need to ask ARC for a handler list first. |
| 408 instance->RequestUrlHandlerList( | 474 instance->RequestUrlHandlerList( |
| 409 url.spec(), | 475 url.spec(), base::Bind(OnUrlHandlerList, render_process_host_id, |
| 410 base::Bind(OnUrlHandlerList, render_process_host_id, routing_id, url)); | 476 routing_id, url, always_ask_user)); |
| 411 return true; | 477 return true; |
| 412 } | 478 } |
| 413 | 479 |
| 414 GetActionResult GetActionForTesting( | 480 GetActionResult GetActionForTesting( |
| 415 const GURL& original_url, | 481 const GURL& original_url, |
| 482 bool always_ask_user, | |
| 416 const std::vector<mojom::IntentHandlerInfoPtr>& handlers, | 483 const std::vector<mojom::IntentHandlerInfoPtr>& handlers, |
| 417 size_t selected_app_index, | 484 size_t selected_app_index, |
| 418 std::pair<GURL, std::string>* out_url_and_package) { | 485 std::pair<GURL, std::string>* out_url_and_package) { |
| 419 return GetAction(original_url, handlers, selected_app_index, | 486 return GetAction(original_url, always_ask_user, handlers, selected_app_index, |
| 420 out_url_and_package); | 487 out_url_and_package); |
| 421 } | 488 } |
| 422 | 489 |
| 423 GURL GetUrlToNavigateOnDeactivateForTesting( | 490 GURL GetUrlToNavigateOnDeactivateForTesting( |
| 424 const std::vector<mojom::IntentHandlerInfoPtr>& handlers) { | 491 const std::vector<mojom::IntentHandlerInfoPtr>& handlers) { |
| 425 return GetUrlToNavigateOnDeactivate(handlers); | 492 return GetUrlToNavigateOnDeactivate(handlers); |
| 426 } | 493 } |
| 427 | 494 |
| 495 bool IsSafeToRedirectToArcWithoutUserConfirmationForTesting( | |
| 496 const GURL& url, | |
| 497 ui::PageTransition page_transition, | |
| 498 const GURL& last_url, | |
| 499 ui::PageTransition last_page_transition) { | |
| 500 return IsSafeToRedirectToArcWithoutUserConfirmation( | |
| 501 url, page_transition, last_url, last_page_transition); | |
| 502 } | |
| 503 | |
| 428 } // namespace arc | 504 } // namespace arc |
| OLD | NEW |