| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 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/ui/intents/web_intent_picker_controller.h" | 5 #include "chrome/browser/ui/intents/web_intent_picker_controller.h" |
| 6 | 6 |
| 7 #include <vector> | 7 #include <vector> |
| 8 | 8 |
| 9 #include "base/bind.h" | 9 #include "base/bind.h" |
| 10 #include "base/bind_helpers.h" | 10 #include "base/bind_helpers.h" |
| 11 #include "chrome/browser/ui/browser.h" | 11 #include "chrome/browser/ui/browser.h" |
| 12 #include "chrome/browser/favicon/favicon_service.h" | 12 #include "chrome/browser/favicon/favicon_service.h" |
| 13 #include "chrome/browser/intents/web_intents_registry_factory.h" | 13 #include "chrome/browser/intents/web_intents_registry_factory.h" |
| 14 #include "chrome/browser/intents/cws_intents_registry_factory.h" | 14 #include "chrome/browser/intents/cws_intents_registry_factory.h" |
| 15 #include "chrome/browser/profiles/profile.h" | 15 #include "chrome/browser/profiles/profile.h" |
| 16 #include "chrome/browser/tab_contents/tab_util.h" | 16 #include "chrome/browser/tab_contents/tab_util.h" |
| 17 #include "chrome/browser/tabs/tab_strip_model.h" | 17 #include "chrome/browser/tabs/tab_strip_model.h" |
| 18 #include "chrome/browser/ui/browser_list.h" | 18 #include "chrome/browser/ui/browser_list.h" |
| 19 #include "chrome/browser/ui/browser_navigator.h" | 19 #include "chrome/browser/ui/browser_navigator.h" |
| 20 #include "chrome/browser/ui/intents/web_intent_picker.h" | 20 #include "chrome/browser/ui/intents/web_intent_picker.h" |
| 21 #include "chrome/browser/ui/intents/web_intent_picker_model.h" | 21 #include "chrome/browser/ui/intents/web_intent_picker_model.h" |
| 22 #include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h" | 22 #include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h" |
| 23 #include "chrome/browser/webdata/web_data_service.h" | 23 #include "chrome/browser/webdata/web_data_service.h" |
| 24 #include "chrome/common/chrome_notification_types.h" | 24 #include "chrome/common/chrome_notification_types.h" |
| 25 #include "content/public/browser/browser_thread.h" |
| 25 #include "content/public/browser/notification_source.h" | 26 #include "content/public/browser/notification_source.h" |
| 26 #include "content/public/browser/web_contents.h" | 27 #include "content/public/browser/web_contents.h" |
| 27 #include "content/public/browser/web_intents_dispatcher.h" | 28 #include "content/public/browser/web_intents_dispatcher.h" |
| 29 #include "content/public/common/url_fetcher.h" |
| 30 #include "content/public/common/url_fetcher_delegate.h" |
| 31 #include "net/base/load_flags.h" |
| 32 #include "skia/ext/image_operations.h" |
| 28 #include "ui/gfx/codec/png_codec.h" | 33 #include "ui/gfx/codec/png_codec.h" |
| 34 #include "ui/gfx/favicon_size.h" |
| 29 #include "ui/gfx/image/image.h" | 35 #include "ui/gfx/image/image.h" |
| 30 #include "webkit/glue/web_intent_service_data.h" | 36 #include "webkit/glue/web_intent_service_data.h" |
| 31 | 37 |
| 32 namespace { | 38 namespace { |
| 33 | 39 |
| 34 // Gets the favicon service for the profile in |tab_contents|. | 40 // Gets the favicon service for the profile in |tab_contents|. |
| 35 FaviconService* GetFaviconService(TabContentsWrapper* wrapper) { | 41 FaviconService* GetFaviconService(TabContentsWrapper* wrapper) { |
| 36 return wrapper->profile()->GetFaviconService(Profile::EXPLICIT_ACCESS); | 42 return wrapper->profile()->GetFaviconService(Profile::EXPLICIT_ACCESS); |
| 37 } | 43 } |
| 38 | 44 |
| (...skipping 13 matching lines...) Expand all Loading... |
| 52 case webkit_glue::WebIntentServiceData::DISPOSITION_INLINE: | 58 case webkit_glue::WebIntentServiceData::DISPOSITION_INLINE: |
| 53 return WebIntentPickerModel::DISPOSITION_INLINE; | 59 return WebIntentPickerModel::DISPOSITION_INLINE; |
| 54 case webkit_glue::WebIntentServiceData::DISPOSITION_WINDOW: | 60 case webkit_glue::WebIntentServiceData::DISPOSITION_WINDOW: |
| 55 return WebIntentPickerModel::DISPOSITION_WINDOW; | 61 return WebIntentPickerModel::DISPOSITION_WINDOW; |
| 56 default: | 62 default: |
| 57 NOTREACHED(); | 63 NOTREACHED(); |
| 58 return WebIntentPickerModel::DISPOSITION_WINDOW; | 64 return WebIntentPickerModel::DISPOSITION_WINDOW; |
| 59 } | 65 } |
| 60 } | 66 } |
| 61 | 67 |
| 68 class URLFetcherTrampoline : public content::URLFetcherDelegate { |
| 69 public: |
| 70 typedef base::Callback<void(const content::URLFetcher* source)> Callback; |
| 71 |
| 72 explicit URLFetcherTrampoline(const Callback& callback) |
| 73 : callback_(callback) {} |
| 74 ~URLFetcherTrampoline() {} |
| 75 |
| 76 // content::URLFetcherDelegate implementation. |
| 77 virtual void OnURLFetchComplete(const content::URLFetcher* source) OVERRIDE { |
| 78 callback_.Run(source); |
| 79 delete source; |
| 80 delete this; |
| 81 } |
| 82 |
| 83 private: |
| 84 Callback callback_; |
| 85 }; |
| 86 |
| 62 } // namespace | 87 } // namespace |
| 63 | 88 |
| 64 WebIntentPickerController::WebIntentPickerController( | 89 WebIntentPickerController::WebIntentPickerController( |
| 65 TabContentsWrapper* wrapper) | 90 TabContentsWrapper* wrapper) |
| 66 : wrapper_(wrapper), | 91 : wrapper_(wrapper), |
| 67 picker_(NULL), | 92 picker_(NULL), |
| 68 picker_model_(new WebIntentPickerModel()), | 93 picker_model_(new WebIntentPickerModel()), |
| 69 pending_async_count_(0), | 94 pending_async_count_(0), |
| 70 picker_shown_(false), | 95 picker_shown_(false), |
| 71 intents_dispatcher_(NULL), | 96 intents_dispatcher_(NULL), |
| (...skipping 212 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 284 | 309 |
| 285 void WebIntentPickerController::OnCWSIntentServicesAvailable( | 310 void WebIntentPickerController::OnCWSIntentServicesAvailable( |
| 286 const CWSIntentsRegistry::IntentExtensionList& extensions) { | 311 const CWSIntentsRegistry::IntentExtensionList& extensions) { |
| 287 for (size_t i = 0; i < extensions.size(); ++i) { | 312 for (size_t i = 0; i < extensions.size(); ++i) { |
| 288 const CWSIntentsRegistry::IntentExtensionInfo& info = extensions[i]; | 313 const CWSIntentsRegistry::IntentExtensionInfo& info = extensions[i]; |
| 289 picker_model_->AddSuggestedExtension( | 314 picker_model_->AddSuggestedExtension( |
| 290 info.name, | 315 info.name, |
| 291 info.id, | 316 info.id, |
| 292 info.average_rating); | 317 info.average_rating); |
| 293 | 318 |
| 294 // TODO(binji): Fetch extension icon. | 319 pending_async_count_++; |
| 320 content::URLFetcher* icon_url_fetcher = content::URLFetcher::Create( |
| 321 0, |
| 322 info.icon_url, |
| 323 content::URLFetcher::GET, |
| 324 new URLFetcherTrampoline( |
| 325 base::Bind( |
| 326 &WebIntentPickerController::OnExtensionIconURLFetchComplete, |
| 327 weak_ptr_factory_.GetWeakPtr(), info.id))); |
| 328 |
| 329 icon_url_fetcher->SetLoadFlags( |
| 330 net::LOAD_DO_NOT_SEND_COOKIES | net::LOAD_DO_NOT_SAVE_COOKIES); |
| 331 icon_url_fetcher->SetRequestContext( |
| 332 wrapper_->profile()->GetRequestContext()); |
| 333 icon_url_fetcher->Start(); |
| 295 } | 334 } |
| 296 | 335 |
| 297 AsyncOperationFinished(); | 336 AsyncOperationFinished(); |
| 298 } | 337 } |
| 299 | 338 |
| 339 void WebIntentPickerController::OnExtensionIconURLFetchComplete( |
| 340 const string16& extension_id, const content::URLFetcher* source) { |
| 341 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); |
| 342 if (source->GetResponseCode() != 200) { |
| 343 AsyncOperationFinished(); |
| 344 return; |
| 345 } |
| 346 |
| 347 scoped_ptr<std::string> response(new std::string); |
| 348 if (!source->GetResponseAsString(response.get())) { |
| 349 AsyncOperationFinished(); |
| 350 return; |
| 351 } |
| 352 |
| 353 // I'd like to have the worker thread post a task directly to the UI thread |
| 354 // to call OnExtensionIcon[Un]Available, but this doesn't work: To do so |
| 355 // would require DecodeExtensionIconAndResize to be a member function (so it |
| 356 // has access to |this|) but a weak pointer cannot be dereferenced on a |
| 357 // thread other than the thread where the WeakPtrFactory was created. Since |
| 358 // the stored |this| pointer is weak, DecodeExtensionIconAndResize asserts |
| 359 // before it even starts. |
| 360 // |
| 361 // Instead, I package up the callbacks that I want the worker thread to call, |
| 362 // and make DecodeExtensionIconAndResize static. The stored weak |this| |
| 363 // pointers are not dereferenced until invocation (on the UI thread). |
| 364 ExtensionIconAvailableCallback available_callback = |
| 365 base::Bind( |
| 366 &WebIntentPickerController::OnExtensionIconAvailable, |
| 367 weak_ptr_factory_.GetWeakPtr(), |
| 368 extension_id); |
| 369 base::Closure unavailable_callback = |
| 370 base::Bind( |
| 371 &WebIntentPickerController::OnExtensionIconUnavailable, |
| 372 weak_ptr_factory_.GetWeakPtr(), |
| 373 extension_id); |
| 374 |
| 375 // Decode PNG and resize on worker thread. |
| 376 content::BrowserThread::PostBlockingPoolTask( |
| 377 FROM_HERE, |
| 378 base::Bind(&DecodeExtensionIconAndResize, |
| 379 base::Passed(&response), |
| 380 available_callback, |
| 381 unavailable_callback)); |
| 382 } |
| 383 |
| 384 // static |
| 385 void WebIntentPickerController::DecodeExtensionIconAndResize( |
| 386 scoped_ptr<std::string> icon_response, |
| 387 const ExtensionIconAvailableCallback& callback, |
| 388 const base::Closure& unavailable_callback) { |
| 389 SkBitmap icon_bitmap; |
| 390 if (gfx::PNGCodec::Decode( |
| 391 reinterpret_cast<const unsigned char*>(icon_response->data()), |
| 392 icon_response->length(), |
| 393 &icon_bitmap)) { |
| 394 SkBitmap resized_icon = skia::ImageOperations::Resize( |
| 395 icon_bitmap, |
| 396 skia::ImageOperations::RESIZE_BEST, |
| 397 gfx::kFaviconSize, gfx::kFaviconSize); |
| 398 gfx::Image icon_image(new SkBitmap(resized_icon)); |
| 399 |
| 400 content::BrowserThread::PostTask( |
| 401 content::BrowserThread::UI, |
| 402 FROM_HERE, |
| 403 base::Bind(callback, icon_image)); |
| 404 } else { |
| 405 content::BrowserThread::PostTask( |
| 406 content::BrowserThread::UI, |
| 407 FROM_HERE, |
| 408 unavailable_callback); |
| 409 } |
| 410 } |
| 411 |
| 412 void WebIntentPickerController::OnExtensionIconAvailable( |
| 413 const string16& extension_id, |
| 414 const gfx::Image& icon_image) { |
| 415 picker_model_->SetSuggestedExtensionIconWithId(extension_id, icon_image); |
| 416 AsyncOperationFinished(); |
| 417 } |
| 418 |
| 419 void WebIntentPickerController::OnExtensionIconUnavailable( |
| 420 const string16& extension_id) { |
| 421 AsyncOperationFinished(); |
| 422 } |
| 423 |
| 300 void WebIntentPickerController::AsyncOperationFinished() { | 424 void WebIntentPickerController::AsyncOperationFinished() { |
| 425 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); |
| 301 if (--pending_async_count_ == 0) { | 426 if (--pending_async_count_ == 0) { |
| 302 picker_->OnPendingAsyncCompleted(); | 427 picker_->OnPendingAsyncCompleted(); |
| 303 } | 428 } |
| 304 } | 429 } |
| 305 | 430 |
| 306 void WebIntentPickerController::ClosePicker() { | 431 void WebIntentPickerController::ClosePicker() { |
| 307 if (picker_) { | 432 if (picker_) { |
| 308 picker_->Close(); | 433 picker_->Close(); |
| 309 } | 434 } |
| 310 } | 435 } |
| OLD | NEW |