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 "base/memory/scoped_ptr.h" | 11 #include "base/memory/scoped_ptr.h" |
12 #include "base/utf_string_conversions.h" | 12 #include "base/utf_string_conversions.h" |
13 #include "chrome/browser/extensions/webstore_installer.h" | 13 #include "chrome/browser/extensions/webstore_installer.h" |
14 #include "chrome/browser/favicon/favicon_service.h" | 14 #include "chrome/browser/favicon/favicon_service.h" |
15 #include "chrome/browser/intents/default_web_intent_service.h" | 15 #include "chrome/browser/intents/default_web_intent_service.h" |
16 #include "chrome/browser/intents/web_intents_registry_factory.h" | 16 #include "chrome/browser/intents/web_intents_registry_factory.h" |
17 #include "chrome/browser/intents/cws_intents_registry_factory.h" | 17 #include "chrome/browser/intents/cws_intents_registry_factory.h" |
18 #include "chrome/browser/profiles/profile.h" | 18 #include "chrome/browser/profiles/profile.h" |
19 #include "chrome/browser/tab_contents/tab_util.h" | 19 #include "chrome/browser/tab_contents/tab_util.h" |
20 #include "chrome/browser/tabs/tab_strip_model.h" | 20 #include "chrome/browser/tabs/tab_strip_model.h" |
21 #include "chrome/browser/ui/browser.h" | 21 #include "chrome/browser/ui/browser.h" |
22 #include "chrome/browser/ui/browser_list.h" | 22 #include "chrome/browser/ui/browser_list.h" |
23 #include "chrome/browser/ui/browser_navigator.h" | 23 #include "chrome/browser/ui/browser_navigator.h" |
24 #include "chrome/browser/ui/intents/web_intent_picker.h" | 24 #include "chrome/browser/ui/intents/web_intent_picker.h" |
25 #include "chrome/browser/ui/intents/web_intent_picker_model.h" | 25 #include "chrome/browser/ui/intents/web_intent_picker_model.h" |
26 #include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h" | 26 #include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h" |
27 #include "chrome/browser/webdata/web_data_service.h" | 27 #include "chrome/browser/webdata/web_data_service.h" |
28 #include "chrome/common/chrome_notification_types.h" | 28 #include "chrome/common/chrome_notification_types.h" |
29 #include "chrome/common/url_constants.h" | |
29 #include "content/public/browser/browser_thread.h" | 30 #include "content/public/browser/browser_thread.h" |
30 #include "content/public/browser/navigation_controller.h" | 31 #include "content/public/browser/navigation_controller.h" |
31 #include "content/public/browser/notification_source.h" | 32 #include "content/public/browser/notification_source.h" |
32 #include "content/public/browser/web_contents.h" | 33 #include "content/public/browser/web_contents.h" |
33 #include "content/public/browser/web_intents_dispatcher.h" | 34 #include "content/public/browser/web_intents_dispatcher.h" |
34 #include "content/public/common/url_fetcher.h" | 35 #include "content/public/common/url_fetcher.h" |
35 #include "content/public/common/url_fetcher_delegate.h" | 36 #include "content/public/common/url_fetcher_delegate.h" |
36 #include "grit/generated_resources.h" | 37 #include "grit/generated_resources.h" |
37 #include "net/base/load_flags.h" | 38 #include "net/base/load_flags.h" |
38 #include "skia/ext/image_operations.h" | 39 #include "skia/ext/image_operations.h" |
(...skipping 118 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
157 intents_dispatcher_ = intents_dispatcher; | 158 intents_dispatcher_ = intents_dispatcher; |
158 intents_dispatcher_->RegisterReplyNotification( | 159 intents_dispatcher_->RegisterReplyNotification( |
159 base::Bind(&WebIntentPickerController::OnSendReturnMessage, | 160 base::Bind(&WebIntentPickerController::OnSendReturnMessage, |
160 weak_ptr_factory_.GetWeakPtr())); | 161 weak_ptr_factory_.GetWeakPtr())); |
161 } | 162 } |
162 | 163 |
163 void WebIntentPickerController::ShowDialog(Browser* browser, | 164 void WebIntentPickerController::ShowDialog(Browser* browser, |
164 const string16& action, | 165 const string16& action, |
165 const string16& type) { | 166 const string16& type) { |
166 // Only show a picker once. | 167 // Only show a picker once. |
167 if (picker_shown_) | 168 // ??? Is this good enough? There's a hole if we don't create the picker |
168 return; | 169 // in this method, but only after disk retrieval. |
169 | |
170 // TODO(binji): Figure out what to do when intents are invoked from incognito | 170 // TODO(binji): Figure out what to do when intents are invoked from incognito |
171 // mode. | 171 // mode. |
172 if (wrapper_->profile()->IsOffTheRecord()) | 172 if (picker_shown_ || wrapper_->profile()->IsOffTheRecord()) { |
173 if (intents_dispatcher_) { | |
174 intents_dispatcher_->SendReplyMessage( | |
175 webkit_glue::WEB_INTENT_REPLY_FAILURE, ASCIIToUTF16("")); | |
176 } | |
173 return; | 177 return; |
178 } | |
174 | 179 |
180 browser_ = browser; | |
181 action_ = action; | |
175 picker_model_->Clear(); | 182 picker_model_->Clear(); |
176 picker_model_->set_action(action); | 183 picker_model_->set_action(action); |
177 picker_model_->set_mimetype(type); | 184 picker_model_->set_mimetype(type); |
178 | 185 |
179 // If picker is non-NULL, it was set by a test. | 186 // If the intent is explicit, skip showing the picker. |
180 if (picker_ == NULL) { | 187 if (intents_dispatcher_) { |
181 picker_ = WebIntentPicker::Create(browser, wrapper_, this, | 188 const GURL& service = intents_dispatcher_->GetIntent().service; |
182 picker_model_.get()); | 189 // TODO(gbillock): When we can parse pages for the intent tag, |
190 // take out this requirement that explicit intents dispatch to | |
191 // extension urls. | |
192 if (!service.is_valid() || !service.SchemeIs(chrome::kExtensionScheme)) { | |
193 intents_dispatcher_->SendReplyMessage( | |
194 webkit_glue::WEB_INTENT_REPLY_FAILURE, ASCIIToUTF16( | |
195 "Only extension urls are supported for explicit invocation")); | |
196 return; | |
197 } | |
198 | |
199 // We get services from the registry because that must retrieve the | |
200 // registered extension page for this action/type if it is permitted to be | |
201 // dispatched. | |
202 pending_async_count_++; | |
203 GetWebIntentsRegistry(wrapper_)->GetIntentServices( | |
204 action, type, base::Bind( | |
205 &WebIntentPickerController::WebIntentServicesForExplicitIntent, | |
206 weak_ptr_factory_.GetWeakPtr())); | |
207 return; | |
183 } | 208 } |
184 | 209 |
185 picker_->SetActionString(GetIntentActionString(UTF16ToUTF8(action))); | 210 pending_async_count_ += 2; |
groby-ooo-7-16
2012/04/24 21:36:51
Why exactly do we keep a pending count? (Nobody im
Greg Billock
2012/04/25 16:04:32
I thought it was used to guide the operation of th
| |
211 pending_registry_calls_count_ += 1; | |
groby-ooo-7-16
2012/04/24 21:36:51
Why add 1 here? We already do that when we call th
Greg Billock
2012/04/25 16:04:32
There's now two registry callbacks we need to wait
| |
186 | 212 |
187 picker_shown_ = true; | |
188 pending_async_count_+= 2; | |
189 GetWebIntentsRegistry(wrapper_)->GetIntentServices( | 213 GetWebIntentsRegistry(wrapper_)->GetIntentServices( |
190 action, type, | 214 action, type, |
191 base::Bind(&WebIntentPickerController::OnWebIntentServicesAvailable, | 215 base::Bind(&WebIntentPickerController::OnWebIntentServicesAvailable, |
192 weak_ptr_factory_.GetWeakPtr())); | 216 weak_ptr_factory_.GetWeakPtr())); |
217 | |
218 GURL invoking_url = wrapper_->web_contents()->GetURL(); | |
219 if (invoking_url.is_valid()) { | |
220 pending_async_count_++; | |
221 pending_registry_calls_count_++; | |
222 GetWebIntentsRegistry(wrapper_)->GetDefaultIntentService( | |
223 action, type, invoking_url, | |
224 base::Bind(&WebIntentPickerController::OnWebIntentDefaultsAvailable, | |
225 weak_ptr_factory_.GetWeakPtr())); | |
226 } | |
227 | |
193 GetCWSIntentsRegistry(wrapper_)->GetIntentServices( | 228 GetCWSIntentsRegistry(wrapper_)->GetIntentServices( |
194 action, type, | 229 action, type, |
195 base::Bind(&WebIntentPickerController::OnCWSIntentServicesAvailable, | 230 base::Bind(&WebIntentPickerController::OnCWSIntentServicesAvailable, |
196 weak_ptr_factory_.GetWeakPtr())); | 231 weak_ptr_factory_.GetWeakPtr())); |
197 } | 232 } |
198 | 233 |
199 void WebIntentPickerController::Observe( | 234 void WebIntentPickerController::Observe( |
200 int type, | 235 int type, |
201 const content::NotificationSource& source, | 236 const content::NotificationSource& source, |
202 const content::NotificationDetails& details) { | 237 const content::NotificationDetails& details) { |
203 DCHECK(type == content::NOTIFICATION_LOAD_START || | 238 DCHECK(type == content::NOTIFICATION_LOAD_START || |
204 type == chrome::NOTIFICATION_TAB_CLOSING); | 239 type == chrome::NOTIFICATION_TAB_CLOSING); |
205 ClosePicker(); | 240 ClosePicker(); |
206 } | 241 } |
207 | 242 |
208 void WebIntentPickerController::OnServiceChosen(const GURL& url, | 243 void WebIntentPickerController::OnServiceChosen(const GURL& url, |
209 Disposition disposition) { | 244 Disposition disposition) { |
210 switch (disposition) { | 245 switch (disposition) { |
211 case WebIntentPickerModel::DISPOSITION_INLINE: | 246 case WebIntentPickerModel::DISPOSITION_INLINE: |
212 // Set the model to inline disposition. It will notify the picker which | 247 // Set the model to inline disposition. It will notify the picker which |
213 // will respond (via OnInlineDispositionWebContentsCreated) with the | 248 // will respond (via OnInlineDispositionWebContentsCreated) with the |
214 // WebContents to dispatch the intent to. | 249 // WebContents to dispatch the intent to. |
215 picker_model_->SetInlineDisposition(url); | 250 picker_model_->SetInlineDisposition(url); |
216 break; | 251 break; |
217 | 252 |
218 case WebIntentPickerModel::DISPOSITION_WINDOW: { | 253 case WebIntentPickerModel::DISPOSITION_WINDOW: { |
219 // TODO(gbillock): This really only handles the 'window' disposition in a | |
220 // quite prototype way. We need to flesh out what happens to the picker | |
221 // during the lifetime of the service url context, and that may mean we | |
222 // need to pass more information into the injector to find the picker | |
223 // again and close it. | |
224 int index = TabStripModel::kNoTab; | 254 int index = TabStripModel::kNoTab; |
255 // TODO(gbillock): use browser_? | |
groby-ooo-7-16
2012/04/24 21:36:51
I believe there has been a CL (or there is one in
Greg Billock
2012/04/25 16:04:32
got rid of this. I'll add a todo to scrap the Show
| |
225 Browser* browser = Browser::GetBrowserForController( | 256 Browser* browser = Browser::GetBrowserForController( |
226 &wrapper_->web_contents()->GetController(), &index); | 257 &wrapper_->web_contents()->GetController(), &index); |
227 TabContentsWrapper* contents = Browser::TabContentsFactory( | 258 TabContentsWrapper* contents = Browser::TabContentsFactory( |
228 browser->profile(), | 259 browser->profile(), |
229 tab_util::GetSiteInstanceForNewTab( | 260 tab_util::GetSiteInstanceForNewTab( |
230 NULL, browser->profile(), url), | 261 NULL, browser->profile(), url), |
231 MSG_ROUTING_NONE, NULL, NULL); | 262 MSG_ROUTING_NONE, NULL, NULL); |
232 | 263 |
233 intents_dispatcher_->DispatchIntent(contents->web_contents()); | 264 intents_dispatcher_->DispatchIntent(contents->web_contents()); |
234 service_tab_ = contents->web_contents(); | 265 service_tab_ = contents->web_contents(); |
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
270 wrapper_->profile(), this, &wrapper_->web_contents()->GetController(), id, | 301 wrapper_->profile(), this, &wrapper_->web_contents()->GetController(), id, |
271 scoped_ptr<WebstoreInstaller::Approval>(NULL), | 302 scoped_ptr<WebstoreInstaller::Approval>(NULL), |
272 WebstoreInstaller::FLAG_INLINE_INSTALL); | 303 WebstoreInstaller::FLAG_INLINE_INSTALL); |
273 | 304 |
274 pending_async_count_++; | 305 pending_async_count_++; |
275 installer->Start(); | 306 installer->Start(); |
276 } | 307 } |
277 | 308 |
278 void WebIntentPickerController::OnExtensionLinkClicked(const std::string& id) { | 309 void WebIntentPickerController::OnExtensionLinkClicked(const std::string& id) { |
279 // Navigate from source tab. | 310 // Navigate from source tab. |
311 // TODO(gbillock): use browser_? | |
groby-ooo-7-16
2012/04/24 21:36:51
See above
Greg Billock
2012/04/25 16:04:32
Done.
| |
280 Browser* browser = | 312 Browser* browser = |
281 BrowserList::FindBrowserWithWebContents(wrapper_->web_contents()); | 313 BrowserList::FindBrowserWithWebContents(wrapper_->web_contents()); |
282 GURL extension_url(extension_urls::GetWebstoreItemDetailURLPrefix() + id); | 314 GURL extension_url(extension_urls::GetWebstoreItemDetailURLPrefix() + id); |
283 browser::NavigateParams params(browser, extension_url, | 315 browser::NavigateParams params(browser, extension_url, |
284 content::PAGE_TRANSITION_AUTO_BOOKMARK); | 316 content::PAGE_TRANSITION_AUTO_BOOKMARK); |
285 params.disposition = NEW_FOREGROUND_TAB; | 317 params.disposition = NEW_FOREGROUND_TAB; |
286 browser::Navigate(¶ms); | 318 browser::Navigate(¶ms); |
287 } | 319 } |
288 | 320 |
289 void WebIntentPickerController::OnSuggestionsLinkClicked() { | 321 void WebIntentPickerController::OnSuggestionsLinkClicked() { |
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
338 AsyncOperationFinished(); | 370 AsyncOperationFinished(); |
339 } | 371 } |
340 | 372 |
341 void WebIntentPickerController::OnSendReturnMessage( | 373 void WebIntentPickerController::OnSendReturnMessage( |
342 webkit_glue::WebIntentReplyType reply_type) { | 374 webkit_glue::WebIntentReplyType reply_type) { |
343 ClosePicker(); | 375 ClosePicker(); |
344 | 376 |
345 if (service_tab_ && | 377 if (service_tab_ && |
346 reply_type != webkit_glue::WEB_INTENT_SERVICE_CONTENTS_CLOSED) { | 378 reply_type != webkit_glue::WEB_INTENT_SERVICE_CONTENTS_CLOSED) { |
347 int index = TabStripModel::kNoTab; | 379 int index = TabStripModel::kNoTab; |
380 // TODO(gbillock): use browser_? | |
groby-ooo-7-16
2012/04/24 21:36:51
See above
Greg Billock
2012/04/25 16:04:32
Done.
| |
348 Browser* browser = Browser::GetBrowserForController( | 381 Browser* browser = Browser::GetBrowserForController( |
349 &service_tab_->GetController(), &index); | 382 &service_tab_->GetController(), &index); |
350 if (browser) { | 383 if (browser) { |
351 browser->tabstrip_model()->CloseTabContentsAt( | 384 browser->tabstrip_model()->CloseTabContentsAt( |
352 index, TabStripModel::CLOSE_CREATE_HISTORICAL_TAB); | 385 index, TabStripModel::CLOSE_CREATE_HISTORICAL_TAB); |
353 | 386 |
354 // Activate source tab. | 387 // Activate source tab. |
355 Browser* source_browser = | 388 Browser* source_browser = |
356 BrowserList::FindBrowserWithWebContents(wrapper_->web_contents()); | 389 BrowserList::FindBrowserWithWebContents(wrapper_->web_contents()); |
357 if (source_browser) { | 390 if (source_browser) { |
(...skipping 21 matching lines...) Expand all Loading... | |
379 FaviconService::Handle handle = favicon_service->GetFaviconForURL( | 412 FaviconService::Handle handle = favicon_service->GetFaviconForURL( |
380 services[i].service_url, | 413 services[i].service_url, |
381 history::FAVICON, | 414 history::FAVICON, |
382 &favicon_consumer_, | 415 &favicon_consumer_, |
383 base::Bind( | 416 base::Bind( |
384 &WebIntentPickerController::OnFaviconDataAvailable, | 417 &WebIntentPickerController::OnFaviconDataAvailable, |
385 weak_ptr_factory_.GetWeakPtr())); | 418 weak_ptr_factory_.GetWeakPtr())); |
386 favicon_consumer_.SetClientData(favicon_service, handle, i); | 419 favicon_consumer_.SetClientData(favicon_service, handle, i); |
387 } | 420 } |
388 | 421 |
422 RegistryCallsCompleted(); | |
groby-ooo-7-16
2012/04/24 21:36:51
Why do we flag registry calls completed here, not
Greg Billock
2012/04/25 16:04:32
Bad name. This is the method we want to happen whe
| |
389 AsyncOperationFinished(); | 423 AsyncOperationFinished(); |
390 } | 424 } |
391 | 425 |
426 void WebIntentPickerController::WebIntentServicesForExplicitIntent( | |
427 const std::vector<webkit_glue::WebIntentServiceData>& services) { | |
428 DCHECK(intents_dispatcher_); | |
429 DCHECK(intents_dispatcher_->GetIntent().service.is_valid()); | |
430 for (size_t i = 0; i < services.size(); ++i) { | |
431 if (services[i].service_url != intents_dispatcher_->GetIntent().service) | |
432 continue; | |
433 | |
434 if (services[i].disposition == | |
435 webkit_glue::WebIntentServiceData::DISPOSITION_INLINE) | |
436 CreatePicker(); | |
groby-ooo-7-16
2012/04/24 21:36:51
This is more of a TBD, but I really think we need
Greg Billock
2012/04/25 16:04:32
Makes sense.
| |
437 OnServiceChosen(services[i].service_url, | |
438 ConvertDisposition(services[i].disposition)); | |
439 return; | |
440 } | |
441 | |
442 // We did not find an acceptable extension. The intent cannot | |
443 // be dispatched. | |
444 intents_dispatcher_->SendReplyMessage( | |
445 webkit_glue::WEB_INTENT_REPLY_FAILURE, ASCIIToUTF16( | |
446 "Explicit extension URL is not available.")); | |
447 | |
448 // Finished. | |
449 AsyncOperationFinished(); | |
450 } | |
451 | |
452 void WebIntentPickerController::OnWebIntentDefaultsAvailable( | |
453 const DefaultWebIntentService& default_service) { | |
454 if (!default_service.service_url.empty()) { | |
455 DCHECK(default_service.suppression == 0); | |
456 default_service_url_ = default_service.service_url; | |
457 } | |
458 | |
459 RegistryCallsCompleted(); | |
460 AsyncOperationFinished(); | |
461 } | |
462 | |
463 void WebIntentPickerController::RegistryCallsCompleted() { | |
464 if (--pending_registry_calls_count_ != 0) return; | |
465 | |
466 if (!default_service_url_.empty()) { | |
467 // If there's a default service, dispatch to it immediately | |
468 // without showing the picker. | |
469 const WebIntentPickerModel::InstalledService* default_service = | |
470 picker_model_->GetInstalledServiceWithURL(GURL(default_service_url_)); | |
471 | |
472 if (default_service != NULL) { | |
473 if (default_service->disposition == | |
474 WebIntentPickerModel::DISPOSITION_INLINE) | |
475 CreatePicker(); | |
476 OnServiceChosen(default_service->url, default_service->disposition); | |
477 return; | |
478 } | |
479 } | |
480 | |
481 CreatePicker(); | |
482 picker_->SetActionString(GetIntentActionString(UTF16ToUTF8(action_))); | |
483 | |
484 // TODO(gbillock): handle the case of having only one service -- set | |
485 // the make-default checkbox to "true" upon display. | |
486 } | |
487 | |
392 void WebIntentPickerController::OnFaviconDataAvailable( | 488 void WebIntentPickerController::OnFaviconDataAvailable( |
393 FaviconService::Handle handle, history::FaviconData favicon_data) { | 489 FaviconService::Handle handle, history::FaviconData favicon_data) { |
394 size_t index = favicon_consumer_.GetClientDataForCurrentRequest(); | 490 size_t index = favicon_consumer_.GetClientDataForCurrentRequest(); |
395 if (favicon_data.is_valid()) { | 491 if (favicon_data.is_valid()) { |
396 SkBitmap icon_bitmap; | 492 SkBitmap icon_bitmap; |
397 | 493 |
398 if (gfx::PNGCodec::Decode(favicon_data.image_data->front(), | 494 if (gfx::PNGCodec::Decode(favicon_data.image_data->front(), |
399 favicon_data.image_data->size(), | 495 favicon_data.image_data->size(), |
400 &icon_bitmap)) { | 496 &icon_bitmap)) { |
401 gfx::Image icon_image(new SkBitmap(icon_bitmap)); | 497 gfx::Image icon_image(new SkBitmap(icon_bitmap)); |
(...skipping 140 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
542 } | 638 } |
543 | 639 |
544 void WebIntentPickerController::AsyncOperationFinished() { | 640 void WebIntentPickerController::AsyncOperationFinished() { |
545 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); | 641 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); |
546 if (--pending_async_count_ == 0) { | 642 if (--pending_async_count_ == 0) { |
547 if (picker_) | 643 if (picker_) |
548 picker_->OnPendingAsyncCompleted(); | 644 picker_->OnPendingAsyncCompleted(); |
549 } | 645 } |
550 } | 646 } |
551 | 647 |
648 void WebIntentPickerController::CreatePicker() { | |
649 // If picker is non-NULL, it was set by a test. | |
650 // Use browser_? | |
651 Browser* browser = | |
652 BrowserList::FindBrowserWithWebContents(wrapper_->web_contents()); | |
653 if (picker_ == NULL) { | |
654 picker_ = WebIntentPicker::Create(browser, wrapper_, this, | |
655 picker_model_.get()); | |
656 } | |
657 picker_shown_ = true; | |
658 } | |
659 | |
552 void WebIntentPickerController::ClosePicker() { | 660 void WebIntentPickerController::ClosePicker() { |
553 if (picker_) | 661 if (picker_) |
554 picker_->Close(); | 662 picker_->Close(); |
555 } | 663 } |
OLD | NEW |