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/arc_navigation_throttle.h" | 5 #include "chrome/browser/chromeos/arc/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" |
| (...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 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 return !net::registry_controlled_domains::SameDomainOrHost( | 56 return !net::registry_controlled_domains::SameDomainOrHost( |
| 57 current_url, previous_url, | 57 current_url, previous_url, |
| 58 net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES); | 58 net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES); |
| 59 } | 59 } |
| 60 | 60 |
| 61 // Returns true if |handlers| contain one or more apps. | |
| 62 bool IsAppAvailable(const mojo::Array<mojom::IntentHandlerInfoPtr>& handlers) { | |
| 63 return !handlers.empty() && | |
| 64 !(handlers.size() == 1 && ArcIntentHelperBridge::IsIntentHelperPackage( | |
|
Luis Héctor Chávez
2016/10/24 20:24:34
nit: I think it's clearer after applying de Morgan
Yusuke Sato
2016/10/24 22:27:16
Done.
Applied yours and shuffled the order a litt
| |
| 65 handlers[0]->package_name)); | |
| 66 } | |
| 67 | |
| 68 // Searches for a preferred app in |handlers| and returns its index. If not | |
| 69 // found, returns |handlers.size()|. | |
| 70 size_t FindPreferredApp( | |
| 71 const mojo::Array<mojom::IntentHandlerInfoPtr>& handlers, | |
| 72 const GURL& url_for_logging) { | |
| 73 for (size_t i = 0; i < handlers.size(); ++i) { | |
| 74 if (!handlers[i]->is_preferred) | |
| 75 continue; | |
| 76 if (ArcIntentHelperBridge::IsIntentHelperPackage( | |
| 77 handlers[i]->package_name)) { | |
| 78 // If Chrome browser was selected as the preferred app, we should't | |
|
djacobo_
2016/10/24 20:22:14
nit: shouldn't*
Yusuke Sato
2016/10/24 22:27:16
Done.
| |
| 79 // create a throttle. | |
| 80 DVLOG(1) | |
| 81 << "Chrome browser is selected as the preferred app for this URL: " | |
| 82 << url_for_logging; | |
|
Luis Héctor Chávez
2016/10/24 20:24:34
isn't this missing |.spec()|? Since it's not a tri
Yusuke Sato
2016/10/24 22:27:16
I'm not sure if I understand your concern.. could
Luis Héctor Chávez
2016/10/24 22:59:54
This snippet of code used to log |url_for_logging.
Yusuke Sato
2016/10/24 23:25:17
Let me use url_for_logging (w/o .spec()) for simpl
| |
| 83 } | |
| 84 return i; | |
| 85 } | |
| 86 return handlers.size(); // not found | |
| 87 } | |
| 88 | |
| 89 // Swaps Chrome app with any app in row |kMaxAppResults-1| iff its index is | |
| 90 // bigger, thus ensuring the user can always see Chrome without scrolling. | |
| 91 // When swap is needed, fills |out_indices| and returns true. | |
| 92 bool IsSwapElementsNeeded( | |
| 93 const mojo::Array<mojom::IntentHandlerInfoPtr>& handlers, | |
| 94 std::pair<size_t, size_t>* out_indices) { | |
| 95 size_t chrome_app_index = 0; | |
| 96 for (size_t i = 0; i < handlers.size(); ++i) { | |
| 97 if (ArcIntentHelperBridge::IsIntentHelperPackage( | |
|
Luis Héctor Chávez
2016/10/24 20:24:34
qq: is it possible that the intent helper package
Yusuke Sato
2016/10/24 22:27:16
On production, Chrome always appears actually. It'
| |
| 98 handlers[i]->package_name)) { | |
| 99 chrome_app_index = i; | |
| 100 break; | |
| 101 } | |
| 102 } | |
| 103 if (chrome_app_index < ArcNavigationThrottle::kMaxAppResults) | |
| 104 return false; | |
| 105 | |
| 106 *out_indices = std::make_pair(ArcNavigationThrottle::kMaxAppResults - 1, | |
| 107 chrome_app_index); | |
| 108 return true; | |
| 109 } | |
| 110 | |
| 61 } // namespace | 111 } // namespace |
| 62 | 112 |
| 63 ArcNavigationThrottle::ArcNavigationThrottle( | 113 ArcNavigationThrottle::ArcNavigationThrottle( |
| 64 content::NavigationHandle* navigation_handle, | 114 content::NavigationHandle* navigation_handle, |
| 65 const ShowIntentPickerCallback& show_intent_picker_cb) | 115 const ShowIntentPickerCallback& show_intent_picker_cb) |
| 66 : content::NavigationThrottle(navigation_handle), | 116 : content::NavigationThrottle(navigation_handle), |
| 67 show_intent_picker_callback_(show_intent_picker_cb), | 117 show_intent_picker_callback_(show_intent_picker_cb), |
| 68 previous_user_action_(CloseReason::INVALID), | 118 previous_user_action_(CloseReason::INVALID), |
| 69 weak_ptr_factory_(this) {} | 119 weak_ptr_factory_(this) {} |
| 70 | 120 |
| (...skipping 85 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 156 url.spec(), base::Bind(&ArcNavigationThrottle::OnAppCandidatesReceived, | 206 url.spec(), base::Bind(&ArcNavigationThrottle::OnAppCandidatesReceived, |
| 157 weak_ptr_factory_.GetWeakPtr())); | 207 weak_ptr_factory_.GetWeakPtr())); |
| 158 return content::NavigationThrottle::DEFER; | 208 return content::NavigationThrottle::DEFER; |
| 159 } | 209 } |
| 160 | 210 |
| 161 // We received the array of app candidates to handle this URL (even the Chrome | 211 // We received the array of app candidates to handle this URL (even the Chrome |
| 162 // app is included). | 212 // app is included). |
| 163 void ArcNavigationThrottle::OnAppCandidatesReceived( | 213 void ArcNavigationThrottle::OnAppCandidatesReceived( |
| 164 mojo::Array<mojom::IntentHandlerInfoPtr> handlers) { | 214 mojo::Array<mojom::IntentHandlerInfoPtr> handlers) { |
| 165 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); | 215 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| 166 if (handlers.empty() || | 216 if (!IsAppAvailable(handlers)) { |
| 167 (handlers.size() == 1 && ArcIntentHelperBridge::IsIntentHelperPackage( | |
| 168 handlers[0]->package_name))) { | |
| 169 // This scenario shouldn't be accesed as ArcNavigationThrottle is created | 217 // This scenario shouldn't be accesed as ArcNavigationThrottle is created |
| 170 // iff there are ARC apps which can actually handle the given URL. | 218 // iff there are ARC apps which can actually handle the given URL. |
| 171 DVLOG(1) << "There are no app candidates for this URL: " | 219 DVLOG(1) << "There are no app candidates for this URL: " |
| 172 << navigation_handle()->GetURL().spec(); | 220 << navigation_handle()->GetURL().spec(); |
|
Yusuke Sato
2016/10/24 23:25:17
Removed this .spec() call too to be consistent wit
| |
| 173 navigation_handle()->Resume(); | 221 navigation_handle()->Resume(); |
| 174 return; | 222 return; |
| 175 } | 223 } |
| 176 | 224 |
| 177 // If one of the apps is marked as preferred, use it right away without | 225 // If one of the apps is marked as preferred, use it right away without |
| 178 // showing the UI. | 226 // showing the UI. |
| 179 for (size_t i = 0; i < handlers.size(); ++i) { | 227 const size_t index = |
| 180 if (!handlers[i]->is_preferred) | 228 FindPreferredApp(handlers, navigation_handle()->GetURL()); |
| 181 continue; | 229 if (index != handlers.size()) { |
| 182 if (ArcIntentHelperBridge::IsIntentHelperPackage( | 230 const std::string package_name = handlers[index]->package_name; |
| 183 handlers[i]->package_name)) { | |
| 184 // If Chrome browser was selected as the preferred app, we should't | |
| 185 // create a throttle. | |
| 186 DVLOG(1) | |
| 187 << "Chrome browser is selected as the preferred app for this URL: " | |
| 188 << navigation_handle()->GetURL().spec(); | |
| 189 } | |
| 190 std::string package_name = handlers[i]->package_name; | |
| 191 OnIntentPickerClosed(std::move(handlers), package_name, | 231 OnIntentPickerClosed(std::move(handlers), package_name, |
| 192 CloseReason::PREFERRED_ACTIVITY_FOUND); | 232 CloseReason::PREFERRED_ACTIVITY_FOUND); |
| 193 return; | 233 return; |
| 194 } | 234 } |
| 195 | 235 |
| 196 // Swap Chrome app with any app in row |kMaxAppResults-1| iff its index is | 236 std::pair<size_t, size_t> indices; |
| 197 // bigger, thus ensuring the user can always see Chrome without scrolling. | 237 if (IsSwapElementsNeeded(handlers, &indices)) |
| 198 size_t chrome_app_index = 0; | 238 std::swap(handlers[indices.first], handlers[indices.second]); |
| 199 for (size_t i = 0; i < handlers.size(); ++i) { | |
| 200 if (ArcIntentHelperBridge::IsIntentHelperPackage( | |
| 201 handlers[i]->package_name)) { | |
| 202 chrome_app_index = i; | |
| 203 break; | |
| 204 } | |
| 205 } | |
| 206 | |
| 207 if (chrome_app_index >= kMaxAppResults) | |
| 208 std::swap(handlers[kMaxAppResults - 1], handlers[chrome_app_index]); | |
| 209 | 239 |
| 210 scoped_refptr<ActivityIconLoader> icon_loader = GetIconLoader(); | 240 scoped_refptr<ActivityIconLoader> icon_loader = GetIconLoader(); |
| 211 if (!icon_loader) { | 241 if (!icon_loader) { |
| 212 LOG(ERROR) << "Cannot get an instance of ActivityIconLoader"; | 242 LOG(ERROR) << "Cannot get an instance of ActivityIconLoader"; |
| 213 navigation_handle()->Resume(); | 243 navigation_handle()->Resume(); |
| 214 return; | 244 return; |
| 215 } | 245 } |
| 216 std::vector<ActivityIconLoader::ActivityName> activities; | 246 std::vector<ActivityIconLoader::ActivityName> activities; |
| 217 for (const auto& handler : handlers) { | 247 for (const auto& handler : handlers) |
| 218 activities.emplace_back(handler->package_name, handler->activity_name); | 248 activities.emplace_back(handler->package_name, handler->activity_name); |
| 219 } | |
| 220 icon_loader->GetActivityIcons( | 249 icon_loader->GetActivityIcons( |
| 221 activities, | 250 activities, |
| 222 base::Bind(&ArcNavigationThrottle::OnAppIconsReceived, | 251 base::Bind(&ArcNavigationThrottle::OnAppIconsReceived, |
| 223 weak_ptr_factory_.GetWeakPtr(), base::Passed(&handlers))); | 252 weak_ptr_factory_.GetWeakPtr(), base::Passed(&handlers))); |
| 224 } | 253 } |
| 225 | 254 |
| 226 void ArcNavigationThrottle::OnAppIconsReceived( | 255 void ArcNavigationThrottle::OnAppIconsReceived( |
| 227 mojo::Array<mojom::IntentHandlerInfoPtr> handlers, | 256 mojo::Array<mojom::IntentHandlerInfoPtr> handlers, |
| 228 std::unique_ptr<ActivityIconLoader::ActivityToIconsMap> icons) { | 257 std::unique_ptr<ActivityIconLoader::ActivityToIconsMap> icons) { |
| 229 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); | 258 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| (...skipping 21 matching lines...) Expand all Loading... | |
| 251 std::string selected_app_package, | 280 std::string selected_app_package, |
| 252 CloseReason close_reason) { | 281 CloseReason close_reason) { |
| 253 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); | 282 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| 254 const GURL& url = navigation_handle()->GetURL(); | 283 const GURL& url = navigation_handle()->GetURL(); |
| 255 content::NavigationHandle* handle = navigation_handle(); | 284 content::NavigationHandle* handle = navigation_handle(); |
| 256 previous_user_action_ = close_reason; | 285 previous_user_action_ = close_reason; |
| 257 | 286 |
| 258 // Make sure that the instance at least supports HandleUrl. | 287 // Make sure that the instance at least supports HandleUrl. |
| 259 auto* instance = ArcIntentHelperBridge::GetIntentHelperInstance( | 288 auto* instance = ArcIntentHelperBridge::GetIntentHelperInstance( |
| 260 "HandleUrl", kMinVersionForHandleUrl); | 289 "HandleUrl", kMinVersionForHandleUrl); |
| 261 size_t selected_app_index = handlers.size(); | 290 // Since we are selecting an app by its package name, we need to locate it |
| 291 // on the |handlers| structure before sending the IPC to ARC. | |
| 292 const size_t selected_app_index = GetAppIndex(handlers, selected_app_package); | |
| 262 if (!instance) { | 293 if (!instance) { |
| 263 close_reason = CloseReason::ERROR; | 294 close_reason = CloseReason::ERROR; |
| 264 } else if (close_reason == CloseReason::JUST_ONCE_PRESSED || | 295 } else if (close_reason == CloseReason::JUST_ONCE_PRESSED || |
| 265 close_reason == CloseReason::ALWAYS_PRESSED || | 296 close_reason == CloseReason::ALWAYS_PRESSED || |
| 266 close_reason == CloseReason::PREFERRED_ACTIVITY_FOUND) { | 297 close_reason == CloseReason::PREFERRED_ACTIVITY_FOUND) { |
| 267 // Since we are selecting an app by its package name, we need to locate it | |
| 268 // on the |handlers| structure before sending the IPC to ARC. | |
| 269 for (size_t i = 0; i < handlers.size(); ++i) { | |
| 270 if (handlers[i]->package_name == selected_app_package) { | |
| 271 selected_app_index = i; | |
| 272 break; | |
| 273 } | |
| 274 } | |
| 275 | |
| 276 if (selected_app_index == handlers.size()) | 298 if (selected_app_index == handlers.size()) |
| 277 close_reason = CloseReason::ERROR; | 299 close_reason = CloseReason::ERROR; |
| 278 } | 300 } |
| 279 | 301 |
| 280 switch (close_reason) { | 302 switch (close_reason) { |
| 281 case CloseReason::ERROR: | 303 case CloseReason::ERROR: |
| 282 case CloseReason::DIALOG_DEACTIVATED: { | 304 case CloseReason::DIALOG_DEACTIVATED: { |
| 283 // If the user fails to select an option from the list, or the UI returned | 305 // If the user fails to select an option from the list, or the UI returned |
| 284 // an error or if |selected_app_index| is not a valid index, then resume | 306 // an error or if |selected_app_index| is not a valid index, then resume |
| 285 // the navigation in Chrome. | 307 // the navigation in Chrome. |
| (...skipping 30 matching lines...) Expand all Loading... | |
| 316 return; | 338 return; |
| 317 } | 339 } |
| 318 } | 340 } |
| 319 | 341 |
| 320 UMA_HISTOGRAM_ENUMERATION("Arc.IntentHandlerAction", | 342 UMA_HISTOGRAM_ENUMERATION("Arc.IntentHandlerAction", |
| 321 static_cast<int>(close_reason), | 343 static_cast<int>(close_reason), |
| 322 static_cast<int>(CloseReason::SIZE)); | 344 static_cast<int>(CloseReason::SIZE)); |
| 323 } | 345 } |
| 324 | 346 |
| 325 // static | 347 // static |
| 348 size_t ArcNavigationThrottle::GetAppIndex( | |
| 349 const mojo::Array<mojom::IntentHandlerInfoPtr>& handlers, | |
| 350 const std::string& selected_app_package) { | |
| 351 for (size_t i = 0; i < handlers.size(); ++i) { | |
| 352 if (handlers[i]->package_name == selected_app_package) | |
| 353 return i; | |
| 354 } | |
| 355 return handlers.size(); | |
| 356 } | |
| 357 | |
| 358 // static | |
| 326 bool ArcNavigationThrottle::ShouldOverrideUrlLoadingForTesting( | 359 bool ArcNavigationThrottle::ShouldOverrideUrlLoadingForTesting( |
| 327 const GURL& previous_url, | 360 const GURL& previous_url, |
| 328 const GURL& current_url) { | 361 const GURL& current_url) { |
| 329 return ShouldOverrideUrlLoading(previous_url, current_url); | 362 return ShouldOverrideUrlLoading(previous_url, current_url); |
| 330 } | 363 } |
| 331 | 364 |
| 365 // static | |
| 366 bool ArcNavigationThrottle::IsAppAvailableForTesting( | |
| 367 const mojo::Array<mojom::IntentHandlerInfoPtr>& handlers) { | |
| 368 return IsAppAvailable(handlers); | |
| 369 } | |
| 370 | |
| 371 // static | |
| 372 size_t ArcNavigationThrottle::FindPreferredAppForTesting( | |
| 373 const mojo::Array<mojom::IntentHandlerInfoPtr>& handlers) { | |
| 374 return FindPreferredApp(handlers, GURL()); | |
| 375 } | |
| 376 | |
| 377 // static | |
| 378 bool ArcNavigationThrottle::IsSwapElementsNeededForTesting( | |
| 379 const mojo::Array<mojom::IntentHandlerInfoPtr>& handlers, | |
| 380 std::pair<size_t, size_t>* out_indices) { | |
| 381 return IsSwapElementsNeeded(handlers, out_indices); | |
| 382 } | |
| 383 | |
| 332 } // namespace arc | 384 } // namespace arc |
| OLD | NEW |