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_navigation_throttle.h" | 5 #include "chrome/browser/chromeos/arc/intent_helper/arc_navigation_throttle.h" |
| 6 | 6 |
| 7 #include <algorithm> | 7 #include <algorithm> |
| 8 | 8 |
| 9 #include "base/bind.h" | 9 #include "base/bind.h" |
| 10 #include "base/logging.h" | 10 #include "base/logging.h" |
| 11 #include "base/memory/ref_counted.h" | 11 #include "base/memory/ref_counted.h" |
| 12 #include "base/metrics/histogram_macros.h" | 12 #include "base/metrics/histogram_macros.h" |
| 13 #include "components/arc/arc_bridge_service.h" | 13 #include "components/arc/arc_bridge_service.h" |
| 14 #include "components/arc/arc_service_manager.h" | 14 #include "components/arc/arc_service_manager.h" |
| 15 #include "components/arc/intent_helper/arc_intent_helper_bridge.h" | 15 #include "components/arc/intent_helper/arc_intent_helper_bridge.h" |
| 16 #include "components/arc/intent_helper/local_activity_resolver.h" | 16 #include "components/arc/intent_helper/local_activity_resolver.h" |
| 17 #include "components/arc/intent_helper/page_transition_util.h" | 17 #include "components/arc/intent_helper/page_transition_util.h" |
| 18 #include "content/public/browser/browser_thread.h" | 18 #include "content/public/browser/browser_thread.h" |
| 19 #include "content/public/browser/navigation_controller.h" | 19 #include "content/public/browser/navigation_controller.h" |
| 20 #include "content/public/browser/navigation_handle.h" | 20 #include "content/public/browser/navigation_handle.h" |
| 21 #include "content/public/browser/site_instance.h" | |
| 21 #include "content/public/browser/web_contents.h" | 22 #include "content/public/browser/web_contents.h" |
| 22 #include "net/base/registry_controlled_domains/registry_controlled_domain.h" | 23 #include "net/base/registry_controlled_domains/registry_controlled_domain.h" |
| 23 #include "ui/base/page_transition_types.h" | 24 #include "ui/base/page_transition_types.h" |
| 24 #include "url/gurl.h" | |
| 25 | 25 |
| 26 namespace arc { | 26 namespace arc { |
| 27 | 27 |
| 28 namespace { | 28 namespace { |
| 29 | 29 |
| 30 constexpr uint32_t kMinVersionForHandleUrl = 2; | 30 constexpr uint32_t kMinVersionForHandleUrl = 2; |
| 31 constexpr uint32_t kMinVersionForRequestUrlHandlerList = 2; | 31 constexpr uint32_t kMinVersionForRequestUrlHandlerList = 2; |
| 32 constexpr uint32_t kMinVersionForAddPreferredPackage = 7; | 32 constexpr uint32_t kMinVersionForAddPreferredPackage = 7; |
| 33 | 33 |
| 34 scoped_refptr<ActivityIconLoader> GetIconLoader() { | 34 scoped_refptr<ActivityIconLoader> GetIconLoader() { |
| (...skipping 11 matching lines...) Expand all Loading... | |
| 46 // it in the desktop browser. | 46 // it in the desktop browser. |
| 47 if (!previous_url.is_valid() || previous_url.is_empty()) | 47 if (!previous_url.is_valid() || previous_url.is_empty()) |
| 48 return false; | 48 return false; |
| 49 | 49 |
| 50 // Also check |current_url| just in case. | 50 // Also check |current_url| just in case. |
| 51 if (!current_url.is_valid() || current_url.is_empty()) { | 51 if (!current_url.is_valid() || current_url.is_empty()) { |
| 52 DVLOG(1) << "Unexpected URL: " << current_url << ", opening it in Chrome."; | 52 DVLOG(1) << "Unexpected URL: " << current_url << ", opening it in Chrome."; |
| 53 return false; | 53 return false; |
| 54 } | 54 } |
| 55 | 55 |
| 56 // Check the scheme for both |previous_url| and |current_url| since an | |
|
Yusuke Sato
2016/10/29 00:39:57
Please update the unit test.
djacobo_
2016/10/31 20:14:43
Done.
| |
| 57 // extension could have referred us. (e.g. Google Docs). | |
| 58 if (!current_url.SchemeIsHTTPOrHTTPS() || | |
| 59 !previous_url.SchemeIsHTTPOrHTTPS()) { | |
| 60 return false; | |
| 61 } | |
| 62 | |
| 56 return !net::registry_controlled_domains::SameDomainOrHost( | 63 return !net::registry_controlled_domains::SameDomainOrHost( |
| 57 current_url, previous_url, | 64 current_url, previous_url, |
| 58 net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES); | 65 net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES); |
| 59 } | 66 } |
| 60 | 67 |
| 61 // Returns true if |handlers| contain one or more apps. When this function is | 68 // Returns true if |handlers| contain one or more apps. When this function is |
| 62 // called from OnAppCandidatesReceived, |handlers| always contain Chrome (aka | 69 // called from OnAppCandidatesReceived, |handlers| always contain Chrome (aka |
| 63 // intent_helper), but the function doesn't treat it as an app. | 70 // intent_helper), but the function doesn't treat it as an app. |
| 64 bool IsAppAvailable(const mojo::Array<mojom::IntentHandlerInfoPtr>& handlers) { | 71 bool IsAppAvailable(const mojo::Array<mojom::IntentHandlerInfoPtr>& handlers) { |
| 65 return handlers.size() > 1 || (handlers.size() == 1 && | 72 return handlers.size() > 1 || (handlers.size() == 1 && |
| (...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 112 } | 119 } |
| 113 | 120 |
| 114 } // namespace | 121 } // namespace |
| 115 | 122 |
| 116 ArcNavigationThrottle::ArcNavigationThrottle( | 123 ArcNavigationThrottle::ArcNavigationThrottle( |
| 117 content::NavigationHandle* navigation_handle, | 124 content::NavigationHandle* navigation_handle, |
| 118 const ShowIntentPickerCallback& show_intent_picker_cb) | 125 const ShowIntentPickerCallback& show_intent_picker_cb) |
| 119 : content::NavigationThrottle(navigation_handle), | 126 : content::NavigationThrottle(navigation_handle), |
| 120 show_intent_picker_callback_(show_intent_picker_cb), | 127 show_intent_picker_callback_(show_intent_picker_cb), |
| 121 previous_user_action_(CloseReason::INVALID), | 128 previous_user_action_(CloseReason::INVALID), |
| 129 starting_gurl_(GURL()), | |
|
Yusuke Sato
2016/10/29 00:39:57
redundant initialization, please remove the line.
djacobo_
2016/10/31 20:14:44
Done.
| |
| 130 redirected_to_arc_(false), | |
| 122 weak_ptr_factory_(this) {} | 131 weak_ptr_factory_(this) {} |
| 123 | 132 |
| 124 ArcNavigationThrottle::~ArcNavigationThrottle() = default; | 133 ArcNavigationThrottle::~ArcNavigationThrottle() = default; |
| 125 | 134 |
| 126 content::NavigationThrottle::ThrottleCheckResult | 135 content::NavigationThrottle::ThrottleCheckResult |
| 127 ArcNavigationThrottle::WillStartRequest() { | 136 ArcNavigationThrottle::WillStartRequest() { |
| 128 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); | 137 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| 138 starting_gurl_ = GetStartingGURL(); | |
| 129 // We must not handle navigations started from the context menu. | 139 // We must not handle navigations started from the context menu. |
| 130 if (navigation_handle()->WasStartedFromContextMenu()) | 140 if (navigation_handle()->WasStartedFromContextMenu()) |
| 131 return content::NavigationThrottle::PROCEED; | 141 return content::NavigationThrottle::PROCEED; |
| 132 return HandleRequest(); | 142 return HandleRequest(); |
| 133 } | 143 } |
| 134 | 144 |
| 135 content::NavigationThrottle::ThrottleCheckResult | 145 content::NavigationThrottle::ThrottleCheckResult |
| 136 ArcNavigationThrottle::WillRedirectRequest() { | 146 ArcNavigationThrottle::WillRedirectRequest() { |
| 137 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); | 147 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| 138 | |
| 139 switch (previous_user_action_) { | 148 switch (previous_user_action_) { |
| 140 case CloseReason::ERROR: | 149 case CloseReason::ERROR: |
| 141 case CloseReason::DIALOG_DEACTIVATED: | 150 case CloseReason::DIALOG_DEACTIVATED: |
| 142 // User dismissed the dialog, or some error occurred before. Don't | 151 // User dismissed the dialog, or some error occurred before. Don't |
| 143 // repeatedly pop up the dialog. | 152 // repeatedly pop up the dialog. |
| 144 return content::NavigationThrottle::PROCEED; | 153 return content::NavigationThrottle::PROCEED; |
| 145 | 154 |
| 146 case CloseReason::ALWAYS_PRESSED: | 155 case CloseReason::ALWAYS_PRESSED: |
| 147 case CloseReason::JUST_ONCE_PRESSED: | 156 case CloseReason::JUST_ONCE_PRESSED: |
| 148 case CloseReason::PREFERRED_ACTIVITY_FOUND: | 157 case CloseReason::PREFERRED_ACTIVITY_FOUND: |
| 149 // Should never get here - if the user selected one of these previously, | 158 // We must never show the intent picker for the same throttle more than |
| 150 // Chrome should not see a redirect. | 159 // once and we must considerate that we may have redirections even after |
| 151 NOTREACHED(); | 160 // seeing the UI and choosing an action. Previously we didn't consider |
| 161 // this as a valid scenario since we didn't enforce having a | |
| 162 // |starting_gurl_| and so ShouldOverrideUrlLoading siltently took care of | |
| 163 // those cases. | |
| 164 return redirected_to_arc_ ? content::NavigationThrottle::CANCEL_AND_IGNORE | |
|
Yusuke Sato
2016/10/29 00:39:57
Is this path really taken with redirected_to_arc_
djacobo_
2016/10/31 20:14:44
Thanks for catching this, I misunderstood what PRO
| |
| 165 : content::NavigationThrottle::PROCEED; | |
| 152 | 166 |
| 153 case CloseReason::INVALID: | 167 case CloseReason::INVALID: |
| 154 // No picker has previously been popped up for this - continue. | 168 // No picker has previously been popped up for this - continue. |
| 155 break; | 169 break; |
| 156 } | 170 } |
| 157 return HandleRequest(); | 171 return HandleRequest(); |
| 158 } | 172 } |
| 159 | 173 |
| 160 content::NavigationThrottle::ThrottleCheckResult | 174 content::NavigationThrottle::ThrottleCheckResult |
| 161 ArcNavigationThrottle::HandleRequest() { | 175 ArcNavigationThrottle::HandleRequest() { |
| 162 const GURL& url = navigation_handle()->GetURL(); | 176 const GURL& url = navigation_handle()->GetURL(); |
| 163 | 177 |
| 164 // Always handle http(s) <form> submissions in Chrome for two reasons: 1) we | 178 // Always handle http(s) <form> submissions in Chrome for two reasons: 1) we |
| 165 // don't have a way to send POST data to ARC, and 2) intercepting http(s) form | 179 // don't have a way to send POST data to ARC, and 2) intercepting http(s) form |
| 166 // submissions is not very important because such submissions are usually | 180 // submissions is not very important because such submissions are usually |
| 167 // done within the same domain. ShouldOverrideUrlLoading() below filters out | 181 // done within the same domain. ShouldOverrideUrlLoading() below filters out |
| 168 // such submissions anyway. | 182 // such submissions anyway. |
| 169 constexpr bool kAllowFormSubmit = false; | 183 constexpr bool kAllowFormSubmit = false; |
| 170 | 184 |
| 171 if (ShouldIgnoreNavigation(navigation_handle()->GetPageTransition(), | 185 if (ShouldIgnoreNavigation(navigation_handle()->GetPageTransition(), |
| 172 kAllowFormSubmit)) | 186 kAllowFormSubmit)) |
| 173 return content::NavigationThrottle::PROCEED; | 187 return content::NavigationThrottle::PROCEED; |
| 174 | 188 |
| 175 const GURL referrer_url = navigation_handle()->GetReferrer().url; | 189 if (!ShouldOverrideUrlLoading(starting_gurl_, url)) |
| 176 const GURL current_url = navigation_handle()->GetURL(); | |
| 177 const GURL last_committed_url = | |
| 178 navigation_handle()->GetWebContents()->GetLastCommittedURL(); | |
| 179 | |
| 180 // For navigations from http to https we clean up the Referrer as part of the | |
| 181 // sanitization proccess, however we may still have access to the last | |
| 182 // committed URL. On the other hand, navigations started within a new tab | |
| 183 // (e.g. due to target="_blank") will keep no track of any previous entries | |
| 184 // and so GetLastCommittedURL() can be seen empty sometimes, this is why we | |
| 185 // use one or the other accordingly. Also we don't use GetVisibleURL() since | |
| 186 // it may contain a still non-committed URL (i.e. it can be the same as | |
| 187 // GetURL()). | |
| 188 const GURL previous_url = | |
| 189 referrer_url.is_empty() ? last_committed_url : referrer_url; | |
| 190 | |
| 191 if (!ShouldOverrideUrlLoading(previous_url, current_url)) | |
| 192 return content::NavigationThrottle::PROCEED; | 190 return content::NavigationThrottle::PROCEED; |
| 193 | 191 |
| 194 ArcServiceManager* arc_service_manager = ArcServiceManager::Get(); | 192 ArcServiceManager* arc_service_manager = ArcServiceManager::Get(); |
| 195 DCHECK(arc_service_manager); | 193 DCHECK(arc_service_manager); |
| 196 scoped_refptr<LocalActivityResolver> local_resolver = | 194 scoped_refptr<LocalActivityResolver> local_resolver = |
| 197 arc_service_manager->activity_resolver(); | 195 arc_service_manager->activity_resolver(); |
| 198 if (local_resolver->ShouldChromeHandleUrl(url)) { | 196 if (local_resolver->ShouldChromeHandleUrl(url)) { |
| 199 // Allow navigation to proceed if there isn't an android app that handles | 197 // Allow navigation to proceed if there isn't an android app that handles |
| 200 // the given URL. | 198 // the given URL. |
| 201 return content::NavigationThrottle::PROCEED; | 199 return content::NavigationThrottle::PROCEED; |
| 202 } | 200 } |
| 203 | 201 |
| 204 auto* instance = ArcIntentHelperBridge::GetIntentHelperInstance( | 202 auto* instance = ArcIntentHelperBridge::GetIntentHelperInstance( |
| 205 "RequestUrlHandlerList", kMinVersionForRequestUrlHandlerList); | 203 "RequestUrlHandlerList", kMinVersionForRequestUrlHandlerList); |
| 206 if (!instance) | 204 if (!instance) |
| 207 return content::NavigationThrottle::PROCEED; | 205 return content::NavigationThrottle::PROCEED; |
| 208 instance->RequestUrlHandlerList( | 206 instance->RequestUrlHandlerList( |
| 209 url.spec(), base::Bind(&ArcNavigationThrottle::OnAppCandidatesReceived, | 207 url.spec(), base::Bind(&ArcNavigationThrottle::OnAppCandidatesReceived, |
| 210 weak_ptr_factory_.GetWeakPtr())); | 208 weak_ptr_factory_.GetWeakPtr())); |
| 211 return content::NavigationThrottle::DEFER; | 209 return content::NavigationThrottle::DEFER; |
| 212 } | 210 } |
| 213 | 211 |
| 212 GURL ArcNavigationThrottle::GetStartingGURL() const { | |
| 213 // This helps us determine a reference GURL for the current NavigationHandle. | |
| 214 // This is the order or preferrence: Referrer > LastCommittedURL > SiteURL, | |
| 215 // GetSiteURL *should* only be used on very rare cases, e.g. when the | |
| 216 // navigation goes from https: to http: on a new tab, thus losing the other | |
| 217 // potential referrers. | |
| 218 const GURL referrer_url = navigation_handle()->GetReferrer().url; | |
| 219 if (referrer_url.is_valid() && !referrer_url.is_empty()) | |
| 220 return referrer_url; | |
| 221 | |
| 222 const GURL last_committed_url = | |
| 223 navigation_handle()->GetWebContents()->GetLastCommittedURL(); | |
| 224 if (last_committed_url.is_valid() && !last_committed_url.is_empty()) | |
| 225 return last_committed_url; | |
| 226 | |
| 227 return navigation_handle()->GetStartingSiteInstance()->GetSiteURL(); | |
| 228 } | |
| 229 | |
| 214 // We received the array of app candidates to handle this URL (even the Chrome | 230 // We received the array of app candidates to handle this URL (even the Chrome |
| 215 // app is included). | 231 // app is included). |
| 216 void ArcNavigationThrottle::OnAppCandidatesReceived( | 232 void ArcNavigationThrottle::OnAppCandidatesReceived( |
| 217 mojo::Array<mojom::IntentHandlerInfoPtr> handlers) { | 233 mojo::Array<mojom::IntentHandlerInfoPtr> handlers) { |
| 218 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); | 234 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| 219 if (!IsAppAvailable(handlers)) { | 235 if (!IsAppAvailable(handlers)) { |
| 220 // This scenario shouldn't be accesed as ArcNavigationThrottle is created | 236 // This scenario shouldn't be accesed as ArcNavigationThrottle is created |
| 221 // iff there are ARC apps which can actually handle the given URL. | 237 // iff there are ARC apps which can actually handle the given URL. |
| 222 DVLOG(1) << "There are no app candidates for this URL: " | 238 DVLOG(1) << "There are no app candidates for this URL: " |
| 223 << navigation_handle()->GetURL(); | 239 << navigation_handle()->GetURL(); |
| (...skipping 98 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 322 } | 338 } |
| 323 // fall through. | 339 // fall through. |
| 324 } | 340 } |
| 325 case CloseReason::JUST_ONCE_PRESSED: | 341 case CloseReason::JUST_ONCE_PRESSED: |
| 326 case CloseReason::PREFERRED_ACTIVITY_FOUND: { | 342 case CloseReason::PREFERRED_ACTIVITY_FOUND: { |
| 327 if (ArcIntentHelperBridge::IsIntentHelperPackage( | 343 if (ArcIntentHelperBridge::IsIntentHelperPackage( |
| 328 handlers[selected_app_index]->package_name)) { | 344 handlers[selected_app_index]->package_name)) { |
| 329 handle->Resume(); | 345 handle->Resume(); |
| 330 } else { | 346 } else { |
| 331 instance->HandleUrl(url.spec(), selected_app_package); | 347 instance->HandleUrl(url.spec(), selected_app_package); |
| 348 redirected_to_arc_ = true; | |
| 332 handle->CancelDeferredNavigation( | 349 handle->CancelDeferredNavigation( |
| 333 content::NavigationThrottle::CANCEL_AND_IGNORE); | 350 content::NavigationThrottle::CANCEL_AND_IGNORE); |
| 334 if (handle->GetWebContents()->GetController().IsInitialNavigation()) | 351 if (handle->GetWebContents()->GetController().IsInitialNavigation()) |
| 335 handle->GetWebContents()->Close(); | 352 handle->GetWebContents()->Close(); |
| 336 } | 353 } |
| 337 break; | 354 break; |
| 338 } | 355 } |
| 339 case CloseReason::INVALID: { | 356 case CloseReason::INVALID: { |
| 340 NOTREACHED(); | 357 NOTREACHED(); |
| 341 return; | 358 return; |
| (...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 378 } | 395 } |
| 379 | 396 |
| 380 // static | 397 // static |
| 381 bool ArcNavigationThrottle::IsSwapElementsNeededForTesting( | 398 bool ArcNavigationThrottle::IsSwapElementsNeededForTesting( |
| 382 const mojo::Array<mojom::IntentHandlerInfoPtr>& handlers, | 399 const mojo::Array<mojom::IntentHandlerInfoPtr>& handlers, |
| 383 std::pair<size_t, size_t>* out_indices) { | 400 std::pair<size_t, size_t>* out_indices) { |
| 384 return IsSwapElementsNeeded(handlers, out_indices); | 401 return IsSwapElementsNeeded(handlers, out_indices); |
| 385 } | 402 } |
| 386 | 403 |
| 387 } // namespace arc | 404 } // namespace arc |
| OLD | NEW |