| 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/extensions/webstore_installer.h" |
| 12 #include "chrome/browser/favicon/favicon_service.h" | 12 #include "chrome/browser/favicon/favicon_service.h" |
| 13 #include "chrome/browser/intents/default_web_intent_service.h" | 13 #include "chrome/browser/intents/default_web_intent_service.h" |
| 14 #include "chrome/browser/intents/web_intents_registry_factory.h" | 14 #include "chrome/browser/intents/web_intents_registry_factory.h" |
| 15 #include "chrome/browser/intents/cws_intents_registry_factory.h" | 15 #include "chrome/browser/intents/cws_intents_registry_factory.h" |
| 16 #include "chrome/browser/profiles/profile.h" | 16 #include "chrome/browser/profiles/profile.h" |
| 17 #include "chrome/browser/tab_contents/tab_util.h" | 17 #include "chrome/browser/tab_contents/tab_util.h" |
| 18 #include "chrome/browser/tabs/tab_strip_model.h" | 18 #include "chrome/browser/tabs/tab_strip_model.h" |
| 19 #include "chrome/browser/ui/browser.h" |
| 19 #include "chrome/browser/ui/browser_list.h" | 20 #include "chrome/browser/ui/browser_list.h" |
| 20 #include "chrome/browser/ui/browser_navigator.h" | 21 #include "chrome/browser/ui/browser_navigator.h" |
| 21 #include "chrome/browser/ui/intents/web_intent_picker.h" | 22 #include "chrome/browser/ui/intents/web_intent_picker.h" |
| 22 #include "chrome/browser/ui/intents/web_intent_picker_model.h" | 23 #include "chrome/browser/ui/intents/web_intent_picker_model.h" |
| 23 #include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h" | 24 #include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h" |
| 24 #include "chrome/browser/webdata/web_data_service.h" | 25 #include "chrome/browser/webdata/web_data_service.h" |
| 25 #include "chrome/common/chrome_notification_types.h" | 26 #include "chrome/common/chrome_notification_types.h" |
| 26 #include "content/public/browser/browser_thread.h" | 27 #include "content/public/browser/browser_thread.h" |
| 28 #include "content/public/browser/navigation_controller.h" |
| 27 #include "content/public/browser/notification_source.h" | 29 #include "content/public/browser/notification_source.h" |
| 28 #include "content/public/browser/web_contents.h" | 30 #include "content/public/browser/web_contents.h" |
| 29 #include "content/public/browser/web_intents_dispatcher.h" | 31 #include "content/public/browser/web_intents_dispatcher.h" |
| 30 #include "content/public/common/url_fetcher.h" | 32 #include "content/public/common/url_fetcher.h" |
| 31 #include "content/public/common/url_fetcher_delegate.h" | 33 #include "content/public/common/url_fetcher_delegate.h" |
| 32 #include "net/base/load_flags.h" | 34 #include "net/base/load_flags.h" |
| 33 #include "skia/ext/image_operations.h" | 35 #include "skia/ext/image_operations.h" |
| 34 #include "ui/gfx/codec/png_codec.h" | 36 #include "ui/gfx/codec/png_codec.h" |
| 35 #include "ui/gfx/favicon_size.h" | 37 #include "ui/gfx/favicon_size.h" |
| 36 #include "ui/gfx/image/image.h" | 38 #include "ui/gfx/image/image.h" |
| (...skipping 22 matching lines...) Expand all Loading... |
| 59 case webkit_glue::WebIntentServiceData::DISPOSITION_INLINE: | 61 case webkit_glue::WebIntentServiceData::DISPOSITION_INLINE: |
| 60 return WebIntentPickerModel::DISPOSITION_INLINE; | 62 return WebIntentPickerModel::DISPOSITION_INLINE; |
| 61 case webkit_glue::WebIntentServiceData::DISPOSITION_WINDOW: | 63 case webkit_glue::WebIntentServiceData::DISPOSITION_WINDOW: |
| 62 return WebIntentPickerModel::DISPOSITION_WINDOW; | 64 return WebIntentPickerModel::DISPOSITION_WINDOW; |
| 63 default: | 65 default: |
| 64 NOTREACHED(); | 66 NOTREACHED(); |
| 65 return WebIntentPickerModel::DISPOSITION_WINDOW; | 67 return WebIntentPickerModel::DISPOSITION_WINDOW; |
| 66 } | 68 } |
| 67 } | 69 } |
| 68 | 70 |
| 71 // Self-deleting trampoline that forwards a WebIntentsRegistry response to a |
| 72 // callback. |
| 73 class WebIntentsRegistryTrampoline : public WebIntentsRegistry::Consumer { |
| 74 public: |
| 75 typedef std::vector<webkit_glue::WebIntentServiceData> IntentServices; |
| 76 typedef base::Callback<void(const IntentServices&)> ForwardingCallback; |
| 77 |
| 78 explicit WebIntentsRegistryTrampoline(const ForwardingCallback& callback); |
| 79 ~WebIntentsRegistryTrampoline(); |
| 80 |
| 81 // WebIntentsRegistry::Consumer implementation. |
| 82 virtual void OnIntentsQueryDone( |
| 83 WebIntentsRegistry::QueryID, |
| 84 const std::vector<webkit_glue::WebIntentServiceData>& services) OVERRIDE; |
| 85 virtual void OnIntentsDefaultsQueryDone( |
| 86 WebIntentsRegistry::QueryID, |
| 87 const DefaultWebIntentService& default_service) OVERRIDE {} |
| 88 |
| 89 private: |
| 90 // Forwarding callback from |OnIntentsQueryDone|. |
| 91 ForwardingCallback callback_; |
| 92 }; |
| 93 |
| 94 WebIntentsRegistryTrampoline::WebIntentsRegistryTrampoline( |
| 95 const ForwardingCallback& callback) |
| 96 : callback_(callback) { |
| 97 } |
| 98 |
| 99 WebIntentsRegistryTrampoline::~WebIntentsRegistryTrampoline() { |
| 100 } |
| 101 |
| 102 void WebIntentsRegistryTrampoline::OnIntentsQueryDone( |
| 103 WebIntentsRegistry::QueryID, |
| 104 const std::vector<webkit_glue::WebIntentServiceData>& services) { |
| 105 DCHECK(!callback_.is_null()); |
| 106 callback_.Run(services); |
| 107 delete this; |
| 108 } |
| 109 |
| 110 // Self-deleting trampoline that forwards A URLFetcher response to a callback. |
| 69 class URLFetcherTrampoline : public content::URLFetcherDelegate { | 111 class URLFetcherTrampoline : public content::URLFetcherDelegate { |
| 70 public: | 112 public: |
| 71 typedef base::Callback<void(const content::URLFetcher* source)> Callback; | 113 typedef base::Callback<void(const content::URLFetcher* source)> |
| 114 ForwardingCallback; |
| 72 | 115 |
| 73 explicit URLFetcherTrampoline(const Callback& callback) | 116 explicit URLFetcherTrampoline(const ForwardingCallback& callback); |
| 74 : callback_(callback) {} | 117 ~URLFetcherTrampoline(); |
| 75 ~URLFetcherTrampoline() {} | |
| 76 | 118 |
| 77 // content::URLFetcherDelegate implementation. | 119 // content::URLFetcherDelegate implementation. |
| 78 virtual void OnURLFetchComplete(const content::URLFetcher* source) OVERRIDE { | 120 virtual void OnURLFetchComplete(const content::URLFetcher* source) OVERRIDE; |
| 79 callback_.Run(source); | |
| 80 delete source; | |
| 81 delete this; | |
| 82 } | |
| 83 | 121 |
| 84 private: | 122 private: |
| 85 Callback callback_; | 123 // Fowarding callback from |OnURLFetchComplete|. |
| 124 ForwardingCallback callback_; |
| 86 }; | 125 }; |
| 87 | 126 |
| 127 URLFetcherTrampoline::URLFetcherTrampoline(const ForwardingCallback& callback) |
| 128 : callback_(callback) { |
| 129 } |
| 130 |
| 131 URLFetcherTrampoline::~URLFetcherTrampoline() { |
| 132 } |
| 133 |
| 134 void URLFetcherTrampoline::OnURLFetchComplete( |
| 135 const content::URLFetcher* source) { |
| 136 DCHECK(!callback_.is_null()); |
| 137 callback_.Run(source); |
| 138 delete source; |
| 139 delete this; |
| 140 } |
| 141 |
| 88 } // namespace | 142 } // namespace |
| 89 | 143 |
| 90 WebIntentPickerController::WebIntentPickerController( | 144 WebIntentPickerController::WebIntentPickerController( |
| 91 TabContentsWrapper* wrapper) | 145 TabContentsWrapper* wrapper) |
| 92 : wrapper_(wrapper), | 146 : wrapper_(wrapper), |
| 93 picker_(NULL), | 147 picker_(NULL), |
| 94 picker_model_(new WebIntentPickerModel()), | 148 picker_model_(new WebIntentPickerModel()), |
| 95 pending_async_count_(0), | 149 pending_async_count_(0), |
| 96 picker_shown_(false), | 150 picker_shown_(false), |
| 97 intents_dispatcher_(NULL), | 151 intents_dispatcher_(NULL), |
| (...skipping 19 matching lines...) Expand all Loading... |
| 117 } | 171 } |
| 118 | 172 |
| 119 void WebIntentPickerController::ShowDialog(Browser* browser, | 173 void WebIntentPickerController::ShowDialog(Browser* browser, |
| 120 const string16& action, | 174 const string16& action, |
| 121 const string16& type) { | 175 const string16& type) { |
| 122 // Only show a picker once. | 176 // Only show a picker once. |
| 123 if (picker_shown_) | 177 if (picker_shown_) |
| 124 return; | 178 return; |
| 125 | 179 |
| 126 picker_model_->Clear(); | 180 picker_model_->Clear(); |
| 181 picker_model_->set_action(action); |
| 182 picker_model_->set_mimetype(type); |
| 127 | 183 |
| 128 // If picker is non-NULL, it was set by a test. | 184 // If picker is non-NULL, it was set by a test. |
| 129 if (picker_ == NULL) { | 185 if (picker_ == NULL) { |
| 130 picker_ = WebIntentPicker::Create(browser, wrapper_, this, | 186 picker_ = WebIntentPicker::Create(browser, wrapper_, this, |
| 131 picker_model_.get()); | 187 picker_model_.get()); |
| 132 } | 188 } |
| 133 | 189 |
| 134 picker_shown_ = true; | 190 picker_shown_ = true; |
| 135 pending_async_count_+= 2; | 191 pending_async_count_+= 2; |
| 136 GetWebIntentsRegistry(wrapper_)->GetIntentServices(action, type, this); | 192 GetWebIntentsRegistry(wrapper_)->GetIntentServices( |
| 137 GetCWSIntentsRegistry(wrapper_)->GetIntentServices(action, type, | 193 action, type, |
| 194 // WebIntentsRegistryTrampoline is self-deleting. |
| 195 new WebIntentsRegistryTrampoline( |
| 196 base::Bind(&WebIntentPickerController::OnWebIntentServicesAvailable, |
| 197 weak_ptr_factory_.GetWeakPtr()))); |
| 198 GetCWSIntentsRegistry(wrapper_)->GetIntentServices( |
| 199 action, type, |
| 138 base::Bind(&WebIntentPickerController::OnCWSIntentServicesAvailable, | 200 base::Bind(&WebIntentPickerController::OnCWSIntentServicesAvailable, |
| 139 weak_ptr_factory_.GetWeakPtr())); | 201 weak_ptr_factory_.GetWeakPtr())); |
| 140 } | 202 } |
| 141 | 203 |
| 142 void WebIntentPickerController::Observe( | 204 void WebIntentPickerController::Observe( |
| 143 int type, | 205 int type, |
| 144 const content::NotificationSource& source, | 206 const content::NotificationSource& source, |
| 145 const content::NotificationDetails& details) { | 207 const content::NotificationDetails& details) { |
| 146 DCHECK(type == content::NOTIFICATION_LOAD_START || | 208 DCHECK(type == content::NOTIFICATION_LOAD_START || |
| 147 type == content::NOTIFICATION_TAB_CLOSING); | 209 type == content::NOTIFICATION_TAB_CLOSING); |
| (...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 200 break; | 262 break; |
| 201 } | 263 } |
| 202 } | 264 } |
| 203 | 265 |
| 204 void WebIntentPickerController::OnInlineDispositionWebContentsCreated( | 266 void WebIntentPickerController::OnInlineDispositionWebContentsCreated( |
| 205 content::WebContents* web_contents) { | 267 content::WebContents* web_contents) { |
| 206 if (web_contents) | 268 if (web_contents) |
| 207 intents_dispatcher_->DispatchIntent(web_contents); | 269 intents_dispatcher_->DispatchIntent(web_contents); |
| 208 } | 270 } |
| 209 | 271 |
| 272 void WebIntentPickerController::OnExtensionInstallRequested( |
| 273 const std::string& id) { |
| 274 webstore_installer_ = new WebstoreInstaller( |
| 275 wrapper_->profile(), this, &wrapper_->web_contents()->GetController(), id, |
| 276 WebstoreInstaller::FLAG_INLINE_INSTALL); |
| 277 |
| 278 pending_async_count_++; |
| 279 webstore_installer_->Start(); |
| 280 } |
| 281 |
| 210 void WebIntentPickerController::OnCancelled() { | 282 void WebIntentPickerController::OnCancelled() { |
| 211 if (!intents_dispatcher_) | 283 if (!intents_dispatcher_) |
| 212 return; | 284 return; |
| 213 | 285 |
| 214 if (service_tab_) { | 286 if (service_tab_) { |
| 215 intents_dispatcher_->SendReplyMessage( | 287 intents_dispatcher_->SendReplyMessage( |
| 216 webkit_glue::WEB_INTENT_SERVICE_TAB_CLOSED, string16()); | 288 webkit_glue::WEB_INTENT_SERVICE_TAB_CLOSED, string16()); |
| 217 } else { | 289 } else { |
| 218 intents_dispatcher_->SendReplyMessage( | 290 intents_dispatcher_->SendReplyMessage( |
| 219 webkit_glue::WEB_INTENT_PICKER_CANCELLED, string16()); | 291 webkit_glue::WEB_INTENT_PICKER_CANCELLED, string16()); |
| 220 } | 292 } |
| 221 | 293 |
| 222 ClosePicker(); | 294 ClosePicker(); |
| 223 } | 295 } |
| 224 | 296 |
| 225 void WebIntentPickerController::OnClosing() { | 297 void WebIntentPickerController::OnClosing() { |
| 226 picker_shown_ = false; | 298 picker_shown_ = false; |
| 227 picker_ = NULL; | 299 picker_ = NULL; |
| 228 } | 300 } |
| 229 | 301 |
| 302 void WebIntentPickerController::OnExtensionInstallSuccess( |
| 303 const std::string& id) { |
| 304 picker_->OnExtensionInstallSuccess(id); |
| 305 pending_async_count_++; |
| 306 GetWebIntentsRegistry(wrapper_)->GetIntentServicesForExtensionFilter( |
| 307 picker_model_->action(), |
| 308 picker_model_->mimetype(), |
| 309 id, |
| 310 new WebIntentsRegistryTrampoline( |
| 311 base::Bind( |
| 312 &WebIntentPickerController::OnExtensionInstallServiceAvailable, |
| 313 weak_ptr_factory_.GetWeakPtr()))); |
| 314 AsyncOperationFinished(); |
| 315 } |
| 316 |
| 317 void WebIntentPickerController::OnExtensionInstallFailure( |
| 318 const std::string& id, |
| 319 const std::string& error) { |
| 320 picker_->OnExtensionInstallFailure(id); |
| 321 AsyncOperationFinished(); |
| 322 } |
| 323 |
| 230 void WebIntentPickerController::OnSendReturnMessage( | 324 void WebIntentPickerController::OnSendReturnMessage( |
| 231 webkit_glue::WebIntentReplyType reply_type) { | 325 webkit_glue::WebIntentReplyType reply_type) { |
| 232 ClosePicker(); | 326 ClosePicker(); |
| 233 | 327 |
| 234 if (service_tab_ && | 328 if (service_tab_ && |
| 235 reply_type != webkit_glue::WEB_INTENT_SERVICE_TAB_CLOSED) { | 329 reply_type != webkit_glue::WEB_INTENT_SERVICE_TAB_CLOSED) { |
| 236 int index = TabStripModel::kNoTab; | 330 int index = TabStripModel::kNoTab; |
| 237 Browser* browser = Browser::GetBrowserForController( | 331 Browser* browser = Browser::GetBrowserForController( |
| 238 &service_tab_->GetController(), &index); | 332 &service_tab_->GetController(), &index); |
| 239 if (browser) { | 333 if (browser) { |
| 240 browser->tabstrip_model()->CloseTabContentsAt( | 334 browser->tabstrip_model()->CloseTabContentsAt( |
| 241 index, TabStripModel::CLOSE_CREATE_HISTORICAL_TAB); | 335 index, TabStripModel::CLOSE_CREATE_HISTORICAL_TAB); |
| 242 | 336 |
| 243 // Activate source tab. | 337 // Activate source tab. |
| 244 Browser* source_browser = | 338 Browser* source_browser = |
| 245 BrowserList::FindBrowserWithWebContents(wrapper_->web_contents()); | 339 BrowserList::FindBrowserWithWebContents(wrapper_->web_contents()); |
| 246 if (source_browser) { | 340 if (source_browser) { |
| 247 int source_index = | 341 int source_index = |
| 248 source_browser->tabstrip_model()->GetIndexOfTabContents(wrapper_); | 342 source_browser->tabstrip_model()->GetIndexOfTabContents(wrapper_); |
| 249 source_browser->ActivateTabAt(source_index, false); | 343 source_browser->ActivateTabAt(source_index, false); |
| 250 } | 344 } |
| 251 } | 345 } |
| 252 service_tab_ = NULL; | 346 service_tab_ = NULL; |
| 253 } | 347 } |
| 254 | 348 |
| 255 intents_dispatcher_ = NULL; | 349 intents_dispatcher_ = NULL; |
| 256 } | 350 } |
| 257 | 351 |
| 258 void WebIntentPickerController::OnIntentsQueryDone( | 352 void WebIntentPickerController::OnWebIntentServicesAvailable( |
| 259 WebIntentsRegistry::QueryID, | |
| 260 const std::vector<webkit_glue::WebIntentServiceData>& services) { | 353 const std::vector<webkit_glue::WebIntentServiceData>& services) { |
| 261 FaviconService* favicon_service = GetFaviconService(wrapper_); | 354 FaviconService* favicon_service = GetFaviconService(wrapper_); |
| 262 for (size_t i = 0; i < services.size(); ++i) { | 355 for (size_t i = 0; i < services.size(); ++i) { |
| 263 picker_model_->AddInstalledService( | 356 picker_model_->AddInstalledService( |
| 264 services[i].title, | 357 services[i].title, |
| 265 services[i].service_url, | 358 services[i].service_url, |
| 266 ConvertDisposition(services[i].disposition)); | 359 ConvertDisposition(services[i].disposition)); |
| 267 | 360 |
| 268 pending_async_count_++; | 361 pending_async_count_++; |
| 269 FaviconService::Handle handle = favicon_service->GetFaviconForURL( | 362 FaviconService::Handle handle = favicon_service->GetFaviconForURL( |
| 270 services[i].service_url, | 363 services[i].service_url, |
| 271 history::FAVICON, | 364 history::FAVICON, |
| 272 &favicon_consumer_, | 365 &favicon_consumer_, |
| 273 base::Bind( | 366 base::Bind( |
| 274 &WebIntentPickerController::OnFaviconDataAvailable, | 367 &WebIntentPickerController::OnFaviconDataAvailable, |
| 275 weak_ptr_factory_.GetWeakPtr())); | 368 weak_ptr_factory_.GetWeakPtr())); |
| 276 favicon_consumer_.SetClientData(favicon_service, handle, i); | 369 favicon_consumer_.SetClientData(favicon_service, handle, i); |
| 277 } | 370 } |
| 278 | 371 |
| 279 AsyncOperationFinished(); | 372 AsyncOperationFinished(); |
| 280 } | 373 } |
| 281 | 374 |
| 282 void WebIntentPickerController::OnIntentsDefaultsQueryDone( | |
| 283 WebIntentsRegistry::QueryID, | |
| 284 const DefaultWebIntentService& default_service) { | |
| 285 } | |
| 286 | |
| 287 void WebIntentPickerController::OnFaviconDataAvailable( | 375 void WebIntentPickerController::OnFaviconDataAvailable( |
| 288 FaviconService::Handle handle, history::FaviconData favicon_data) { | 376 FaviconService::Handle handle, history::FaviconData favicon_data) { |
| 289 size_t index = favicon_consumer_.GetClientDataForCurrentRequest(); | 377 size_t index = favicon_consumer_.GetClientDataForCurrentRequest(); |
| 290 if (favicon_data.is_valid()) { | 378 if (favicon_data.is_valid()) { |
| 291 SkBitmap icon_bitmap; | 379 SkBitmap icon_bitmap; |
| 292 | 380 |
| 293 if (gfx::PNGCodec::Decode(favicon_data.image_data->front(), | 381 if (gfx::PNGCodec::Decode(favicon_data.image_data->front(), |
| 294 favicon_data.image_data->size(), | 382 favicon_data.image_data->size(), |
| 295 &icon_bitmap)) { | 383 &icon_bitmap)) { |
| 296 gfx::Image icon_image(new SkBitmap(icon_bitmap)); | 384 gfx::Image icon_image(new SkBitmap(icon_bitmap)); |
| (...skipping 112 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 409 const gfx::Image& icon_image) { | 497 const gfx::Image& icon_image) { |
| 410 picker_model_->SetSuggestedExtensionIconWithId(extension_id, icon_image); | 498 picker_model_->SetSuggestedExtensionIconWithId(extension_id, icon_image); |
| 411 AsyncOperationFinished(); | 499 AsyncOperationFinished(); |
| 412 } | 500 } |
| 413 | 501 |
| 414 void WebIntentPickerController::OnExtensionIconUnavailable( | 502 void WebIntentPickerController::OnExtensionIconUnavailable( |
| 415 const string16& extension_id) { | 503 const string16& extension_id) { |
| 416 AsyncOperationFinished(); | 504 AsyncOperationFinished(); |
| 417 } | 505 } |
| 418 | 506 |
| 507 void WebIntentPickerController::OnExtensionInstallServiceAvailable( |
| 508 const std::vector<webkit_glue::WebIntentServiceData>& services) { |
| 509 DCHECK(services.size() > 0); |
| 510 |
| 511 // TODO(binji): We're going to need to disambiguate if there are multiple |
| 512 // services. For now, just choose the first. |
| 513 const webkit_glue::WebIntentServiceData& service_data = services[0]; |
| 514 OnServiceChosen( |
| 515 service_data.service_url, |
| 516 ConvertDisposition(service_data.disposition)); |
| 517 AsyncOperationFinished(); |
| 518 } |
| 519 |
| 419 void WebIntentPickerController::AsyncOperationFinished() { | 520 void WebIntentPickerController::AsyncOperationFinished() { |
| 420 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); | 521 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); |
| 421 if (--pending_async_count_ == 0) { | 522 if (--pending_async_count_ == 0) { |
| 422 picker_->OnPendingAsyncCompleted(); | 523 picker_->OnPendingAsyncCompleted(); |
| 423 } | 524 } |
| 424 } | 525 } |
| 425 | 526 |
| 426 void WebIntentPickerController::ClosePicker() { | 527 void WebIntentPickerController::ClosePicker() { |
| 427 if (picker_) { | 528 if (picker_) { |
| 428 picker_->Close(); | 529 picker_->Close(); |
| 429 } | 530 } |
| 430 } | 531 } |
| OLD | NEW |