| 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 |
| 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 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 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), |
| 122 weak_ptr_factory_(this) {} | 129 weak_ptr_factory_(this) {} |
| 123 | 130 |
| 124 ArcNavigationThrottle::~ArcNavigationThrottle() = default; | 131 ArcNavigationThrottle::~ArcNavigationThrottle() = default; |
| 125 | 132 |
| 126 content::NavigationThrottle::ThrottleCheckResult | 133 content::NavigationThrottle::ThrottleCheckResult |
| 127 ArcNavigationThrottle::WillStartRequest() { | 134 ArcNavigationThrottle::WillStartRequest() { |
| 128 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); | 135 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| 136 starting_gurl_ = GetStartingGURL(); |
| 129 return HandleRequest(); | 137 return HandleRequest(); |
| 130 } | 138 } |
| 131 | 139 |
| 132 content::NavigationThrottle::ThrottleCheckResult | 140 content::NavigationThrottle::ThrottleCheckResult |
| 133 ArcNavigationThrottle::WillRedirectRequest() { | 141 ArcNavigationThrottle::WillRedirectRequest() { |
| 134 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); | 142 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| 135 | |
| 136 switch (previous_user_action_) { | 143 switch (previous_user_action_) { |
| 137 case CloseReason::ERROR: | 144 case CloseReason::ERROR: |
| 138 case CloseReason::DIALOG_DEACTIVATED: | 145 case CloseReason::DIALOG_DEACTIVATED: |
| 139 // User dismissed the dialog, or some error occurred before. Don't | 146 // User dismissed the dialog, or some error occurred before. Don't |
| 140 // repeatedly pop up the dialog. | 147 // repeatedly pop up the dialog. |
| 141 return content::NavigationThrottle::PROCEED; | 148 return content::NavigationThrottle::PROCEED; |
| 142 | 149 |
| 143 case CloseReason::ALWAYS_PRESSED: | 150 case CloseReason::ALWAYS_PRESSED: |
| 144 case CloseReason::JUST_ONCE_PRESSED: | 151 case CloseReason::JUST_ONCE_PRESSED: |
| 145 case CloseReason::PREFERRED_ACTIVITY_FOUND: | 152 case CloseReason::PREFERRED_ACTIVITY_FOUND: |
| 146 // Should never get here - if the user selected one of these previously, | 153 // We must never show the intent picker for the same throttle more than |
| 147 // Chrome should not see a redirect. | 154 // once and we must considerate that we may have redirections within the |
| 148 NOTREACHED(); | 155 // same ArcNavigationThrottle even after seeing the UI and selecting an |
| 156 // app to handle the navigation. This section can be reached iff the user |
| 157 // selected Chrome to continue the navigation, since Resume() tells the |
| 158 // throttle to continue with the chain of redirections. |
| 159 // |
| 160 // For example, by clicking a youtube link on gmail you can see the |
| 161 // following URLs, assume our |starting_gurl_| is "http://www.google.com": |
| 162 // |
| 163 // 1) https://www.google.com/url?hl=en&q=https://youtube.com/watch?v=fake |
| 164 // 2) https://youtube.com/watch?v=fake |
| 165 // 3) https://www.youtube.com/watch?v=fake |
| 166 // |
| 167 // 1) was caught via WillStartRequest() and 2) and 3) are caught via |
| 168 // WillRedirectRequest().Step 2) triggers the intent picker and step 3) |
| 169 // will be seen iff the user picks Chrome, or if Chrome was marked as the |
| 170 // preferred app for this kind of URL. This happens since after choosing |
| 171 // Chrome we tell the throttle to Resume(), thus allowing for further |
| 172 // redirections. |
| 173 return content::NavigationThrottle::PROCEED; |
| 149 | 174 |
| 150 case CloseReason::INVALID: | 175 case CloseReason::INVALID: |
| 151 // No picker has previously been popped up for this - continue. | 176 // No picker has previously been popped up for this - continue. |
| 152 break; | 177 break; |
| 153 } | 178 } |
| 154 return HandleRequest(); | 179 return HandleRequest(); |
| 155 } | 180 } |
| 156 | 181 |
| 157 content::NavigationThrottle::ThrottleCheckResult | 182 content::NavigationThrottle::ThrottleCheckResult |
| 158 ArcNavigationThrottle::HandleRequest() { | 183 ArcNavigationThrottle::HandleRequest() { |
| 159 const GURL& url = navigation_handle()->GetURL(); | 184 const GURL& url = navigation_handle()->GetURL(); |
| 160 | 185 |
| 161 // Always handle http(s) <form> submissions in Chrome for two reasons: 1) we | 186 // Always handle http(s) <form> submissions in Chrome for two reasons: 1) we |
| 162 // don't have a way to send POST data to ARC, and 2) intercepting http(s) form | 187 // don't have a way to send POST data to ARC, and 2) intercepting http(s) form |
| 163 // submissions is not very important because such submissions are usually | 188 // submissions is not very important because such submissions are usually |
| 164 // done within the same domain. ShouldOverrideUrlLoading() below filters out | 189 // done within the same domain. ShouldOverrideUrlLoading() below filters out |
| 165 // such submissions anyway. | 190 // such submissions anyway. |
| 166 constexpr bool kAllowFormSubmit = false; | 191 constexpr bool kAllowFormSubmit = false; |
| 167 | 192 |
| 168 // We must never handle navigations started within a context menu. | 193 // We must never handle navigations started within a context menu. |
| 169 if (navigation_handle()->WasStartedFromContextMenu()) | 194 if (navigation_handle()->WasStartedFromContextMenu()) |
| 170 return content::NavigationThrottle::PROCEED; | 195 return content::NavigationThrottle::PROCEED; |
| 171 | 196 |
| 172 if (ShouldIgnoreNavigation(navigation_handle()->GetPageTransition(), | 197 if (ShouldIgnoreNavigation(navigation_handle()->GetPageTransition(), |
| 173 kAllowFormSubmit)) | 198 kAllowFormSubmit)) |
| 174 return content::NavigationThrottle::PROCEED; | 199 return content::NavigationThrottle::PROCEED; |
| 175 | 200 |
| 176 const GURL referrer_url = navigation_handle()->GetReferrer().url; | 201 if (!ShouldOverrideUrlLoading(starting_gurl_, url)) |
| 177 const GURL current_url = navigation_handle()->GetURL(); | |
| 178 const GURL last_committed_url = | |
| 179 navigation_handle()->GetWebContents()->GetLastCommittedURL(); | |
| 180 | |
| 181 // For navigations from http to https we clean up the Referrer as part of the | |
| 182 // sanitization proccess, however we may still have access to the last | |
| 183 // committed URL. On the other hand, navigations started within a new tab | |
| 184 // (e.g. due to target="_blank") will keep no track of any previous entries | |
| 185 // and so GetLastCommittedURL() can be seen empty sometimes, this is why we | |
| 186 // use one or the other accordingly. Also we don't use GetVisibleURL() since | |
| 187 // it may contain a still non-committed URL (i.e. it can be the same as | |
| 188 // GetURL()). | |
| 189 const GURL previous_url = | |
| 190 referrer_url.is_empty() ? last_committed_url : referrer_url; | |
| 191 | |
| 192 if (!ShouldOverrideUrlLoading(previous_url, current_url)) | |
| 193 return content::NavigationThrottle::PROCEED; | 202 return content::NavigationThrottle::PROCEED; |
| 194 | 203 |
| 195 ArcServiceManager* arc_service_manager = ArcServiceManager::Get(); | 204 ArcServiceManager* arc_service_manager = ArcServiceManager::Get(); |
| 196 DCHECK(arc_service_manager); | 205 DCHECK(arc_service_manager); |
| 197 scoped_refptr<LocalActivityResolver> local_resolver = | 206 scoped_refptr<LocalActivityResolver> local_resolver = |
| 198 arc_service_manager->activity_resolver(); | 207 arc_service_manager->activity_resolver(); |
| 199 if (local_resolver->ShouldChromeHandleUrl(url)) { | 208 if (local_resolver->ShouldChromeHandleUrl(url)) { |
| 200 // Allow navigation to proceed if there isn't an android app that handles | 209 // Allow navigation to proceed if there isn't an android app that handles |
| 201 // the given URL. | 210 // the given URL. |
| 202 return content::NavigationThrottle::PROCEED; | 211 return content::NavigationThrottle::PROCEED; |
| 203 } | 212 } |
| 204 | 213 |
| 205 auto* instance = ArcIntentHelperBridge::GetIntentHelperInstance( | 214 auto* instance = ArcIntentHelperBridge::GetIntentHelperInstance( |
| 206 "RequestUrlHandlerList", kMinVersionForRequestUrlHandlerList); | 215 "RequestUrlHandlerList", kMinVersionForRequestUrlHandlerList); |
| 207 if (!instance) | 216 if (!instance) |
| 208 return content::NavigationThrottle::PROCEED; | 217 return content::NavigationThrottle::PROCEED; |
| 209 instance->RequestUrlHandlerList( | 218 instance->RequestUrlHandlerList( |
| 210 url.spec(), base::Bind(&ArcNavigationThrottle::OnAppCandidatesReceived, | 219 url.spec(), base::Bind(&ArcNavigationThrottle::OnAppCandidatesReceived, |
| 211 weak_ptr_factory_.GetWeakPtr())); | 220 weak_ptr_factory_.GetWeakPtr())); |
| 212 return content::NavigationThrottle::DEFER; | 221 return content::NavigationThrottle::DEFER; |
| 213 } | 222 } |
| 214 | 223 |
| 224 GURL ArcNavigationThrottle::GetStartingGURL() const { |
| 225 // This helps us determine a reference GURL for the current NavigationHandle. |
| 226 // This is the order or preferrence: Referrer > LastCommittedURL > SiteURL, |
| 227 // GetSiteURL *should* only be used on very rare cases, e.g. when the |
| 228 // navigation goes from https: to http: on a new tab, thus losing the other |
| 229 // potential referrers. |
| 230 const GURL referrer_url = navigation_handle()->GetReferrer().url; |
| 231 if (referrer_url.is_valid() && !referrer_url.is_empty()) |
| 232 return referrer_url; |
| 233 |
| 234 const GURL last_committed_url = |
| 235 navigation_handle()->GetWebContents()->GetLastCommittedURL(); |
| 236 if (last_committed_url.is_valid() && !last_committed_url.is_empty()) |
| 237 return last_committed_url; |
| 238 |
| 239 return navigation_handle()->GetStartingSiteInstance()->GetSiteURL(); |
| 240 } |
| 241 |
| 215 // We received the array of app candidates to handle this URL (even the Chrome | 242 // We received the array of app candidates to handle this URL (even the Chrome |
| 216 // app is included). | 243 // app is included). |
| 217 void ArcNavigationThrottle::OnAppCandidatesReceived( | 244 void ArcNavigationThrottle::OnAppCandidatesReceived( |
| 218 mojo::Array<mojom::IntentHandlerInfoPtr> handlers) { | 245 mojo::Array<mojom::IntentHandlerInfoPtr> handlers) { |
| 219 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); | 246 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| 220 if (!IsAppAvailable(handlers)) { | 247 if (!IsAppAvailable(handlers)) { |
| 221 // This scenario shouldn't be accesed as ArcNavigationThrottle is created | 248 // This scenario shouldn't be accesed as ArcNavigationThrottle is created |
| 222 // iff there are ARC apps which can actually handle the given URL. | 249 // iff there are ARC apps which can actually handle the given URL. |
| 223 DVLOG(1) << "There are no app candidates for this URL: " | 250 DVLOG(1) << "There are no app candidates for this URL: " |
| 224 << navigation_handle()->GetURL(); | 251 << navigation_handle()->GetURL(); |
| (...skipping 154 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 379 } | 406 } |
| 380 | 407 |
| 381 // static | 408 // static |
| 382 bool ArcNavigationThrottle::IsSwapElementsNeededForTesting( | 409 bool ArcNavigationThrottle::IsSwapElementsNeededForTesting( |
| 383 const mojo::Array<mojom::IntentHandlerInfoPtr>& handlers, | 410 const mojo::Array<mojom::IntentHandlerInfoPtr>& handlers, |
| 384 std::pair<size_t, size_t>* out_indices) { | 411 std::pair<size_t, size_t>* out_indices) { |
| 385 return IsSwapElementsNeeded(handlers, out_indices); | 412 return IsSwapElementsNeeded(handlers, out_indices); |
| 386 } | 413 } |
| 387 | 414 |
| 388 } // namespace arc | 415 } // namespace arc |
| OLD | NEW |