diff options
author | erikkay@chromium.org <erikkay@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-08-28 20:26:05 +0000 |
---|---|---|
committer | erikkay@chromium.org <erikkay@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-08-28 20:26:05 +0000 |
commit | 86c008e8a7da9c00c5a676eb201ba5d0c976748e (patch) | |
tree | 8e58aeeab8564a396ccf67807d5bddfcdaa05807 /chrome/browser | |
parent | 5ec8d59c7e79d1a7aae4137051ffc184ec51096c (diff) | |
download | chromium_src-86c008e8a7da9c00c5a676eb201ba5d0c976748e.zip chromium_src-86c008e8a7da9c00c5a676eb201ba5d0c976748e.tar.gz chromium_src-86c008e8a7da9c00c5a676eb201ba5d0c976748e.tar.bz2 |
override chrome:// URLs via extensions.
Overrides are declared in an extension's manifest. The last one installed
wins. However, we keep a list of those installed per page so that priority
is preserved and so that uninstall will revert to a previous state.
Review URL: http://codereview.chromium.org/174277
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@24791 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser')
-rw-r--r-- | chrome/browser/browser_about_handler.cc | 2 | ||||
-rw-r--r-- | chrome/browser/browser_about_handler.h | 3 | ||||
-rw-r--r-- | chrome/browser/browser_prefs.cc | 2 | ||||
-rw-r--r-- | chrome/browser/browser_url_handler.cc | 11 | ||||
-rw-r--r-- | chrome/browser/browser_url_handler.h | 5 | ||||
-rw-r--r-- | chrome/browser/extensions/extension_apitest.cc | 55 | ||||
-rw-r--r-- | chrome/browser/extensions/extension_apitest.h | 8 | ||||
-rw-r--r-- | chrome/browser/extensions/extension_browsertest.cc | 23 | ||||
-rw-r--r-- | chrome/browser/extensions/extension_dom_ui.cc | 222 | ||||
-rw-r--r-- | chrome/browser/extensions/extension_dom_ui.h | 34 | ||||
-rw-r--r-- | chrome/browser/extensions/extension_host.cc | 22 | ||||
-rw-r--r-- | chrome/browser/extensions/extension_override_apitest.cc | 13 | ||||
-rw-r--r-- | chrome/browser/extensions/extensions_service.cc | 13 | ||||
-rw-r--r-- | chrome/browser/tab_contents/navigation_controller.cc | 2 |
14 files changed, 361 insertions, 54 deletions
diff --git a/chrome/browser/browser_about_handler.cc b/chrome/browser/browser_about_handler.cc index 9db4260..6cc4491 100644 --- a/chrome/browser/browser_about_handler.cc +++ b/chrome/browser/browser_about_handler.cc @@ -698,7 +698,7 @@ void AboutMemoryHandler::OnDetailsAvailable() { // ----------------------------------------------------------------------------- -bool WillHandleBrowserAboutURL(GURL* url) { +bool WillHandleBrowserAboutURL(GURL* url, Profile* profile) { // We only handle about: schemes. if (!url->SchemeIs(chrome::kAboutScheme)) return false; diff --git a/chrome/browser/browser_about_handler.h b/chrome/browser/browser_about_handler.h index dedf929..87e5e75 100644 --- a/chrome/browser/browser_about_handler.h +++ b/chrome/browser/browser_about_handler.h @@ -8,6 +8,7 @@ #define CHROME_BROWSER_BROWSER_ABOUT_HANDLER_H_ class GURL; +class Profile; // Decides whether the given URL will be handled by the browser about handler // and returns true if so. On true, it may also modify the given URL to be the @@ -15,7 +16,7 @@ class GURL; // handles all "about:" URLs as "about:blank. // // This is used by BrowserURLHandler. -bool WillHandleBrowserAboutURL(GURL* url); +bool WillHandleBrowserAboutURL(GURL* url, Profile* profile); // We have a few magic commands that don't cause navigations, but rather pop up // dialogs. This function handles those cases, and returns true if so. In this diff --git a/chrome/browser/browser_prefs.cc b/chrome/browser/browser_prefs.cc index 74e9b65..cb1a8cc 100644 --- a/chrome/browser/browser_prefs.cc +++ b/chrome/browser/browser_prefs.cc @@ -12,6 +12,7 @@ #include "chrome/browser/debugger/devtools_manager.h" #include "chrome/browser/dom_ui/new_tab_ui.h" #include "chrome/browser/download/download_manager.h" +#include "chrome/browser/extensions/extension_dom_ui.h" #include "chrome/browser/external_protocol_handler.h" #include "chrome/browser/google_url_tracker.h" #include "chrome/browser/metrics/metrics_service.h" @@ -75,6 +76,7 @@ void RegisterAllPrefs(PrefService* user_prefs, PrefService* local_state) { AutofillManager::RegisterUserPrefs(user_prefs); TabContents::RegisterUserPrefs(user_prefs); TemplateURLPrepopulateData::RegisterUserPrefs(user_prefs); + ExtensionDOMUI::RegisterUserPrefs(user_prefs); NewTabUI::RegisterUserPrefs(user_prefs); BlockedPopupContainer::RegisterUserPrefs(user_prefs); DevToolsManager::RegisterUserPrefs(user_prefs); diff --git a/chrome/browser/browser_url_handler.cc b/chrome/browser/browser_url_handler.cc index e42e34e..506ccfd 100644 --- a/chrome/browser/browser_url_handler.cc +++ b/chrome/browser/browser_url_handler.cc @@ -7,11 +7,13 @@ #include "base/string_util.h" #include "chrome/browser/browser_about_handler.h" #include "chrome/browser/dom_ui/dom_ui_factory.h" +#include "chrome/browser/extensions/extension_dom_ui.h" +#include "chrome/browser/profile.h" #include "chrome/common/url_constants.h" #include "googleurl/src/gurl.h" // Handles rewriting view-source URLs for what we'll actually load. -static bool HandleViewSource(GURL* url) { +static bool HandleViewSource(GURL* url, Profile* profile) { if (url->SchemeIs(chrome::kViewSourceScheme)) { // Load the inner URL instead. *url = GURL(url->path()); @@ -21,7 +23,7 @@ static bool HandleViewSource(GURL* url) { } // Handles URLs for DOM UI. These URLs need no rewriting. -static bool HandleDOMUI(GURL* url) { +static bool HandleDOMUI(GURL* url, Profile* profile) { if (!DOMUIFactory::UseDOMUIForURL(*url)) return false; return true; @@ -35,17 +37,18 @@ void BrowserURLHandler::InitURLHandlers() { return; // Add the default URL handlers. + url_handlers_.push_back(&ExtensionDOMUI::HandleChromeURLOverride); url_handlers_.push_back(&WillHandleBrowserAboutURL); // about: url_handlers_.push_back(&HandleDOMUI); // chrome: & friends. url_handlers_.push_back(&HandleViewSource); // view-source: } // static -void BrowserURLHandler::RewriteURLIfNecessary(GURL* url) { +void BrowserURLHandler::RewriteURLIfNecessary(GURL* url, Profile* profile) { if (url_handlers_.empty()) InitURLHandlers(); for (size_t i = 0; i < url_handlers_.size(); ++i) { - if ((*url_handlers_[i])(url)) + if ((*url_handlers_[i])(url, profile)) return; } } diff --git a/chrome/browser/browser_url_handler.h b/chrome/browser/browser_url_handler.h index 8200c8a..593408e 100644 --- a/chrome/browser/browser_url_handler.h +++ b/chrome/browser/browser_url_handler.h @@ -15,6 +15,7 @@ #include <vector> class GURL; +class Profile; // BrowserURLHandler manages the list of all special URLs and manages // dispatching the URL handling to registered handlers. @@ -26,11 +27,11 @@ class BrowserURLHandler { // - optionally set |dispatcher| to the necessary DOMMessageDispatcher // - return true. // If the URL is not handled by a handler, it should return false. - typedef bool (*URLHandler)(GURL* url); + typedef bool (*URLHandler)(GURL* url, Profile* profile); // HandleBrowserURL gives all registered URLHandlers a shot at processing // the given URL, and modifies it in place. - static void RewriteURLIfNecessary(GURL* url); + static void RewriteURLIfNecessary(GURL* url, Profile* profile); // We initialize the list of url_handlers_ lazily the first time MaybeHandle // is called. diff --git a/chrome/browser/extensions/extension_apitest.cc b/chrome/browser/extensions/extension_apitest.cc index 3c45524..415d1c9 100644 --- a/chrome/browser/extensions/extension_apitest.cc +++ b/chrome/browser/extensions/extension_apitest.cc @@ -14,34 +14,48 @@ static const int kTimeoutMs = 60 * 1000; // 1 minute // Load an extension and wait for it to notify of PASSED or FAILED. bool ExtensionApiTest::RunExtensionTest(const char* extension_name) { - bool result; - completed_ = false; { NotificationRegistrar registrar; registrar.Add(this, NotificationType::EXTENSION_TEST_PASSED, NotificationService::AllSources()); registrar.Add(this, NotificationType::EXTENSION_TEST_FAILED, NotificationService::AllSources()); - result = LoadExtension(test_data_dir_.AppendASCII(extension_name)); - // If the test runs quickly, we may get the notification while waiting - // for the Load to finish. - if (completed_) { - result = passed_; - } else { - result = WaitForPassFail(); + if (!LoadExtension(test_data_dir_.AppendASCII(extension_name))) { + message_ = "Failed to load extension."; + return false; } } - return result; + + // TODO(erikkay) perhaps we shouldn't do this implicitly. + return WaitForPassFail(); } bool ExtensionApiTest::WaitForPassFail() { - completed_ = false; - passed_ = false; - MessageLoop::current()->PostDelayedTask( - FROM_HERE, new MessageLoop::QuitTask, kTimeoutMs); - ui_test_utils::RunMessageLoop(); - return passed_; + NotificationRegistrar registrar; + registrar.Add(this, NotificationType::EXTENSION_TEST_PASSED, + NotificationService::AllSources()); + registrar.Add(this, NotificationType::EXTENSION_TEST_FAILED, + NotificationService::AllSources()); + + // Depending on the tests, multiple results can come in from a single call + // to RunMessageLoop(), so we maintain a queue of results and just pull them + // off as the test calls this, going to the run loop only when the queue is + // empty. + if (!results_.size()) { + MessageLoop::current()->PostDelayedTask( + FROM_HERE, new MessageLoop::QuitTask, kTimeoutMs); + ui_test_utils::RunMessageLoop(); + } + if (results_.size()) { + bool ret = results_.front(); + results_.pop_front(); + message_ = messages_.front(); + messages_.pop_front(); + return ret; + } + message_ = "No response from message loop."; + return false; } void ExtensionApiTest::SetUpCommandLine(CommandLine* command_line) { @@ -55,16 +69,15 @@ void ExtensionApiTest::Observe(NotificationType type, switch (type.value) { case NotificationType::EXTENSION_TEST_PASSED: std::cout << "Got EXTENSION_TEST_PASSED notification.\n"; - completed_ = true; - passed_ = true; + results_.push_back(true); + messages_.push_back(""); MessageLoopForUI::current()->Quit(); break; case NotificationType::EXTENSION_TEST_FAILED: std::cout << "Got EXTENSION_TEST_FAILED notification.\n"; - completed_ = true; - passed_ = false; - message_ = *(Details<std::string>(details).ptr()); + results_.push_back(false); + messages_.push_back(*(Details<std::string>(details).ptr())); MessageLoopForUI::current()->Quit(); break; diff --git a/chrome/browser/extensions/extension_apitest.h b/chrome/browser/extensions/extension_apitest.h index af2dd28..466735d 100644 --- a/chrome/browser/extensions/extension_apitest.h +++ b/chrome/browser/extensions/extension_apitest.h @@ -33,13 +33,11 @@ class ExtensionApiTest : public ExtensionBrowserTest { void Observe(NotificationType type, const NotificationSource& source, const NotificationDetails& details); - // Did the extension side of the unit test complete? - bool completed_; - - // Did the extension side of the unit test pass? - bool passed_; + // A sequential list of pass/fail notifications from the test extension(s). + std::deque<bool> results_; // If it failed, what was the error message? + std::deque<std::string> messages_; std::string message_; }; diff --git a/chrome/browser/extensions/extension_browsertest.cc b/chrome/browser/extensions/extension_browsertest.cc index 27e6311..cf7a513 100644 --- a/chrome/browser/extensions/extension_browsertest.cc +++ b/chrome/browser/extensions/extension_browsertest.cc @@ -149,22 +149,20 @@ bool ExtensionBrowserTest::WaitForExtensionHostsToLoad() { // Wait for all the extension hosts that exist to finish loading. // NOTE: This assumes that the extension host list is not changing while // this method is running. + + NotificationRegistrar registrar; + registrar.Add(this, NotificationType::EXTENSION_HOST_DID_STOP_LOADING, + NotificationService::AllSources()); + ExtensionProcessManager* manager = browser()->profile()->GetExtensionProcessManager(); base::Time start_time = base::Time::Now(); + MessageLoop::current()->PostDelayedTask(FROM_HERE, + new MessageLoop::QuitTask, kTimeoutMs); for (ExtensionProcessManager::const_iterator iter = manager->begin(); iter != manager->end(); ++iter) { - while (!(*iter)->did_stop_loading()) { - if ((base::Time::Now() - start_time).InMilliseconds() > kTimeoutMs) { - std::cout << "Extension host did not load for URL: " - << (*iter)->GetURL().spec(); - return false; - } - - MessageLoop::current()->PostDelayedTask(FROM_HERE, - new MessageLoop::QuitTask, 200); + if (!(*iter)->did_stop_loading()) ui_test_utils::RunMessageLoop(); - } } return true; @@ -184,6 +182,11 @@ void ExtensionBrowserTest::Observe(NotificationType type, MessageLoopForUI::current()->Quit(); break; + case NotificationType::EXTENSION_HOST_DID_STOP_LOADING: + std::cout << "Got EXTENSION_HOST_DID_STOP_LOADING notification.\n"; + MessageLoopForUI::current()->Quit(); + break; + default: NOTREACHED(); break; diff --git a/chrome/browser/extensions/extension_dom_ui.cc b/chrome/browser/extensions/extension_dom_ui.cc index ed5abb9..044a28a 100644 --- a/chrome/browser/extensions/extension_dom_ui.cc +++ b/chrome/browser/extensions/extension_dom_ui.cc @@ -5,8 +5,16 @@ #include "chrome/browser/extensions/extension_dom_ui.h" #include "chrome/browser/browser.h" +#include "chrome/browser/browser_list.h" +#include "chrome/browser/profile.h" #include "chrome/browser/tab_contents/tab_contents.h" #include "chrome/common/bindings_policy.h" +#include "chrome/common/pref_service.h" +#include "chrome/common/url_constants.h" + +namespace { +const wchar_t kExtensionURLOverrides[] = L"extensions.chrome_url_overrides"; +} ExtensionDOMUI::ExtensionDOMUI(TabContents* tab_contents) : DOMUI(tab_contents) { @@ -14,18 +22,35 @@ ExtensionDOMUI::ExtensionDOMUI(TabContents* tab_contents) hide_favicon_ = true; should_hide_url_ = true; bindings_ = BindingsPolicy::EXTENSION; + + // For chrome:// overrides, some of the defaults are a little different. + GURL url = tab_contents->GetURL(); + if (url.SchemeIs(chrome::kChromeUIScheme)) { + if (url.host() == chrome::kChromeUINewTabHost) { + focus_location_bar_by_default_ = true; + } else { + // Current behavior of other chrome:// pages is to display the URL. + should_hide_url_ = false; + } + } } -void ExtensionDOMUI::RenderViewCreated(RenderViewHost* render_view_host) { +void ExtensionDOMUI::ResetExtensionFunctionDispatcher( + RenderViewHost* render_view_host) { + // Use the NavigationController to get the URL rather than the TabContents + // since this is the real underlying URL (see HandleChromeURLOverride). + NavigationController& controller = tab_contents()->controller(); + const GURL& url = controller.pending_entry()->url(); extension_function_dispatcher_.reset( - new ExtensionFunctionDispatcher(render_view_host, this, - tab_contents()->GetURL())); + new ExtensionFunctionDispatcher(render_view_host, this, url)); +} + +void ExtensionDOMUI::RenderViewCreated(RenderViewHost* render_view_host) { + ResetExtensionFunctionDispatcher(render_view_host); } void ExtensionDOMUI::RenderViewReused(RenderViewHost* render_view_host) { - extension_function_dispatcher_.reset( - new ExtensionFunctionDispatcher(render_view_host, this, - tab_contents()->GetURL())); + ResetExtensionFunctionDispatcher(render_view_host); } void ExtensionDOMUI::ProcessDOMUIMessage(const std::string& message, @@ -39,3 +64,188 @@ void ExtensionDOMUI::ProcessDOMUIMessage(const std::string& message, Browser* ExtensionDOMUI::GetBrowser() { return static_cast<Browser*>(tab_contents()->delegate()); } + +//////////////////////////////////////////////////////////////////////////////// +// chrome:// URL overrides + +// static +void ExtensionDOMUI::RegisterUserPrefs(PrefService* prefs) { + prefs->RegisterDictionaryPref(kExtensionURLOverrides); +} + +// static +bool ExtensionDOMUI::HandleChromeURLOverride(GURL* url, Profile* profile) { + if (!url->SchemeIs(chrome::kChromeUIScheme)) + return false; + + // Even when the extensions service is enabled by default, it's still + // disabled in incognito mode. + ExtensionsService* service = profile->GetExtensionsService(); + if (!service) + return false; + + const DictionaryValue* overrides = + profile->GetPrefs()->GetDictionary(kExtensionURLOverrides); + std::string page = url->host(); + ListValue* url_list; + if (!overrides || !overrides->GetList(UTF8ToWide(page), &url_list)) + return false; + + if (!service->is_ready()) { + // TODO(erikkay) So far, it looks like extensions load before the new tab + // page. I don't know if we have anything that enforces this, so add this + // check for safety. + NOTREACHED() << "Chrome URL override requested before extensions loaded"; + return false; + } + + while (url_list->GetSize()) { + Value* val; + url_list->Get(0, &val); + + // Verify that the override value is good. If not, unregister it and find + // the next one. + std::string override; + if (!val->GetAsString(&override)) { + NOTREACHED(); + UnregisterChromeURLOverride(page, profile, val); + continue; + } + GURL extension_url(override); + if (!extension_url.is_valid()) { + NOTREACHED(); + UnregisterChromeURLOverride(page, profile, val); + continue; + } + + // Verify that the extension that's being referred to actually exists. + Extension* extension = service->GetExtensionByURL(extension_url); + if (!extension) { + // This can currently happen if you use --load-extension one run, and + // then don't use it the next. It could also happen if an extension + // were deleted directly from the filesystem, etc. + LOG(WARNING) << "chrome URL override present for non-existant extension"; + UnregisterChromeURLOverride(page, profile, val); + continue; + } + + *url = extension_url; + return true; + } + return false; +} + +// static +void ExtensionDOMUI::RegisterChromeURLOverrides( + Profile* profile, const DictionaryValue* new_overrides) { + if (!new_overrides) + return; + + PrefService* prefs = profile->GetPrefs(); + DictionaryValue* all_overrides = + prefs->GetMutableDictionary(kExtensionURLOverrides); + + // For each override provided by the extension, add it to the front of + // the override list if it's not already in the list. + DictionaryValue::key_iterator iter = new_overrides->begin_keys(); + for (;iter != new_overrides->end_keys(); ++iter) { + Value* val; + new_overrides->Get(*iter, &val); + std::string string_val; + if (!val->GetAsString(&string_val)) { + NOTREACHED(); + continue; + } + ListValue* page_overrides; + if (!all_overrides->GetList(*iter, &page_overrides)) { + page_overrides = new ListValue(); + all_overrides->Set(*iter, page_overrides); + } else { + // Verify that the override isn't already in the list. + ListValue::iterator i = page_overrides->begin(); + for (; i != page_overrides->end(); ++i) { + std::string override_val; + if (!(*i)->GetAsString(&override_val)) { + NOTREACHED(); + continue; + } + if (override_val == string_val) + break; + } + // This value is already in the list, leave it alone. + if (i != page_overrides->end()) + continue; + } + // Insert the override at the front of the list. Last registered override + // wins. + page_overrides->Insert(0, val->DeepCopy()); + } +} + +// static +void ExtensionDOMUI::UnregisterAndReplaceOverride(const std::string& page, + Profile* profile, ListValue* list, Value* override) { + int index = list->Remove(*override); + if (index == 0) { + // This is the active override, so we need to find all existing + // tabs for this override and get them to reload the original URL. + for (TabContentsIterator iterator; !iterator.done(); iterator++) { + TabContents* tab = *iterator; + if (tab->profile() != profile) + continue; + + GURL url = tab->GetURL(); + if (!url.SchemeIs(chrome::kChromeUIScheme) || url.host() != page) + continue; + + // Don't use Reload() since |url| isn't the same as the internal URL + // that NavigationController has. + tab->controller().LoadURL(url, url, PageTransition::RELOAD); + } + } +} + +// static +void ExtensionDOMUI::UnregisterChromeURLOverride(const std::string& page, + Profile* profile, Value* override) { + if (!override) + return; + PrefService* prefs = profile->GetPrefs(); + DictionaryValue* all_overrides = + prefs->GetMutableDictionary(kExtensionURLOverrides); + ListValue* page_overrides; + if (!all_overrides->GetList(UTF8ToWide(page), &page_overrides)) { + // If it's being unregistered, it should already be in the list. + NOTREACHED(); + return; + } else { + UnregisterAndReplaceOverride(page, profile, page_overrides, override); + } +} + +// static +void ExtensionDOMUI::UnregisterChromeURLOverrides( + Profile* profile, const DictionaryValue* new_overrides) { + if (!new_overrides) + return; + PrefService* prefs = profile->GetPrefs(); + DictionaryValue* all_overrides = + prefs->GetMutableDictionary(kExtensionURLOverrides); + DictionaryValue::key_iterator iter = new_overrides->begin_keys(); + for (; iter != new_overrides->end_keys(); ++iter) { + Value* val; + if (!new_overrides->Get(*iter, &val)) { + NOTREACHED(); + return; + } + ListValue* page_overrides; + if (!all_overrides->GetList(*iter, &page_overrides)) { + // If it's being unregistered, it should already be in the list. + NOTREACHED(); + continue; + } else { + UnregisterAndReplaceOverride(WideToUTF8(*iter), profile, page_overrides, + val); + } + } +} diff --git a/chrome/browser/extensions/extension_dom_ui.h b/chrome/browser/extensions/extension_dom_ui.h index ff64a9d..b8ef8dc 100644 --- a/chrome/browser/extensions/extension_dom_ui.h +++ b/chrome/browser/extensions/extension_dom_ui.h @@ -9,6 +9,10 @@ #include "chrome/browser/dom_ui/dom_ui.h" #include "chrome/browser/extensions/extension_function_dispatcher.h" +class ListValue; +class PrefService; +class TabContents; + // This class implements DOMUI for extensions and allows extensions to put UI in // the main tab contents area. class ExtensionDOMUI @@ -16,6 +20,7 @@ class ExtensionDOMUI public ExtensionFunctionDispatcher::Delegate { public: explicit ExtensionDOMUI(TabContents* tab_contents); + ExtensionFunctionDispatcher* extension_function_dispatcher() const { return extension_function_dispatcher_.get(); } @@ -31,7 +36,36 @@ class ExtensionDOMUI // ExtensionFunctionDispatcher::Delegate virtual Browser* GetBrowser(); + // BrowserURLHandler + static bool HandleChromeURLOverride(GURL* url, Profile* profile); + + // Register and unregister a dictionary of one or more overrides. + // Page names are the keys, and chrome-extension: URLs are the values. + // (e.g. { "newtab": "chrome-extension://<id>/my_new_tab.html" } + static void RegisterChromeURLOverrides(Profile* profile, + const DictionaryValue* overrides); + static void UnregisterChromeURLOverrides(Profile* profile, + const DictionaryValue* overrides); + static void UnregisterChromeURLOverride(const std::string& page, + Profile* profile, + Value* override); + + // Called from BrowserPrefs + static void RegisterUserPrefs(PrefService* prefs); + private: + // Unregister the specified override, and if it's the currently active one, + // ensure that something takes its place. + static void UnregisterAndReplaceOverride(const std::string& page, + Profile* profile, + ListValue* list, + Value* override); + + // When the RenderViewHost changes (RenderViewCreated and RenderViewReused), + // we need to reset the ExtensionFunctionDispatcher so it's talking to the + // right one, as well as being linked to the correct URL. + void ResetExtensionFunctionDispatcher(RenderViewHost* render_view_host); + scoped_ptr<ExtensionFunctionDispatcher> extension_function_dispatcher_; }; diff --git a/chrome/browser/extensions/extension_host.cc b/chrome/browser/extensions/extension_host.cc index bc03f8f..60aa0d3 100644 --- a/chrome/browser/extensions/extension_host.cc +++ b/chrome/browser/extensions/extension_host.cc @@ -174,7 +174,13 @@ void ExtensionHost::DidStopLoading(RenderViewHost* render_view_host) { } #endif - did_stop_loading_ = true; + if (!did_stop_loading_) { + NotificationService::current()->Notify( + NotificationType::EXTENSION_HOST_DID_STOP_LOADING, + Source<Profile>(profile_), + Details<ExtensionHost>(this)); + did_stop_loading_ = true; + } } void ExtensionHost::DocumentAvailableInMainFrame(RenderViewHost* rvh) { @@ -303,8 +309,18 @@ Browser* ExtensionHost::GetBrowser() { if (view_.get()) return view_->browser(); #endif - Browser* browser = BrowserList::GetLastActiveWithProfile( - render_view_host()->process()->profile()); + Profile* profile = render_view_host()->process()->profile(); + Browser* browser = BrowserList::GetLastActiveWithProfile(profile); + + // It's possible for a browser to exist, but to have never been active. + // This can happen if you launch the browser on a machine without an active + // desktop (a headless buildbot) or if you quickly give another app focus + // at launch time. This is easy to do with browser_tests. + if (!browser) + browser = BrowserList::FindBrowserWithProfile(profile); + + // TODO(erikkay): can this still return NULL? Is Rafael's comment still + // valid here? // NOTE(rafaelw): This can return NULL in some circumstances. In particular, // a toolstrip or background_page onload chrome.tabs api call can make it // into here before the browser is sufficiently initialized to return here. diff --git a/chrome/browser/extensions/extension_override_apitest.cc b/chrome/browser/extensions/extension_override_apitest.cc new file mode 100644 index 0000000..7b10e33 --- /dev/null +++ b/chrome/browser/extensions/extension_override_apitest.cc @@ -0,0 +1,13 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/extensions/extension_apitest.h" + +IN_PROC_BROWSER_TEST_F(ExtensionApiTest, Overrides) { + ASSERT_TRUE(RunExtensionTest("override1")) << message_; // new tab + EXPECT_EQ(results_.size(), 0U); + + // TODO(erikkay) load a second override and verify behavior, then unload + // the first and verify behavior, etc. +} diff --git a/chrome/browser/extensions/extensions_service.cc b/chrome/browser/extensions/extensions_service.cc index acb74cd..b53f243 100644 --- a/chrome/browser/extensions/extensions_service.cc +++ b/chrome/browser/extensions/extensions_service.cc @@ -11,6 +11,7 @@ #include "chrome/browser/chrome_thread.h" #include "chrome/browser/extensions/crx_installer.h" #include "chrome/browser/extensions/extension_browser_event_router.h" +#include "chrome/browser/extensions/extension_dom_ui.h" #include "chrome/browser/extensions/extension_file_util.h" #include "chrome/browser/extensions/extension_updater.h" #include "chrome/browser/extensions/external_extension_provider.h" @@ -195,6 +196,9 @@ void ExtensionsService::UninstallExtension(const std::string& extension_id, install_directory_)); } + ExtensionDOMUI::UnregisterChromeURLOverrides(profile_, + extension->GetChromeURLOverrides()); + UnloadExtension(extension_id); } @@ -213,6 +217,9 @@ void ExtensionsService::EnableExtension(const std::string& extension_id) { extension); disabled_extensions_.erase(iter); + ExtensionDOMUI::RegisterChromeURLOverrides(profile_, + extension->GetChromeURLOverrides()); + NotificationService::current()->Notify( NotificationType::EXTENSION_LOADED, Source<ExtensionsService>(this), @@ -314,6 +321,9 @@ void ExtensionsService::UnloadExtension(const std::string& extension_id) { // Callers should not send us nonexistant extensions. CHECK(extension.get()); + ExtensionDOMUI::UnregisterChromeURLOverrides(profile_, + extension->GetChromeURLOverrides()); + ExtensionList::iterator iter = std::find(disabled_extensions_.begin(), disabled_extensions_.end(), extension.get()); @@ -426,6 +436,9 @@ void ExtensionsService::OnExtensionLoaded(Extension* extension, NotificationType::THEME_INSTALLED, Source<ExtensionsService>(this), Details<Extension>(extension)); + } else { + ExtensionDOMUI::RegisterChromeURLOverrides(profile_, + extension->GetChromeURLOverrides()); } break; case Extension::DISABLED: diff --git a/chrome/browser/tab_contents/navigation_controller.cc b/chrome/browser/tab_contents/navigation_controller.cc index 244f251..3704169 100644 --- a/chrome/browser/tab_contents/navigation_controller.cc +++ b/chrome/browser/tab_contents/navigation_controller.cc @@ -358,7 +358,7 @@ NavigationEntry* NavigationController::CreateNavigationEntry( // will actually be loaded. This real URL won't be shown to the user, just // used internally. GURL loaded_url(url); - BrowserURLHandler::RewriteURLIfNecessary(&loaded_url); + BrowserURLHandler::RewriteURLIfNecessary(&loaded_url, profile_); NavigationEntry* entry = new NavigationEntry(NULL, -1, loaded_url, referrer, string16(), transition); |