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. When this function is |
| 62 // called from OnAppCandidatesReceived, |handlers| always contain Chrome (aka |
| 63 // intent_helper), but the function doesn't treat it as an app. |
| 64 bool IsAppAvailable(const mojo::Array<mojom::IntentHandlerInfoPtr>& handlers) { |
| 65 return handlers.size() > 1 || (handlers.size() == 1 && |
| 66 !ArcIntentHelperBridge::IsIntentHelperPackage( |
| 67 handlers[0]->package_name)); |
| 68 } |
| 69 |
| 70 // Searches for a preferred app in |handlers| and returns its index. If not |
| 71 // found, returns |handlers.size()|. |
| 72 size_t FindPreferredApp( |
| 73 const mojo::Array<mojom::IntentHandlerInfoPtr>& handlers, |
| 74 const GURL& url_for_logging) { |
| 75 for (size_t i = 0; i < handlers.size(); ++i) { |
| 76 if (!handlers[i]->is_preferred) |
| 77 continue; |
| 78 if (ArcIntentHelperBridge::IsIntentHelperPackage( |
| 79 handlers[i]->package_name)) { |
| 80 // If Chrome browser was selected as the preferred app, we shouldn't |
| 81 // create a throttle. |
| 82 DVLOG(1) |
| 83 << "Chrome browser is selected as the preferred app for this URL: " |
| 84 << url_for_logging; |
| 85 } |
| 86 return i; |
| 87 } |
| 88 return handlers.size(); // not found |
| 89 } |
| 90 |
| 91 // Swaps Chrome app with any app in row |kMaxAppResults-1| iff its index is |
| 92 // bigger, thus ensuring the user can always see Chrome without scrolling. |
| 93 // When swap is needed, fills |out_indices| and returns true. If |handlers| |
| 94 // do not have Chrome, returns false. |
| 95 bool IsSwapElementsNeeded( |
| 96 const mojo::Array<mojom::IntentHandlerInfoPtr>& handlers, |
| 97 std::pair<size_t, size_t>* out_indices) { |
| 98 size_t chrome_app_index = 0; |
| 99 for (size_t i = 0; i < handlers.size(); ++i) { |
| 100 if (ArcIntentHelperBridge::IsIntentHelperPackage( |
| 101 handlers[i]->package_name)) { |
| 102 chrome_app_index = i; |
| 103 break; |
| 104 } |
| 105 } |
| 106 if (chrome_app_index < ArcNavigationThrottle::kMaxAppResults) |
| 107 return false; |
| 108 |
| 109 *out_indices = std::make_pair(ArcNavigationThrottle::kMaxAppResults - 1, |
| 110 chrome_app_index); |
| 111 return true; |
| 112 } |
| 113 |
61 } // namespace | 114 } // namespace |
62 | 115 |
63 ArcNavigationThrottle::ArcNavigationThrottle( | 116 ArcNavigationThrottle::ArcNavigationThrottle( |
64 content::NavigationHandle* navigation_handle, | 117 content::NavigationHandle* navigation_handle, |
65 const ShowIntentPickerCallback& show_intent_picker_cb) | 118 const ShowIntentPickerCallback& show_intent_picker_cb) |
66 : content::NavigationThrottle(navigation_handle), | 119 : content::NavigationThrottle(navigation_handle), |
67 show_intent_picker_callback_(show_intent_picker_cb), | 120 show_intent_picker_callback_(show_intent_picker_cb), |
68 previous_user_action_(CloseReason::INVALID), | 121 previous_user_action_(CloseReason::INVALID), |
69 weak_ptr_factory_(this) {} | 122 weak_ptr_factory_(this) {} |
70 | 123 |
(...skipping 85 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
156 url.spec(), base::Bind(&ArcNavigationThrottle::OnAppCandidatesReceived, | 209 url.spec(), base::Bind(&ArcNavigationThrottle::OnAppCandidatesReceived, |
157 weak_ptr_factory_.GetWeakPtr())); | 210 weak_ptr_factory_.GetWeakPtr())); |
158 return content::NavigationThrottle::DEFER; | 211 return content::NavigationThrottle::DEFER; |
159 } | 212 } |
160 | 213 |
161 // We received the array of app candidates to handle this URL (even the Chrome | 214 // We received the array of app candidates to handle this URL (even the Chrome |
162 // app is included). | 215 // app is included). |
163 void ArcNavigationThrottle::OnAppCandidatesReceived( | 216 void ArcNavigationThrottle::OnAppCandidatesReceived( |
164 mojo::Array<mojom::IntentHandlerInfoPtr> handlers) { | 217 mojo::Array<mojom::IntentHandlerInfoPtr> handlers) { |
165 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); | 218 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
166 if (handlers.empty() || | 219 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 | 220 // This scenario shouldn't be accesed as ArcNavigationThrottle is created |
170 // iff there are ARC apps which can actually handle the given URL. | 221 // iff there are ARC apps which can actually handle the given URL. |
171 DVLOG(1) << "There are no app candidates for this URL: " | 222 DVLOG(1) << "There are no app candidates for this URL: " |
172 << navigation_handle()->GetURL().spec(); | 223 << navigation_handle()->GetURL(); |
173 navigation_handle()->Resume(); | 224 navigation_handle()->Resume(); |
174 return; | 225 return; |
175 } | 226 } |
176 | 227 |
177 // If one of the apps is marked as preferred, use it right away without | 228 // If one of the apps is marked as preferred, use it right away without |
178 // showing the UI. | 229 // showing the UI. |
179 for (size_t i = 0; i < handlers.size(); ++i) { | 230 const size_t index = |
180 if (!handlers[i]->is_preferred) | 231 FindPreferredApp(handlers, navigation_handle()->GetURL()); |
181 continue; | 232 if (index != handlers.size()) { |
182 if (ArcIntentHelperBridge::IsIntentHelperPackage( | 233 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, | 234 OnIntentPickerClosed(std::move(handlers), package_name, |
192 CloseReason::PREFERRED_ACTIVITY_FOUND); | 235 CloseReason::PREFERRED_ACTIVITY_FOUND); |
193 return; | 236 return; |
194 } | 237 } |
195 | 238 |
196 // Swap Chrome app with any app in row |kMaxAppResults-1| iff its index is | 239 std::pair<size_t, size_t> indices; |
197 // bigger, thus ensuring the user can always see Chrome without scrolling. | 240 if (IsSwapElementsNeeded(handlers, &indices)) |
198 size_t chrome_app_index = 0; | 241 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 | 242 |
210 scoped_refptr<ActivityIconLoader> icon_loader = GetIconLoader(); | 243 scoped_refptr<ActivityIconLoader> icon_loader = GetIconLoader(); |
211 if (!icon_loader) { | 244 if (!icon_loader) { |
212 LOG(ERROR) << "Cannot get an instance of ActivityIconLoader"; | 245 LOG(ERROR) << "Cannot get an instance of ActivityIconLoader"; |
213 navigation_handle()->Resume(); | 246 navigation_handle()->Resume(); |
214 return; | 247 return; |
215 } | 248 } |
216 std::vector<ActivityIconLoader::ActivityName> activities; | 249 std::vector<ActivityIconLoader::ActivityName> activities; |
217 for (const auto& handler : handlers) { | 250 for (const auto& handler : handlers) |
218 activities.emplace_back(handler->package_name, handler->activity_name); | 251 activities.emplace_back(handler->package_name, handler->activity_name); |
219 } | |
220 icon_loader->GetActivityIcons( | 252 icon_loader->GetActivityIcons( |
221 activities, | 253 activities, |
222 base::Bind(&ArcNavigationThrottle::OnAppIconsReceived, | 254 base::Bind(&ArcNavigationThrottle::OnAppIconsReceived, |
223 weak_ptr_factory_.GetWeakPtr(), base::Passed(&handlers))); | 255 weak_ptr_factory_.GetWeakPtr(), base::Passed(&handlers))); |
224 } | 256 } |
225 | 257 |
226 void ArcNavigationThrottle::OnAppIconsReceived( | 258 void ArcNavigationThrottle::OnAppIconsReceived( |
227 mojo::Array<mojom::IntentHandlerInfoPtr> handlers, | 259 mojo::Array<mojom::IntentHandlerInfoPtr> handlers, |
228 std::unique_ptr<ActivityIconLoader::ActivityToIconsMap> icons) { | 260 std::unique_ptr<ActivityIconLoader::ActivityToIconsMap> icons) { |
229 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); | 261 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
(...skipping 21 matching lines...) Expand all Loading... |
251 std::string selected_app_package, | 283 std::string selected_app_package, |
252 CloseReason close_reason) { | 284 CloseReason close_reason) { |
253 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); | 285 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
254 const GURL& url = navigation_handle()->GetURL(); | 286 const GURL& url = navigation_handle()->GetURL(); |
255 content::NavigationHandle* handle = navigation_handle(); | 287 content::NavigationHandle* handle = navigation_handle(); |
256 previous_user_action_ = close_reason; | 288 previous_user_action_ = close_reason; |
257 | 289 |
258 // Make sure that the instance at least supports HandleUrl. | 290 // Make sure that the instance at least supports HandleUrl. |
259 auto* instance = ArcIntentHelperBridge::GetIntentHelperInstance( | 291 auto* instance = ArcIntentHelperBridge::GetIntentHelperInstance( |
260 "HandleUrl", kMinVersionForHandleUrl); | 292 "HandleUrl", kMinVersionForHandleUrl); |
261 size_t selected_app_index = handlers.size(); | 293 // Since we are selecting an app by its package name, we need to locate it |
| 294 // on the |handlers| structure before sending the IPC to ARC. |
| 295 const size_t selected_app_index = GetAppIndex(handlers, selected_app_package); |
262 if (!instance) { | 296 if (!instance) { |
263 close_reason = CloseReason::ERROR; | 297 close_reason = CloseReason::ERROR; |
264 } else if (close_reason == CloseReason::JUST_ONCE_PRESSED || | 298 } else if (close_reason == CloseReason::JUST_ONCE_PRESSED || |
265 close_reason == CloseReason::ALWAYS_PRESSED || | 299 close_reason == CloseReason::ALWAYS_PRESSED || |
266 close_reason == CloseReason::PREFERRED_ACTIVITY_FOUND) { | 300 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()) | 301 if (selected_app_index == handlers.size()) |
277 close_reason = CloseReason::ERROR; | 302 close_reason = CloseReason::ERROR; |
278 } | 303 } |
279 | 304 |
280 switch (close_reason) { | 305 switch (close_reason) { |
281 case CloseReason::ERROR: | 306 case CloseReason::ERROR: |
282 case CloseReason::DIALOG_DEACTIVATED: { | 307 case CloseReason::DIALOG_DEACTIVATED: { |
283 // If the user fails to select an option from the list, or the UI returned | 308 // 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 | 309 // an error or if |selected_app_index| is not a valid index, then resume |
285 // the navigation in Chrome. | 310 // the navigation in Chrome. |
(...skipping 30 matching lines...) Expand all Loading... |
316 return; | 341 return; |
317 } | 342 } |
318 } | 343 } |
319 | 344 |
320 UMA_HISTOGRAM_ENUMERATION("Arc.IntentHandlerAction", | 345 UMA_HISTOGRAM_ENUMERATION("Arc.IntentHandlerAction", |
321 static_cast<int>(close_reason), | 346 static_cast<int>(close_reason), |
322 static_cast<int>(CloseReason::SIZE)); | 347 static_cast<int>(CloseReason::SIZE)); |
323 } | 348 } |
324 | 349 |
325 // static | 350 // static |
| 351 size_t ArcNavigationThrottle::GetAppIndex( |
| 352 const mojo::Array<mojom::IntentHandlerInfoPtr>& handlers, |
| 353 const std::string& selected_app_package) { |
| 354 for (size_t i = 0; i < handlers.size(); ++i) { |
| 355 if (handlers[i]->package_name == selected_app_package) |
| 356 return i; |
| 357 } |
| 358 return handlers.size(); |
| 359 } |
| 360 |
| 361 // static |
326 bool ArcNavigationThrottle::ShouldOverrideUrlLoadingForTesting( | 362 bool ArcNavigationThrottle::ShouldOverrideUrlLoadingForTesting( |
327 const GURL& previous_url, | 363 const GURL& previous_url, |
328 const GURL& current_url) { | 364 const GURL& current_url) { |
329 return ShouldOverrideUrlLoading(previous_url, current_url); | 365 return ShouldOverrideUrlLoading(previous_url, current_url); |
330 } | 366 } |
331 | 367 |
| 368 // static |
| 369 bool ArcNavigationThrottle::IsAppAvailableForTesting( |
| 370 const mojo::Array<mojom::IntentHandlerInfoPtr>& handlers) { |
| 371 return IsAppAvailable(handlers); |
| 372 } |
| 373 |
| 374 // static |
| 375 size_t ArcNavigationThrottle::FindPreferredAppForTesting( |
| 376 const mojo::Array<mojom::IntentHandlerInfoPtr>& handlers) { |
| 377 return FindPreferredApp(handlers, GURL()); |
| 378 } |
| 379 |
| 380 // static |
| 381 bool ArcNavigationThrottle::IsSwapElementsNeededForTesting( |
| 382 const mojo::Array<mojom::IntentHandlerInfoPtr>& handlers, |
| 383 std::pair<size_t, size_t>* out_indices) { |
| 384 return IsSwapElementsNeeded(handlers, out_indices); |
| 385 } |
| 386 |
332 } // namespace arc | 387 } // namespace arc |
OLD | NEW |