protocol_handler_registry.cc revision a1401311d1ab56c4ed0a474bd38c108f75cb0cd9
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 3// found in the LICENSE file. 4 5#include "chrome/browser/custom_handlers/protocol_handler_registry.h" 6 7#include <utility> 8 9#include "base/bind.h" 10#include "base/command_line.h" 11#include "base/logging.h" 12#include "base/prefs/pref_service.h" 13#include "chrome/browser/chrome_notification_types.h" 14#include "chrome/browser/custom_handlers/register_protocol_handler_infobar_delegate.h" 15#include "chrome/browser/net/chrome_url_request_context.h" 16#include "chrome/browser/profiles/profile_io_data.h" 17#include "chrome/common/chrome_switches.h" 18#include "chrome/common/custom_handlers/protocol_handler.h" 19#include "chrome/common/pref_names.h" 20#include "components/user_prefs/pref_registry_syncable.h" 21#include "content/public/browser/child_process_security_policy.h" 22#include "grit/generated_resources.h" 23#include "net/base/network_delegate.h" 24#include "net/url_request/url_request_redirect_job.h" 25#include "ui/base/l10n/l10n_util.h" 26 27using content::BrowserThread; 28using content::ChildProcessSecurityPolicy; 29 30namespace { 31 32const ProtocolHandler& LookupHandler( 33 const ProtocolHandlerRegistry::ProtocolHandlerMap& handler_map, 34 const std::string& scheme) { 35 ProtocolHandlerRegistry::ProtocolHandlerMap::const_iterator p = 36 handler_map.find(scheme); 37 38 if (p != handler_map.end()) 39 return p->second; 40 41 return ProtocolHandler::EmptyProtocolHandler(); 42} 43 44// If true default protocol handlers will be removed if the OS level 45// registration for a protocol is no longer Chrome. 46bool ShouldRemoveHandlersNotInOS() { 47#if defined(OS_LINUX) 48 // We don't do this on Linux as the OS registration there is not reliable, 49 // and Chrome OS doesn't have any notion of OS registration. 50 // TODO(benwells): When Linux support is more reliable remove this 51 // difference (http://crbug.com/88255). 52 return false; 53#else 54 return ShellIntegration::CanSetAsDefaultProtocolClient() != 55 ShellIntegration::SET_DEFAULT_NOT_ALLOWED; 56#endif 57} 58 59} // namespace 60 61// IOThreadDelegate ------------------------------------------------------------ 62 63// IOThreadDelegate is an IO thread specific object. Access to the class should 64// all be done via the IO thread. The registry living on the UI thread makes 65// a best effort to update the IO object after local updates are completed. 66class ProtocolHandlerRegistry::IOThreadDelegate 67 : public base::RefCountedThreadSafe< 68 ProtocolHandlerRegistry::IOThreadDelegate> { 69 public: 70 71 // Creates a new instance. If |enabled| is true the registry is considered 72 // enabled on the IO thread. 73 explicit IOThreadDelegate(bool enabled); 74 75 // Returns true if the protocol has a default protocol handler. 76 // Should be called only from the IO thread. 77 bool IsHandledProtocol(const std::string& scheme) const; 78 79 // Clears the default for the provided protocol. 80 // Should be called only from the IO thread. 81 void ClearDefault(const std::string& scheme); 82 83 // Makes this ProtocolHandler the default handler for its protocol. 84 // Should be called only from the IO thread. 85 void SetDefault(const ProtocolHandler& handler); 86 87 // Creates a URL request job for the given request if there is a matching 88 // protocol handler, returns NULL otherwise. 89 net::URLRequestJob* MaybeCreateJob( 90 net::URLRequest* request, net::NetworkDelegate* network_delegate) const; 91 92 // Indicate that the registry has been enabled in the IO thread's 93 // copy of the data. 94 void Enable() { enabled_ = true; } 95 96 // Indicate that the registry has been disabled in the IO thread's copy of 97 // the data. 98 void Disable() { enabled_ = false; } 99 100 private: 101 friend class base::RefCountedThreadSafe<IOThreadDelegate>; 102 virtual ~IOThreadDelegate(); 103 104 // Copy of protocol handlers use only on the IO thread. 105 ProtocolHandlerRegistry::ProtocolHandlerMap default_handlers_; 106 107 // Is the registry enabled on the IO thread. 108 bool enabled_; 109 110 DISALLOW_COPY_AND_ASSIGN(IOThreadDelegate); 111}; 112 113ProtocolHandlerRegistry::IOThreadDelegate::IOThreadDelegate(bool) 114 : enabled_(true) {} 115ProtocolHandlerRegistry::IOThreadDelegate::~IOThreadDelegate() {} 116 117bool ProtocolHandlerRegistry::IOThreadDelegate::IsHandledProtocol( 118 const std::string& scheme) const { 119 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 120 return enabled_ && !LookupHandler(default_handlers_, scheme).IsEmpty(); 121} 122 123void ProtocolHandlerRegistry::IOThreadDelegate::ClearDefault( 124 const std::string& scheme) { 125 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 126 default_handlers_.erase(scheme); 127} 128 129void ProtocolHandlerRegistry::IOThreadDelegate::SetDefault( 130 const ProtocolHandler& handler) { 131 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 132 ClearDefault(handler.protocol()); 133 default_handlers_.insert(std::make_pair(handler.protocol(), handler)); 134} 135 136// Create a new job for the supplied |URLRequest| if a default handler 137// is registered and the associated handler is able to interpret 138// the url from |request|. 139net::URLRequestJob* ProtocolHandlerRegistry::IOThreadDelegate::MaybeCreateJob( 140 net::URLRequest* request, net::NetworkDelegate* network_delegate) const { 141 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 142 143 ProtocolHandler handler = LookupHandler(default_handlers_, 144 request->url().scheme()); 145 if (handler.IsEmpty()) 146 return NULL; 147 148 GURL translated_url(handler.TranslateUrl(request->url())); 149 if (!translated_url.is_valid()) 150 return NULL; 151 152 return new net::URLRequestRedirectJob( 153 request, network_delegate, translated_url, 154 net::URLRequestRedirectJob::REDIRECT_307_TEMPORARY_REDIRECT, 155 "Protocol Handler Registry"); 156} 157 158// JobInterceptorFactory ------------------------------------------------------- 159 160// Instances of JobInterceptorFactory are produced for ownership by the IO 161// thread where it handler URL requests. We should never hold 162// any pointers on this class, only produce them in response to 163// requests via |ProtocolHandlerRegistry::CreateJobInterceptorFactory|. 164ProtocolHandlerRegistry::JobInterceptorFactory::JobInterceptorFactory( 165 IOThreadDelegate* io_thread_delegate) 166 : io_thread_delegate_(io_thread_delegate) { 167 DCHECK(io_thread_delegate_.get()); 168 DetachFromThread(); 169} 170 171ProtocolHandlerRegistry::JobInterceptorFactory::~JobInterceptorFactory() { 172} 173 174void ProtocolHandlerRegistry::JobInterceptorFactory::Chain( 175 scoped_ptr<net::URLRequestJobFactory> job_factory) { 176 job_factory_ = job_factory.Pass(); 177} 178 179net::URLRequestJob* 180ProtocolHandlerRegistry::JobInterceptorFactory:: 181MaybeCreateJobWithProtocolHandler( 182 const std::string& scheme, 183 net::URLRequest* request, 184 net::NetworkDelegate* network_delegate) const { 185 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 186 net::URLRequestJob* job = io_thread_delegate_->MaybeCreateJob( 187 request, network_delegate); 188 if (job) 189 return job; 190 return job_factory_->MaybeCreateJobWithProtocolHandler( 191 scheme, request, network_delegate); 192} 193 194bool ProtocolHandlerRegistry::JobInterceptorFactory::IsHandledProtocol( 195 const std::string& scheme) const { 196 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 197 return io_thread_delegate_->IsHandledProtocol(scheme) || 198 job_factory_->IsHandledProtocol(scheme); 199} 200 201bool ProtocolHandlerRegistry::JobInterceptorFactory::IsHandledURL( 202 const GURL& url) const { 203 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 204 return (url.is_valid() && 205 io_thread_delegate_->IsHandledProtocol(url.scheme())) || 206 job_factory_->IsHandledURL(url); 207} 208 209bool ProtocolHandlerRegistry::JobInterceptorFactory::IsSafeRedirectTarget( 210 const GURL& location) const { 211 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 212 return job_factory_->IsSafeRedirectTarget(location); 213} 214 215// DefaultClientObserver ------------------------------------------------------ 216 217ProtocolHandlerRegistry::DefaultClientObserver::DefaultClientObserver( 218 ProtocolHandlerRegistry* registry) 219 : worker_(NULL), 220 registry_(registry) { 221 DCHECK(registry_); 222} 223 224ProtocolHandlerRegistry::DefaultClientObserver::~DefaultClientObserver() { 225 if (worker_) 226 worker_->ObserverDestroyed(); 227 228 DefaultClientObserverList::iterator iter = std::find( 229 registry_->default_client_observers_.begin(), 230 registry_->default_client_observers_.end(), this); 231 registry_->default_client_observers_.erase(iter); 232} 233 234void ProtocolHandlerRegistry::DefaultClientObserver::SetDefaultWebClientUIState( 235 ShellIntegration::DefaultWebClientUIState state) { 236 if (worker_) { 237 if (ShouldRemoveHandlersNotInOS() && 238 (state == ShellIntegration::STATE_NOT_DEFAULT)) { 239 registry_->ClearDefault(worker_->protocol()); 240 } 241 } else { 242 NOTREACHED(); 243 } 244} 245 246bool ProtocolHandlerRegistry::DefaultClientObserver:: 247 IsInteractiveSetDefaultPermitted() { 248 return true; 249} 250 251void ProtocolHandlerRegistry::DefaultClientObserver::SetWorker( 252 ShellIntegration::DefaultProtocolClientWorker* worker) { 253 worker_ = worker; 254} 255 256bool ProtocolHandlerRegistry::DefaultClientObserver::IsOwnedByWorker() { 257 return true; 258} 259 260// Delegate -------------------------------------------------------------------- 261 262ProtocolHandlerRegistry::Delegate::~Delegate() {} 263 264void ProtocolHandlerRegistry::Delegate::RegisterExternalHandler( 265 const std::string& protocol) { 266 ChildProcessSecurityPolicy* policy = 267 ChildProcessSecurityPolicy::GetInstance(); 268 if (!policy->IsWebSafeScheme(protocol)) { 269 policy->RegisterWebSafeScheme(protocol); 270 } 271} 272 273void ProtocolHandlerRegistry::Delegate::DeregisterExternalHandler( 274 const std::string& protocol) { 275} 276 277bool ProtocolHandlerRegistry::Delegate::IsExternalHandlerRegistered( 278 const std::string& protocol) { 279 // NOTE(koz): This function is safe to call from any thread, despite living 280 // in ProfileIOData. 281 return ProfileIOData::IsHandledProtocol(protocol); 282} 283 284ShellIntegration::DefaultProtocolClientWorker* 285ProtocolHandlerRegistry::Delegate::CreateShellWorker( 286 ShellIntegration::DefaultWebClientObserver* observer, 287 const std::string& protocol) { 288 return new ShellIntegration::DefaultProtocolClientWorker(observer, protocol); 289} 290 291ProtocolHandlerRegistry::DefaultClientObserver* 292ProtocolHandlerRegistry::Delegate::CreateShellObserver( 293 ProtocolHandlerRegistry* registry) { 294 return new DefaultClientObserver(registry); 295} 296 297void ProtocolHandlerRegistry::Delegate::RegisterWithOSAsDefaultClient( 298 const std::string& protocol, ProtocolHandlerRegistry* registry) { 299 DefaultClientObserver* observer = CreateShellObserver(registry); 300 // The worker pointer is reference counted. While it is running the 301 // message loops of the FILE and UI thread will hold references to it 302 // and it will be automatically freed once all its tasks have finished. 303 scoped_refptr<ShellIntegration::DefaultProtocolClientWorker> worker; 304 worker = CreateShellWorker(observer, protocol); 305 observer->SetWorker(worker.get()); 306 registry->default_client_observers_.push_back(observer); 307 worker->StartSetAsDefault(); 308} 309 310// ProtocolHandlerRegistry ----------------------------------------------------- 311 312ProtocolHandlerRegistry::ProtocolHandlerRegistry(Profile* profile, 313 Delegate* delegate) 314 : profile_(profile), 315 delegate_(delegate), 316 enabled_(true), 317 is_loading_(false), 318 is_loaded_(false), 319 io_thread_delegate_(new IOThreadDelegate(enabled_)){ 320} 321 322bool ProtocolHandlerRegistry::SilentlyHandleRegisterHandlerRequest( 323 const ProtocolHandler& handler) { 324 if (handler.IsEmpty() || !CanSchemeBeOverridden(handler.protocol())) 325 return true; 326 327 if (!enabled() || IsRegistered(handler) || HasIgnoredEquivalent(handler)) 328 return true; 329 330 if (AttemptReplace(handler)) 331 return true; 332 333 return false; 334} 335 336void ProtocolHandlerRegistry::OnAcceptRegisterProtocolHandler( 337 const ProtocolHandler& handler) { 338 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 339 RegisterProtocolHandler(handler); 340 SetDefault(handler); 341 Save(); 342 NotifyChanged(); 343} 344 345void ProtocolHandlerRegistry::OnDenyRegisterProtocolHandler( 346 const ProtocolHandler& handler) { 347 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 348 RegisterProtocolHandler(handler); 349 Save(); 350 NotifyChanged(); 351} 352 353void ProtocolHandlerRegistry::OnIgnoreRegisterProtocolHandler( 354 const ProtocolHandler& handler) { 355 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 356 IgnoreProtocolHandler(handler); 357 Save(); 358 NotifyChanged(); 359} 360 361bool ProtocolHandlerRegistry::AttemptReplace(const ProtocolHandler& handler) { 362 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 363 ProtocolHandler old_default = GetHandlerFor(handler.protocol()); 364 bool make_new_handler_default = handler.IsSameOrigin(old_default); 365 ProtocolHandlerList to_replace(GetReplacedHandlers(handler)); 366 if (to_replace.empty()) 367 return false; 368 for (ProtocolHandlerList::iterator p = to_replace.begin(); 369 p != to_replace.end(); ++p) { 370 RemoveHandler(*p); 371 } 372 if (make_new_handler_default) { 373 OnAcceptRegisterProtocolHandler(handler); 374 } else { 375 InsertHandler(handler); 376 NotifyChanged(); 377 } 378 return true; 379} 380 381ProtocolHandlerRegistry::ProtocolHandlerList 382ProtocolHandlerRegistry::GetReplacedHandlers( 383 const ProtocolHandler& handler) const { 384 ProtocolHandlerList replaced_handlers; 385 const ProtocolHandlerList* handlers = GetHandlerList(handler.protocol()); 386 if (!handlers) 387 return replaced_handlers; 388 for (ProtocolHandlerList::const_iterator p = handlers->begin(); 389 p != handlers->end(); p++) { 390 if (handler.IsSameOrigin(*p)) { 391 replaced_handlers.push_back(*p); 392 } 393 } 394 return replaced_handlers; 395} 396 397void ProtocolHandlerRegistry::ClearDefault(const std::string& scheme) { 398 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 399 400 default_handlers_.erase(scheme); 401 BrowserThread::PostTask( 402 BrowserThread::IO, 403 FROM_HERE, 404 base::Bind(&IOThreadDelegate::ClearDefault, io_thread_delegate_, scheme)); 405 Save(); 406 NotifyChanged(); 407} 408 409bool ProtocolHandlerRegistry::IsDefault( 410 const ProtocolHandler& handler) const { 411 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 412 return GetHandlerFor(handler.protocol()) == handler; 413} 414 415void ProtocolHandlerRegistry::InstallDefaultsForChromeOS() { 416#if defined(OS_CHROMEOS) 417 // Only chromeos has default protocol handlers at this point. 418 AddPredefinedHandler( 419 ProtocolHandler::CreateProtocolHandler( 420 "mailto", 421 GURL(l10n_util::GetStringUTF8(IDS_GOOGLE_MAILTO_HANDLER_URL)), 422 l10n_util::GetStringUTF16(IDS_GOOGLE_MAILTO_HANDLER_NAME))); 423 AddPredefinedHandler( 424 ProtocolHandler::CreateProtocolHandler( 425 "webcal", 426 GURL(l10n_util::GetStringUTF8(IDS_GOOGLE_WEBCAL_HANDLER_URL)), 427 l10n_util::GetStringUTF16(IDS_GOOGLE_WEBCAL_HANDLER_NAME))); 428#else 429 NOTREACHED(); // this method should only ever be called in chromeos. 430#endif 431} 432 433void ProtocolHandlerRegistry::InitProtocolSettings() { 434 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 435 436 // Any further default additions to the table will get rejected from now on. 437 is_loaded_ = true; 438 is_loading_ = true; 439 440 PrefService* prefs = profile_->GetPrefs(); 441 if (prefs->HasPrefPath(prefs::kCustomHandlersEnabled)) { 442 if (prefs->GetBoolean(prefs::kCustomHandlersEnabled)) { 443 Enable(); 444 } else { 445 Disable(); 446 } 447 } 448 std::vector<const base::DictionaryValue*> registered_handlers = 449 GetHandlersFromPref(prefs::kRegisteredProtocolHandlers); 450 for (std::vector<const base::DictionaryValue*>::const_iterator p = 451 registered_handlers.begin(); 452 p != registered_handlers.end(); ++p) { 453 ProtocolHandler handler = ProtocolHandler::CreateProtocolHandler(*p); 454 RegisterProtocolHandler(handler); 455 bool is_default = false; 456 if ((*p)->GetBoolean("default", &is_default) && is_default) { 457 SetDefault(handler); 458 } 459 } 460 std::vector<const base::DictionaryValue*> ignored_handlers = 461 GetHandlersFromPref(prefs::kIgnoredProtocolHandlers); 462 for (std::vector<const base::DictionaryValue*>::const_iterator p = 463 ignored_handlers.begin(); 464 p != ignored_handlers.end(); ++p) { 465 IgnoreProtocolHandler(ProtocolHandler::CreateProtocolHandler(*p)); 466 } 467 is_loading_ = false; 468 469 // For each default protocol handler, check that we are still registered 470 // with the OS as the default application. 471 if (ShouldRemoveHandlersNotInOS()) { 472 for (ProtocolHandlerMap::const_iterator p = default_handlers_.begin(); 473 p != default_handlers_.end(); ++p) { 474 ProtocolHandler handler = p->second; 475 DefaultClientObserver* observer = delegate_->CreateShellObserver(this); 476 scoped_refptr<ShellIntegration::DefaultProtocolClientWorker> worker; 477 worker = delegate_->CreateShellWorker(observer, handler.protocol()); 478 observer->SetWorker(worker.get()); 479 default_client_observers_.push_back(observer); 480 worker->StartCheckIsDefault(); 481 } 482 } 483} 484 485int ProtocolHandlerRegistry::GetHandlerIndex(const std::string& scheme) const { 486 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 487 const ProtocolHandler& handler = GetHandlerFor(scheme); 488 if (handler.IsEmpty()) 489 return -1; 490 const ProtocolHandlerList* handlers = GetHandlerList(scheme); 491 if (!handlers) 492 return -1; 493 494 ProtocolHandlerList::const_iterator p; 495 int i; 496 for (i = 0, p = handlers->begin(); p != handlers->end(); ++p, ++i) { 497 if (*p == handler) 498 return i; 499 } 500 return -1; 501} 502 503ProtocolHandlerRegistry::ProtocolHandlerList 504ProtocolHandlerRegistry::GetHandlersFor( 505 const std::string& scheme) const { 506 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 507 ProtocolHandlerMultiMap::const_iterator p = protocol_handlers_.find(scheme); 508 if (p == protocol_handlers_.end()) { 509 return ProtocolHandlerList(); 510 } 511 return p->second; 512} 513 514ProtocolHandlerRegistry::ProtocolHandlerList 515ProtocolHandlerRegistry::GetIgnoredHandlers() { 516 return ignored_protocol_handlers_; 517} 518 519void ProtocolHandlerRegistry::GetRegisteredProtocols( 520 std::vector<std::string>* output) const { 521 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 522 ProtocolHandlerMultiMap::const_iterator p; 523 for (p = protocol_handlers_.begin(); p != protocol_handlers_.end(); ++p) { 524 if (!p->second.empty()) 525 output->push_back(p->first); 526 } 527} 528 529bool ProtocolHandlerRegistry::CanSchemeBeOverridden( 530 const std::string& scheme) const { 531 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 532 const ProtocolHandlerList* handlers = GetHandlerList(scheme); 533 // If we already have a handler for this scheme, we can add more. 534 if (handlers != NULL && !handlers->empty()) 535 return true; 536 // Don't override a scheme if it already has an external handler. 537 return !delegate_->IsExternalHandlerRegistered(scheme); 538} 539 540bool ProtocolHandlerRegistry::IsRegistered( 541 const ProtocolHandler& handler) const { 542 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 543 const ProtocolHandlerList* handlers = GetHandlerList(handler.protocol()); 544 if (!handlers) { 545 return false; 546 } 547 return std::find(handlers->begin(), handlers->end(), handler) != 548 handlers->end(); 549} 550 551bool ProtocolHandlerRegistry::IsIgnored(const ProtocolHandler& handler) const { 552 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 553 ProtocolHandlerList::const_iterator i; 554 for (i = ignored_protocol_handlers_.begin(); 555 i != ignored_protocol_handlers_.end(); ++i) { 556 if (*i == handler) { 557 return true; 558 } 559 } 560 return false; 561} 562 563bool ProtocolHandlerRegistry::HasRegisteredEquivalent( 564 const ProtocolHandler& handler) const { 565 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 566 const ProtocolHandlerList* handlers = GetHandlerList(handler.protocol()); 567 if (!handlers) { 568 return false; 569 } 570 ProtocolHandlerList::const_iterator i; 571 for (i = handlers->begin(); i != handlers->end(); ++i) { 572 if (handler.IsEquivalent(*i)) { 573 return true; 574 } 575 } 576 return false; 577} 578 579bool ProtocolHandlerRegistry::HasIgnoredEquivalent( 580 const ProtocolHandler& handler) const { 581 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 582 ProtocolHandlerList::const_iterator i; 583 for (i = ignored_protocol_handlers_.begin(); 584 i != ignored_protocol_handlers_.end(); ++i) { 585 if (handler.IsEquivalent(*i)) { 586 return true; 587 } 588 } 589 return false; 590} 591 592void ProtocolHandlerRegistry::RemoveIgnoredHandler( 593 const ProtocolHandler& handler) { 594 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 595 bool should_notify = false; 596 ProtocolHandlerList::iterator p = std::find( 597 ignored_protocol_handlers_.begin(), ignored_protocol_handlers_.end(), 598 handler); 599 if (p != ignored_protocol_handlers_.end()) { 600 ignored_protocol_handlers_.erase(p); 601 Save(); 602 should_notify = true; 603 } 604 if (should_notify) 605 NotifyChanged(); 606} 607 608bool ProtocolHandlerRegistry::IsHandledProtocol( 609 const std::string& scheme) const { 610 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 611 return enabled_ && !GetHandlerFor(scheme).IsEmpty(); 612} 613 614void ProtocolHandlerRegistry::RemoveHandler( 615 const ProtocolHandler& handler) { 616 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 617 ProtocolHandlerList& handlers = protocol_handlers_[handler.protocol()]; 618 ProtocolHandlerList::iterator p = 619 std::find(handlers.begin(), handlers.end(), handler); 620 if (p != handlers.end()) { 621 handlers.erase(p); 622 } 623 ProtocolHandlerMap::iterator q = default_handlers_.find(handler.protocol()); 624 if (q != default_handlers_.end() && q->second == handler) { 625 // Make the new top handler in the list the default. 626 if (!handlers.empty()) { 627 // NOTE We pass a copy because SetDefault() modifies handlers. 628 SetDefault(ProtocolHandler(handlers[0])); 629 } else { 630 BrowserThread::PostTask( 631 BrowserThread::IO, FROM_HERE, 632 base::Bind(&IOThreadDelegate::ClearDefault, io_thread_delegate_, 633 q->second.protocol())); 634 635 default_handlers_.erase(q); 636 } 637 } 638 639 if (!IsHandledProtocol(handler.protocol())) { 640 delegate_->DeregisterExternalHandler(handler.protocol()); 641 } 642 Save(); 643 NotifyChanged(); 644} 645 646void ProtocolHandlerRegistry::RemoveDefaultHandler(const std::string& scheme) { 647 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 648 ProtocolHandler current_default = GetHandlerFor(scheme); 649 if (!current_default.IsEmpty()) 650 RemoveHandler(current_default); 651} 652 653const ProtocolHandler& ProtocolHandlerRegistry::GetHandlerFor( 654 const std::string& scheme) const { 655 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 656 return LookupHandler(default_handlers_, scheme); 657} 658 659void ProtocolHandlerRegistry::Enable() { 660 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 661 if (enabled_) { 662 return; 663 } 664 enabled_ = true; 665 BrowserThread::PostTask( 666 BrowserThread::IO, 667 FROM_HERE, 668 base::Bind(&IOThreadDelegate::Enable, io_thread_delegate_)); 669 670 ProtocolHandlerMap::const_iterator p; 671 for (p = default_handlers_.begin(); p != default_handlers_.end(); ++p) { 672 delegate_->RegisterExternalHandler(p->first); 673 } 674 Save(); 675 NotifyChanged(); 676} 677 678void ProtocolHandlerRegistry::Disable() { 679 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 680 if (!enabled_) { 681 return; 682 } 683 enabled_ = false; 684 BrowserThread::PostTask( 685 BrowserThread::IO, 686 FROM_HERE, 687 base::Bind(&IOThreadDelegate::Disable, io_thread_delegate_)); 688 689 ProtocolHandlerMap::const_iterator p; 690 for (p = default_handlers_.begin(); p != default_handlers_.end(); ++p) { 691 delegate_->DeregisterExternalHandler(p->first); 692 } 693 Save(); 694 NotifyChanged(); 695} 696 697void ProtocolHandlerRegistry::Shutdown() { 698 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 699 delegate_.reset(NULL); 700 // We free these now in case there are any outstanding workers running. If 701 // we didn't free them they could respond to workers and try to update the 702 // protocol handler registry after it was deleted. 703 // Observers remove themselves from this list when they are deleted; so 704 // we delete the last item until none are left in the list. 705 while (!default_client_observers_.empty()) { 706 delete default_client_observers_.back(); 707 } 708} 709 710// static 711void ProtocolHandlerRegistry::RegisterProfilePrefs( 712 user_prefs::PrefRegistrySyncable* registry) { 713 registry->RegisterListPref(prefs::kRegisteredProtocolHandlers, 714 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); 715 registry->RegisterListPref(prefs::kIgnoredProtocolHandlers, 716 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); 717 registry->RegisterBooleanPref( 718 prefs::kCustomHandlersEnabled, 719 true, 720 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); 721} 722 723ProtocolHandlerRegistry::~ProtocolHandlerRegistry() { 724 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 725 DCHECK(default_client_observers_.empty()); 726} 727 728void ProtocolHandlerRegistry::PromoteHandler(const ProtocolHandler& handler) { 729 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 730 DCHECK(IsRegistered(handler)); 731 ProtocolHandlerMultiMap::iterator p = 732 protocol_handlers_.find(handler.protocol()); 733 ProtocolHandlerList& list = p->second; 734 list.erase(std::find(list.begin(), list.end(), handler)); 735 list.insert(list.begin(), handler); 736} 737 738void ProtocolHandlerRegistry::Save() { 739 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 740 if (is_loading_) { 741 return; 742 } 743 scoped_ptr<base::Value> registered_protocol_handlers( 744 EncodeRegisteredHandlers()); 745 scoped_ptr<base::Value> ignored_protocol_handlers(EncodeIgnoredHandlers()); 746 scoped_ptr<base::Value> enabled(base::Value::CreateBooleanValue(enabled_)); 747 profile_->GetPrefs()->Set(prefs::kRegisteredProtocolHandlers, 748 *registered_protocol_handlers); 749 profile_->GetPrefs()->Set(prefs::kIgnoredProtocolHandlers, 750 *ignored_protocol_handlers); 751 profile_->GetPrefs()->Set(prefs::kCustomHandlersEnabled, *enabled); 752} 753 754const ProtocolHandlerRegistry::ProtocolHandlerList* 755ProtocolHandlerRegistry::GetHandlerList( 756 const std::string& scheme) const { 757 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 758 ProtocolHandlerMultiMap::const_iterator p = protocol_handlers_.find(scheme); 759 if (p == protocol_handlers_.end()) { 760 return NULL; 761 } 762 return &p->second; 763} 764 765void ProtocolHandlerRegistry::SetDefault(const ProtocolHandler& handler) { 766 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 767 ProtocolHandlerMap::const_iterator p = default_handlers_.find( 768 handler.protocol()); 769 // If we're not loading, and we are setting a default for a new protocol, 770 // register with the OS. 771 if (!is_loading_ && p == default_handlers_.end()) 772 delegate_->RegisterWithOSAsDefaultClient(handler.protocol(), this); 773 default_handlers_.erase(handler.protocol()); 774 default_handlers_.insert(std::make_pair(handler.protocol(), handler)); 775 PromoteHandler(handler); 776 BrowserThread::PostTask( 777 BrowserThread::IO, 778 FROM_HERE, 779 base::Bind(&IOThreadDelegate::SetDefault, io_thread_delegate_, handler)); 780} 781 782void ProtocolHandlerRegistry::InsertHandler(const ProtocolHandler& handler) { 783 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 784 ProtocolHandlerMultiMap::iterator p = 785 protocol_handlers_.find(handler.protocol()); 786 787 if (p != protocol_handlers_.end()) { 788 p->second.push_back(handler); 789 return; 790 } 791 792 ProtocolHandlerList new_list; 793 new_list.push_back(handler); 794 protocol_handlers_[handler.protocol()] = new_list; 795} 796 797base::Value* ProtocolHandlerRegistry::EncodeRegisteredHandlers() { 798 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 799 base::ListValue* protocol_handlers = new base::ListValue(); 800 for (ProtocolHandlerMultiMap::iterator i = protocol_handlers_.begin(); 801 i != protocol_handlers_.end(); ++i) { 802 for (ProtocolHandlerList::iterator j = i->second.begin(); 803 j != i->second.end(); ++j) { 804 base::DictionaryValue* encoded = j->Encode(); 805 if (IsDefault(*j)) { 806 encoded->Set("default", base::Value::CreateBooleanValue(true)); 807 } 808 protocol_handlers->Append(encoded); 809 } 810 } 811 return protocol_handlers; 812} 813 814base::Value* ProtocolHandlerRegistry::EncodeIgnoredHandlers() { 815 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 816 base::ListValue* handlers = new base::ListValue(); 817 for (ProtocolHandlerList::iterator i = ignored_protocol_handlers_.begin(); 818 i != ignored_protocol_handlers_.end(); ++i) { 819 handlers->Append(i->Encode()); 820 } 821 return handlers; 822} 823 824void ProtocolHandlerRegistry::NotifyChanged() { 825 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 826 content::NotificationService::current()->Notify( 827 chrome::NOTIFICATION_PROTOCOL_HANDLER_REGISTRY_CHANGED, 828 content::Source<Profile>(profile_), 829 content::NotificationService::NoDetails()); 830} 831 832void ProtocolHandlerRegistry::RegisterProtocolHandler( 833 const ProtocolHandler& handler) { 834 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 835 DCHECK(CanSchemeBeOverridden(handler.protocol())); 836 DCHECK(!handler.IsEmpty()); 837 if (IsRegistered(handler)) { 838 return; 839 } 840 if (enabled_ && !delegate_->IsExternalHandlerRegistered(handler.protocol())) 841 delegate_->RegisterExternalHandler(handler.protocol()); 842 InsertHandler(handler); 843} 844 845std::vector<const base::DictionaryValue*> 846ProtocolHandlerRegistry::GetHandlersFromPref(const char* pref_name) const { 847 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 848 std::vector<const base::DictionaryValue*> result; 849 PrefService* prefs = profile_->GetPrefs(); 850 if (!prefs->HasPrefPath(pref_name)) { 851 return result; 852 } 853 854 const base::ListValue* handlers = prefs->GetList(pref_name); 855 if (handlers) { 856 for (size_t i = 0; i < handlers->GetSize(); ++i) { 857 const base::DictionaryValue* dict; 858 if (!handlers->GetDictionary(i, &dict)) 859 continue; 860 if (ProtocolHandler::IsValidDict(dict)) { 861 result.push_back(dict); 862 } 863 } 864 } 865 return result; 866} 867 868void ProtocolHandlerRegistry::IgnoreProtocolHandler( 869 const ProtocolHandler& handler) { 870 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 871 ignored_protocol_handlers_.push_back(handler); 872} 873 874void ProtocolHandlerRegistry::AddPredefinedHandler( 875 const ProtocolHandler& handler) { 876 DCHECK(!is_loaded_); // Must be called prior InitProtocolSettings. 877 RegisterProtocolHandler(handler); 878 SetDefault(handler); 879} 880 881scoped_ptr<ProtocolHandlerRegistry::JobInterceptorFactory> 882ProtocolHandlerRegistry::CreateJobInterceptorFactory() { 883 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 884 // this is always created on the UI thread (in profile_io's 885 // InitializeOnUIThread. Any method calls must be done 886 // on the IO thread (this is checked). 887 return scoped_ptr<JobInterceptorFactory>( 888 new JobInterceptorFactory(io_thread_delegate_.get())); 889} 890