Bug 512784: add Services.jsm module to allow easy global access to common services, r=dao, r=Mossop
authorGavin Sharp <gavin@gavinsharp.com>
Thu, 28 Jan 2010 13:31:45 -0500
changeset 38696 78e5543c0bc4d84d4dfbb5e352831f59ff9c68d7
parent 38695 42c7d789364e12abf81b397f74e65e5ba719804d
child 38697 58c60f220da285f1847dacc839350c1828b8e40d
push id11805
push usergsharp@mozilla.com
push dateThu, 25 Feb 2010 17:33:11 +0000
treeherderautoland@78e5543c0bc4 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdao, Mossop
bugs512784
milestone1.9.3a2pre
Bug 512784: add Services.jsm module to allow easy global access to common services, r=dao, r=Mossop
browser/base/content/browser.js
browser/base/content/test/browser_utilityOverlay.js
browser/base/content/utilityOverlay.js
toolkit/content/Makefile.in
toolkit/content/Services.jsm
toolkit/content/tests/browser/Makefile.in
toolkit/content/tests/browser/browser_Services.js
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -44,16 +44,17 @@
 #   Ehsan Akhgari <ehsan.akhgari@gmail.com>
 #   Dão Gottwald <dao@mozilla.com>
 #   Thomas K. Dyas <tdyas@zecador.org>
 #   Edward Lee <edward.lee@engineering.uiuc.edu>
 #   Paul O’Shannessy <paul@oshannessy.com>
 #   Nils Maier <maierman@web.de>
 #   Rob Arnold <robarnold@cmu.edu>
 #   Dietrich Ayala <dietrich@mozilla.com>
+#   Gavin Sharp <gavin@gavinsharp.com>
 #
 # Alternatively, the contents of this file may be used under the terms of
 # either the GNU General Public License Version 2 or later (the "GPL"), or
 # the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 # in which case the provisions of the GPL or the LGPL are applicable instead
 # of those above. If you wish to allow use of your version of this file only
 # under the terms of either the GPL or the LGPL, and not to allow others to
 # use your version of this file under the terms of the MPL, indicate your
@@ -61,16 +62,17 @@
 # and other provisions required by the GPL or the LGPL. If you do not delete
 # the provisions above, a recipient may use your version of this file under
 # the terms of any one of the MPL, the GPL or the LGPL.
 #
 # ***** END LICENSE BLOCK *****
 
 let Ci = Components.interfaces;
 let Cu = Components.utils;
+
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
 const nsIWebNavigation = Ci.nsIWebNavigation;
 
 var gCharsetMenu = null;
 var gLastBrowserCharset = null;
 var gPrevCharset = null;
 var gProxyFavIcon = null;
@@ -101,19 +103,17 @@ var gEditUIVisible = true;
   window.__defineSetter__(name, function (val) {
     delete window[name];
     return window[name] = val;
   });
 });
 
 __defineGetter__("gPrefService", function() {
   delete this.gPrefService;
-  return this.gPrefService = Cc["@mozilla.org/preferences-service;1"]
-                               .getService(Ci.nsIPrefBranch2)
-                               .QueryInterface(Ci.nsIPrefService);
+  return this.gPrefService = Services.prefs;
 });
 
 __defineGetter__("PluralForm", function() {
   Cu.import("resource://gre/modules/PluralForm.jsm");
   return this.PluralForm;
 });
 __defineSetter__("PluralForm", function (val) {
   delete this.PluralForm;
@@ -321,19 +321,16 @@ function findChildShell(aDocument, aDocS
       return docShell;
   }
   return null;
 }
 
 const gPopupBlockerObserver = {
   _reportButton: null,
 
-  get _pm ()
-    Cc["@mozilla.org/permissionmanager;1"].getService(Ci.nsIPermissionManager),
-
   onUpdatePageReport: function (aEvent)
   {
     if (aEvent.originalTarget != gBrowser.selectedBrowser)
       return;
 
     if (!this._reportButton)
       this._reportButton = document.getElementById("page-report-button");
 
@@ -390,17 +387,17 @@ const gPopupBlockerObserver = {
       // Record the fact that we've reported this blocked popup, so we don't
       // show it again.
       gBrowser.pageReport.reported = true;
     }
   },
 
   toggleAllowPopupsForSite: function (aEvent)
   {
-    var pm = this._pm;
+    var pm = Services.pm;
     var shouldBlock = aEvent.target.getAttribute("block") == "true";
     var perm = shouldBlock ? pm.DENY_ACTION : pm.ALLOW_ACTION;
     pm.add(gBrowser.currentURI, "popup", perm);
 
     gBrowser.getNotificationBox().removeCurrentNotification();
   },
 
   fillPopupList: function (aEvent)
@@ -414,17 +411,17 @@ const gPopupBlockerObserver = {
     //          also back out the fix for bug 343772 where
     //          nsGlobalWindow::CheckOpenAllow() was changed to also
     //          check if the top window's location is whitelisted.
     var uri = gBrowser.currentURI;
     var blockedPopupAllowSite = document.getElementById("blockedPopupAllowSite");
     try {
       blockedPopupAllowSite.removeAttribute("hidden");
 
-      var pm = this._pm;
+      var pm = Services.pm;
       if (pm.testPermission(uri, "popup") == pm.ALLOW_ACTION) {
         // Offer an item to block popups for this site, if a whitelist entry exists
         // already for it.
         let blockString = gNavigatorBundle.getFormattedString("popupBlock", [uri.host]);
         blockedPopupAllowSite.setAttribute("label", blockString);
         blockedPopupAllowSite.setAttribute("block", "true");
       }
       else {
@@ -531,19 +528,17 @@ const gPopupBlockerObserver = {
     var bundlePreferences = document.getElementById("bundle_preferences");
     var params = { blockVisible   : false,
                    sessionVisible : false,
                    allowVisible   : true,
                    prefilledHost  : host,
                    permissionType : "popup",
                    windowTitle    : bundlePreferences.getString("popuppermissionstitle"),
                    introText      : bundlePreferences.getString("popuppermissionstext") };
-    var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"]
-                        .getService(Components.interfaces.nsIWindowMediator);
-    var existingWindow = wm.getMostRecentWindow("Browser:Permissions");
+    var existingWindow = Services.wm.getMostRecentWindow("Browser:Permissions");
     if (existingWindow) {
       existingWindow.initWithParams(params);
       existingWindow.focus();
     }
     else
       window.openDialog("chrome://browser/content/preferences/permissions.xul",
                         "_blank", "resizable,dialog=no,centerscreen", params);
   },
@@ -1024,19 +1019,17 @@ function BrowserStartup() {
           box.removeAttribute("sidebarcommand");
         }
       }
     }
   }
 
   // Certain kinds of automigration rely on this notification to complete their
   // tasks BEFORE the browser window is shown.
-  Cc["@mozilla.org/observer-service;1"]
-    .getService(Ci.nsIObserverService)
-    .notifyObservers(null, "browser-window-before-show", "");
+  Services.obs.notifyObservers(null, "browser-window-before-show", "");
 
   // Set a sane starting width/height for all resolutions on new profiles.
   if (!document.documentElement.hasAttribute("width")) {
     let defaultWidth = 994;
     let defaultHeight;
     if (screen.availHeight <= 600) {
       document.documentElement.setAttribute("sizemode", "maximized");
       defaultWidth = 610;
@@ -1096,28 +1089,29 @@ function HandleAppCommandEvent(evt) {
     BrowserHome();
     break;
   default:
     break;
   }
 }
 
 function prepareForStartup() {
-  var os = Components.classes["@mozilla.org/observer-service;1"].getService(Components.interfaces.nsIObserverService);
-
   gBrowser.addEventListener("DOMUpdatePageReport", gPopupBlockerObserver.onUpdatePageReport, false);
+
   // Note: we need to listen to untrusted events, because the pluginfinder XBL
   // binding can't fire trusted ones (runs with page privileges).
   gBrowser.addEventListener("PluginNotFound", gMissingPluginInstaller.newMissingPlugin, true, true);
   gBrowser.addEventListener("PluginCrashed", gMissingPluginInstaller.pluginInstanceCrashed, true, true);
   gBrowser.addEventListener("PluginBlocklisted", gMissingPluginInstaller.newMissingPlugin, true, true);
   gBrowser.addEventListener("PluginOutdated", gMissingPluginInstaller.newMissingPlugin, true, true);
   gBrowser.addEventListener("PluginDisabled", gMissingPluginInstaller.newDisabledPlugin, true, true);
   gBrowser.addEventListener("NewPluginInstalled", gMissingPluginInstaller.refreshBrowser, false);
-  os.addObserver(gMissingPluginInstaller.pluginCrashed, "plugin-crashed", false);
+
+  Services.obs.addObserver(gMissingPluginInstaller.pluginCrashed, "plugin-crashed", false);
+
   window.addEventListener("AppCommand", HandleAppCommandEvent, true);
 
   var webNavigation;
   try {
     webNavigation = getWebNavigation();
     if (!webNavigation)
       throw "no XBL binding for browser";
   } catch (e) {
@@ -1151,17 +1145,17 @@ function prepareForStartup() {
 
   // Manually hook up session and global history for the first browser
   // so that we don't have to load global history before bringing up a
   // window.
   // Wire up session and global history before any possible
   // progress notifications for back/forward button updating
   webNavigation.sessionHistory = Components.classes["@mozilla.org/browser/shistory;1"]
                                            .createInstance(Components.interfaces.nsISHistory);
-  os.addObserver(gBrowser.browsers[0], "browser:purge-session-history", false);
+  Services.obs.addObserver(gBrowser.browsers[0], "browser:purge-session-history", false);
 
   // remove the disablehistory attribute so the browser cleans up, as
   // though it had done this work itself
   gBrowser.browsers[0].removeAttribute("disablehistory");
 
   // enable global history
   try {
     gBrowser.docShell.QueryInterface(Components.interfaces.nsIDocShellHistory).useGlobalHistory = true;
@@ -1180,19 +1174,18 @@ function prepareForStartup() {
   gBrowser.addEventListener("MozApplicationManifest",
                             OfflineApps, false);
 
   // setup simple gestures support
   gGestureSupport.init(true);
 }
 
 function delayedStartup(isLoadingBlank, mustLoadSidebar) {
-  var os = Cc["@mozilla.org/observer-service;1"].getService(Ci.nsIObserverService);
-  os.addObserver(gSessionHistoryObserver, "browser:purge-session-history", false);
-  os.addObserver(gXPInstallObserver, "xpinstall-install-blocked", false);
+  Services.obs.addObserver(gSessionHistoryObserver, "browser:purge-session-history", false);
+  Services.obs.addObserver(gXPInstallObserver, "xpinstall-install-blocked", false);
 
   BrowserOffline.init();
   OfflineApps.init();
 
   gBrowser.addEventListener("pageshow", function(evt) { setTimeout(pageShowEventHandlers, 0, evt); }, true);
 
   // Ensure login manager is up and running.
   Cc["@mozilla.org/login-manager;1"].getService(Ci.nsILoginManager);
@@ -1251,22 +1244,20 @@ function delayedStartup(isLoadingBlank, 
       var shellBundle = document.getElementById("bundle_shell");
 
       var brandShortName = brandBundle.getString("brandShortName");
       var promptTitle = shellBundle.getString("setDefaultBrowserTitle");
       var promptMessage = shellBundle.getFormattedString("setDefaultBrowserMessage",
                                                          [brandShortName]);
       var checkboxLabel = shellBundle.getFormattedString("setDefaultBrowserDontAsk",
                                                          [brandShortName]);
-      const IPS = Components.interfaces.nsIPromptService;
-      var ps = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
-                                                .getService(IPS);
       var checkEveryTime = { value: shouldCheck };
+      var ps = Services.prompt;
       var rv = ps.confirmEx(window, promptTitle, promptMessage,
-                            IPS.STD_YES_NO_BUTTONS,
+                            ps.STD_YES_NO_BUTTONS,
                             null, null, null, checkboxLabel, checkEveryTime);
       if (rv == 0)
         shell.setDefaultBrowser(true, false);
       shell.shouldCheckDefaultBrowser = checkEveryTime.value;
     }
   }
 #endif
 
@@ -1396,21 +1387,19 @@ function BrowserShutdown()
 
   try {
     FullZoom.destroy();
   }
   catch(ex) {
     Components.utils.reportError(ex);
   }
 
-  var os = Components.classes["@mozilla.org/observer-service;1"]
-    .getService(Components.interfaces.nsIObserverService);
-  os.removeObserver(gSessionHistoryObserver, "browser:purge-session-history");
-  os.removeObserver(gXPInstallObserver, "xpinstall-install-blocked");
-  os.removeObserver(gMissingPluginInstaller.pluginCrashed, "plugin-crashed");
+  Services.obs.removeObserver(gSessionHistoryObserver, "browser:purge-session-history");
+  Services.obs.removeObserver(gXPInstallObserver, "xpinstall-install-blocked");
+  Services.obs.removeObserver(gMissingPluginInstaller.pluginCrashed, "plugin-crashed");
 
   try {
     gBrowser.removeProgressListener(window.XULBrowserWindow);
     gBrowser.removeTabsProgressListener(window.TabsProgressListener);
   } catch (ex) {
   }
 
   PlacesStarButton.uninit();
@@ -1423,19 +1412,17 @@ function BrowserShutdown()
     Components.utils.reportError(ex);
   }
 
   BrowserOffline.uninit();
   OfflineApps.uninit();
   DownloadMonitorPanel.uninit();
   gPrivateBrowsingUI.uninit();
 
-  var windowManager = Components.classes['@mozilla.org/appshell/window-mediator;1'].getService();
-  var windowManagerInterface = windowManager.QueryInterface(Components.interfaces.nsIWindowMediator);
-  var enumerator = windowManagerInterface.getEnumerator(null);
+  var enumerator = Services.wm.getEnumerator(null);
   enumerator.getNext();
   if (!enumerator.hasMoreElements()) {
     document.persist("sidebar-box", "sidebarcommand");
     document.persist("sidebar-box", "width");
     document.persist("sidebar-box", "src");
     document.persist("sidebar-title", "value");
   }
 
@@ -1944,29 +1931,27 @@ function loadURI(uri, referrer, postData
   } catch (e) {
   }
 }
 
 function getShortcutOrURI(aURL, aPostDataRef) {
   var shortcutURL = null;
   var keyword = aURL;
   var param = "";
-  var searchService = Cc["@mozilla.org/browser/search-service;1"].
-                      getService(Ci.nsIBrowserSearchService);
 
   var offset = aURL.indexOf(" ");
   if (offset > 0) {
     keyword = aURL.substr(0, offset);
     param = aURL.substr(offset + 1);
   }
 
   if (!aPostDataRef)
     aPostDataRef = {};
 
-  var engine = searchService.getEngineByAlias(keyword);
+  var engine = Services.search.getEngineByAlias(keyword);
   if (engine) {
     var submission = engine.getSubmission(param, null);
     aPostDataRef.value = submission.postData;
     return submission.uri.spec;
   }
 
   [shortcutURL, aPostDataRef.value] =
     PlacesUtils.getURLAndPostDataForKeyword(keyword);
@@ -2810,21 +2795,20 @@ var homeButtonObserver = {
     {
       var statusTextFld = document.getElementById("statusbar-display");
       statusTextFld.label = "";
     }
 }
 
 function openHomeDialog(aURL)
 {
-  var promptService = Components.classes["@mozilla.org/embedcomp/prompt-service;1"].getService(Components.interfaces.nsIPromptService);
   var promptTitle = gNavigatorBundle.getString("droponhometitle");
   var promptMsg   = gNavigatorBundle.getString("droponhomemsg");
-  var pressedVal  = promptService.confirmEx(window, promptTitle, promptMsg,
-                          promptService.STD_YES_NO_BUTTONS,
+  var pressedVal  = Services.prompt.confirmEx(window, promptTitle, promptMsg,
+                          Services.prompt.STD_YES_NO_BUTTONS,
                           null, null, null, null, {value:0});
 
   if (pressedVal == 0) {
     try {
       var str = Components.classes["@mozilla.org/supports-string;1"]
                           .createInstance(Components.interfaces.nsISupportsString);
       str.data = aURL;
       gPrefService.setComplexValue("browser.startup.homepage",
@@ -3067,19 +3051,17 @@ const BrowserSearch = {
     if (gBrowser.shouldLoadFavIcon(targetDoc.documentURIObject))
       iconURL = targetDoc.documentURIObject.prePath + "/favicon.ico";
 
     var hidden = false;
     // If this engine (identified by title) is already in the list, add it
     // to the list of hidden engines rather than to the main list.
     // XXX This will need to be changed when engines are identified by URL;
     // see bug 335102.
-    var searchService = Cc["@mozilla.org/browser/search-service;1"].
-                        getService(Ci.nsIBrowserSearchService);
-    if (searchService.getEngineByName(engine.title))
+    if (Services.search.getEngineByName(engine.title))
       hidden = true;
 
     var engines = (hidden ? browser.hiddenEngines : browser.engines) || [];
 
     engines.push({ uri: engine.href,
                    title: engine.title,
                    icon: iconURL });
 
@@ -3145,45 +3127,40 @@ const BrowserSearch = {
     var searchBar = this.searchBar;
     if (searchBar && window.fullScreen)
       FullScreen.mouseoverToggle(true);
 
     if (isElementVisible(searchBar)) {
       searchBar.select();
       searchBar.focus();
     } else {
-      var ss = Cc["@mozilla.org/browser/search-service;1"].
-               getService(Ci.nsIBrowserSearchService);
-      var searchForm = ss.defaultEngine.searchForm;
-      openUILinkIn(searchForm, "current");
+      openUILinkIn(Services.search.defaultEngine.searchForm, "current");
     }
   },
 
   /**
    * Loads a search results page, given a set of search terms. Uses the current
    * engine if the search bar is visible, or the default engine otherwise.
    *
    * @param searchText
    *        The search terms to use for the search.
    *
    * @param useNewTab
    *        Boolean indicating whether or not the search should load in a new
    *        tab.
    */
   loadSearch: function BrowserSearch_search(searchText, useNewTab) {
-    var ss = Cc["@mozilla.org/browser/search-service;1"].
-             getService(Ci.nsIBrowserSearchService);
     var engine;
   
     // If the search bar is visible, use the current engine, otherwise, fall
     // back to the default engine.
     if (isElementVisible(this.searchBar))
-      engine = ss.currentEngine;
+      engine = Services.search.currentEngine;
     else
-      engine = ss.defaultEngine;
+      engine = Services.search.defaultEngine;
   
     var submission = engine.getSubmission(searchText, null); // HTML response
 
     // getSubmission can return null if the engine doesn't have a URL
     // with a text/html response type.  This is unlikely (since
     // SearchService._addEngineToStore() should fail for such an engine),
     // but let's be on the safe side.
     if (!submission)
@@ -3306,19 +3283,17 @@ function toJavaScriptConsole()
 function BrowserDownloadsUI()
 {
   Cc["@mozilla.org/download-manager-ui;1"].
   getService(Ci.nsIDownloadManagerUI).show(window);
 }
 
 function toOpenWindowByType(inType, uri, features)
 {
-  var windowManager = Components.classes['@mozilla.org/appshell/window-mediator;1'].getService();
-  var windowManagerInterface = windowManager.QueryInterface(Components.interfaces.nsIWindowMediator);
-  var topWindow = windowManagerInterface.getMostRecentWindow(inType);
+  var topWindow = Services.wm.getMostRecentWindow(inType);
 
   if (topWindow)
     topWindow.focus();
   else if (features)
     window.open(uri, "_blank", features);
   else
     window.open(uri, "_blank", "chrome,extrachrome,menubar,resizable,scrollbars,status,toolbar");
 }
@@ -4322,39 +4297,34 @@ var XULBrowserWindow = {
   startDocumentLoad: function (aRequest) {
     // clear out feed data
     gBrowser.selectedBrowser.feeds = null;
 
     // clear out search-engine data
     gBrowser.selectedBrowser.engines = null;    
 
     var uri = aRequest.QueryInterface(Ci.nsIChannel).URI;
-    var observerService = Cc["@mozilla.org/observer-service;1"]
-                            .getService(Ci.nsIObserverService);
 
     if (gURLBar &&
         gURLBar.value == "" &&
         getWebNavigation().currentURI.spec == "about:blank")
       URLBarSetURI(uri);
 
     try {
-      observerService.notifyObservers(content, "StartDocumentLoad", uri.spec);
+      Services.obs.notifyObservers(content, "StartDocumentLoad", uri.spec);
     } catch (e) {
     }
   },
 
   endDocumentLoad: function (aRequest, aStatus) {
     var urlStr = aRequest.QueryInterface(Ci.nsIChannel).originalURI.spec;
 
-    var observerService = Cc["@mozilla.org/observer-service;1"]
-                            .getService(Ci.nsIObserverService);
-
     var notification = Components.isSuccessCode(aStatus) ? "EndDocumentLoad" : "FailDocumentLoad";
     try {
-      observerService.notifyObservers(content, notification, urlStr);
+      Services.obs.notifyObservers(content, notification, urlStr);
     } catch (e) {
     }
   }
 };
 
 var CombinedStopReload = {
   init: function () {
     if (this._initialized)
@@ -5166,25 +5136,23 @@ function SelectDetector(event, doReload)
 {
     var uri =  event.target.getAttribute("id");
     var prefvalue = uri.substring('chardet.'.length, uri.length);
     if ("off" == prefvalue) { // "off" is special value to turn off the detectors
         prefvalue = "";
     }
 
     try {
-        var pref = Components.classes["@mozilla.org/preferences-service;1"]
-                             .getService(Components.interfaces.nsIPrefBranch);
-        var str =  Components.classes["@mozilla.org/supports-string;1"]
-                             .createInstance(Components.interfaces.nsISupportsString);
+        var str =  Cc["@mozilla.org/supports-string;1"].
+                   createInstance(Ci.nsISupportsString);
 
         str.data = prefvalue;
-        pref.setComplexValue("intl.charset.detector",
-                             Components.interfaces.nsISupportsString, str);
-        if (doReload) window.content.location.reload();
+        gPrefService.setComplexValue("intl.charset.detector", Ci.nsISupportsString, str);
+        if (doReload)
+          window.content.location.reload();
     }
     catch (ex) {
         dump("Failed to set the intl.charset.detector preference.\n");
     }
 }
 
 function SetForcedDetector(doReload)
 {
@@ -5226,69 +5194,55 @@ function UpdateCurrentCharset()
     }
 
     var menuitem = document.getElementById('charset.' + wnd.document.characterSet);
     if (menuitem) {
         menuitem.setAttribute('checked', 'true');
     }
 }
 
-function UpdateCharsetDetector()
-{
-    var prefvalue;
-
-    try {
-        var pref = Components.classes["@mozilla.org/preferences-service;1"]
-                             .getService(Components.interfaces.nsIPrefBranch);
-        prefvalue = pref.getComplexValue("intl.charset.detector",
-                                         Components.interfaces.nsIPrefLocalizedString).data;
-    }
-    catch (ex) {
-        prefvalue = "";
-    }
-
-    if (prefvalue == "") prefvalue = "off";
-    dump("intl.charset.detector = "+ prefvalue + "\n");
-
-    prefvalue = 'chardet.' + prefvalue;
-    var menuitem = document.getElementById(prefvalue);
-
-    if (menuitem) {
-        menuitem.setAttribute('checked', 'true');
-    }
+function UpdateCharsetDetector() {
+  var prefvalue = "off";
+
+  try {
+    prefvalue = gPrefService.getComplexValue("intl.charset.detector", Ci.nsIPrefLocalizedString).data;
+  }
+  catch (ex) {}
+
+  prefvalue = "chardet." + prefvalue;
+
+  var menuitem = document.getElementById(prefvalue);
+  if (menuitem)
+    menuitem.setAttribute("checked", "true");
 }
 
-function UpdateMenus(event)
-{
-    // use setTimeout workaround to delay checkmark the menu
-    // when onmenucomplete is ready then use it instead of oncreate
-    // see bug 78290 for the detail
-    UpdateCurrentCharset();
-    setTimeout(UpdateCurrentCharset, 0);
-    UpdateCharsetDetector();
-    setTimeout(UpdateCharsetDetector, 0);
+function UpdateMenus(event) {
+  // use setTimeout workaround to delay checkmark the menu
+  // when onmenucomplete is ready then use it instead of oncreate
+  // see bug 78290 for the detail
+  UpdateCurrentCharset();
+  setTimeout(UpdateCurrentCharset, 0);
+  UpdateCharsetDetector();
+  setTimeout(UpdateCharsetDetector, 0);
 }
 
-function CreateMenu(node)
-{
-  var observerService = Components.classes["@mozilla.org/observer-service;1"].getService(Components.interfaces.nsIObserverService);
-  observerService.notifyObservers(null, "charsetmenu-selected", node);
+function CreateMenu(node) {
+  Services.obs.notifyObservers(null, "charsetmenu-selected", node);
 }
 
-function charsetLoadListener (event)
-{
-    var charset = window.content.document.characterSet;
-
-    if (charset.length > 0 && (charset != gLastBrowserCharset)) {
-        if (!gCharsetMenu)
-          gCharsetMenu = Components.classes['@mozilla.org/rdf/datasource;1?name=charset-menu'].getService().QueryInterface(Components.interfaces.nsICurrentCharsetListener);
-        gCharsetMenu.SetCurrentCharset(charset);
-        gPrevCharset = gLastBrowserCharset;
-        gLastBrowserCharset = charset;
-    }
+function charsetLoadListener(event) {
+  var charset = window.content.document.characterSet;
+
+  if (charset.length > 0 && (charset != gLastBrowserCharset)) {
+    if (!gCharsetMenu)
+      gCharsetMenu = Cc['@mozilla.org/rdf/datasource;1?name=charset-menu'].getService(Ci.nsICurrentCharsetListener);
+    gCharsetMenu.SetCurrentCharset(charset);
+    gPrevCharset = gLastBrowserCharset;
+    gLastBrowserCharset = charset;
+  }
 }
 
 /* Begin Page Style Functions */
 function getAllStyleSheets(frameset) {
   var styleSheetsArray = Array.slice(frameset.document.styleSheets);
   for (let i = 0; i < frameset.frames.length; i++) {
     let frameSheets = getAllStyleSheets(frameset.frames[i]);
     styleSheetsArray = styleSheetsArray.concat(frameSheets);
@@ -5388,38 +5342,32 @@ function setStyleDisabled(disabled) {
 var BrowserOffline = {
   /////////////////////////////////////////////////////////////////////////////
   // BrowserOffline Public Methods
   init: function ()
   {
     if (!this._uiElement)
       this._uiElement = document.getElementById("goOfflineMenuitem");
 
-    var os = Components.classes["@mozilla.org/observer-service;1"].getService(Components.interfaces.nsIObserverService);
-    os.addObserver(this, "network:offline-status-changed", false);
-
-    var ioService = Components.classes["@mozilla.org/network/io-service;1"].
-      getService(Components.interfaces.nsIIOService2);
-
-    this._updateOfflineUI(ioService.offline);
+    Services.obs.addObserver(this, "network:offline-status-changed", false);
+
+    this._updateOfflineUI(Services.io.offline);
   },
 
   uninit: function ()
   {
     try {
-      var os = Components.classes["@mozilla.org/observer-service;1"].getService(Components.interfaces.nsIObserverService);
-      os.removeObserver(this, "network:offline-status-changed");
+      Services.obs.removeObserver(this, "network:offline-status-changed");
     } catch (ex) {
     }
   },
 
   toggleOfflineStatus: function ()
   {
-    var ioService = Components.classes["@mozilla.org/network/io-service;1"].
-      getService(Components.interfaces.nsIIOService2);
+    var ioService = Services.io;
 
     // Stop automatic management of the offline status
     try {
       ioService.manageOfflineStatus = false;
     } catch (ex) {
     }
   
     if (!ioService.offline && !this._canGoOffline()) {
@@ -5443,29 +5391,27 @@ var BrowserOffline = {
 
     this._updateOfflineUI(aState == "offline");
   },
 
   /////////////////////////////////////////////////////////////////////////////
   // BrowserOffline Implementation Methods
   _canGoOffline: function ()
   {
-    var os = Components.classes["@mozilla.org/observer-service;1"].getService(Components.interfaces.nsIObserverService);
-    if (os) {
-      try {
-        var cancelGoOffline = Components.classes["@mozilla.org/supports-PRBool;1"].createInstance(Components.interfaces.nsISupportsPRBool);
-        os.notifyObservers(cancelGoOffline, "offline-requested", null);
-
-        // Something aborted the quit process.
-        if (cancelGoOffline.data)
-          return false;
-      }
-      catch (ex) {
-      }
-    }
+    try {
+      var cancelGoOffline = Cc["@mozilla.org/supports-PRBool;1"].createInstance(Ci.nsISupportsPRBool);
+      Services.obs.notifyObservers(cancelGoOffline, "offline-requested", null);
+
+      // Something aborted the quit process.
+      if (cancelGoOffline.data)
+        return false;
+    }
+    catch (ex) {
+    }
+
     return true;
   },
 
   _uiElement: null,
   _updateOfflineUI: function (aOffline)
   {
     var offlineLocked = gPrefService.prefIsLocked("network.online");
     if (offlineLocked)
@@ -5475,28 +5421,24 @@ var BrowserOffline = {
   }
 };
 
 var OfflineApps = {
   /////////////////////////////////////////////////////////////////////////////
   // OfflineApps Public Methods
   init: function ()
   {
-    var obs = Cc["@mozilla.org/observer-service;1"].
-              getService(Ci.nsIObserverService);
-    obs.addObserver(this, "dom-storage-warn-quota-exceeded", false);
-    obs.addObserver(this, "offline-cache-update-completed", false);
+    Services.obs.addObserver(this, "dom-storage-warn-quota-exceeded", false);
+    Services.obs.addObserver(this, "offline-cache-update-completed", false);
   },
 
   uninit: function ()
   {
-    var obs = Cc["@mozilla.org/observer-service;1"].
-              getService(Ci.nsIObserverService);
-    obs.removeObserver(this, "dom-storage-warn-quota-exceeded");
-    obs.removeObserver(this, "offline-cache-update-completed");
+    Services.obs.removeObserver(this, "dom-storage-warn-quota-exceeded");
+    Services.obs.removeObserver(this, "offline-cache-update-completed");
   },
 
   handleEvent: function(event) {
     if (event.type == "MozApplicationManifest") {
       this.offlineAppRequested(event.originalTarget.defaultView);
     }
   },
 
@@ -5583,55 +5525,47 @@ var OfflineApps = {
 
       notificationBox.appendNotification(message, "offline-app-usage",
                                          "chrome://browser/skin/Info.png",
                                          priority, buttons);
     }
 
     // Now that we've warned once, prevent the warning from showing up
     // again.
-    var pm = Cc["@mozilla.org/permissionmanager;1"].
-             getService(Ci.nsIPermissionManager);
-    pm.add(aURI, "offline-app",
-           Ci.nsIOfflineCacheUpdateService.ALLOW_NO_WARN);
+    Services.pm.add(aURI, "offline-app",
+                    Ci.nsIOfflineCacheUpdateService.ALLOW_NO_WARN);
   },
 
   // XXX: duplicated in preferences/advanced.js
   _getOfflineAppUsage: function (host, groups)
   {
-    var cacheService = Components.classes["@mozilla.org/network/application-cache-service;1"].
-                       getService(Components.interfaces.nsIApplicationCacheService);
+    var cacheService = Cc["@mozilla.org/network/application-cache-service;1"].
+                       getService(Ci.nsIApplicationCacheService);
     if (!groups)
       groups = cacheService.getGroups();
 
-    var ios = Components.classes["@mozilla.org/network/io-service;1"].
-              getService(Components.interfaces.nsIIOService);
-
     var usage = 0;
     for (var i = 0; i < groups.length; i++) {
-      var uri = ios.newURI(groups[i], null, null);
+      var uri = Services.io.newURI(groups[i], null, null);
       if (uri.asciiHost == host) {
         var cache = cacheService.getActiveCache(groups[i]);
         usage += cache.usage;
       }
     }
 
-    var storageManager = Components.classes["@mozilla.org/dom/storagemanager;1"].
-                         getService(Components.interfaces.nsIDOMStorageManager);
+    var storageManager = Cc["@mozilla.org/dom/storagemanager;1"].
+                         getService(Ci.nsIDOMStorageManager);
     usage += storageManager.getUsage(host);
 
     return usage;
   },
 
   _checkUsage: function(aURI) {
-    var pm = Cc["@mozilla.org/permissionmanager;1"].
-             getService(Ci.nsIPermissionManager);
-
     // if the user has already allowed excessive usage, don't bother checking
-    if (pm.testExactPermission(aURI, "offline-app") !=
+    if (Services.pm.testExactPermission(aURI, "offline-app") !=
         Ci.nsIOfflineCacheUpdateService.ALLOW_NO_WARN) {
       var usage = this._getOfflineAppUsage(aURI.asciiHost);
       var warnQuota = gPrefService.getIntPref("offline-apps.quota.warn");
       if (usage >= warnQuota * 1024) {
         return true;
       }
     }
 
@@ -5643,22 +5577,19 @@ var OfflineApps = {
       return;
     }
 
     var browserWindow = this._getBrowserWindowForContentWindow(aContentWindow);
     var browser = this._getBrowserForContentWindow(browserWindow,
                                                    aContentWindow);
 
     var currentURI = aContentWindow.document.documentURIObject;
-    var pm = Cc["@mozilla.org/permissionmanager;1"].
-             getService(Ci.nsIPermissionManager);
 
     // don't bother showing UI if the user has already made a decision
-    if (pm.testExactPermission(currentURI, "offline-app") !=
-        Ci.nsIPermissionManager.UNKNOWN_ACTION)
+    if (Services.pm.testExactPermission(currentURI, "offline-app") != Services.pm.UNKNOWN_ACTION)
       return;
 
     try {
       if (gPrefService.getBoolPref("offline-apps.allow_by_default")) {
         // all pages can use offline capabilities, no need to ask the user
         return;
       }
     } catch(e) {
@@ -5702,32 +5633,26 @@ var OfflineApps = {
         notificationBox.appendNotification(message, notificationID,
                                            "chrome://browser/skin/Info.png",
                                            priority, buttons);
       notification.documents = [ aContentWindow.document ];
     }
   },
 
   allowSite: function(aDocument) {
-    var pm = Cc["@mozilla.org/permissionmanager;1"].
-             getService(Ci.nsIPermissionManager);
-    pm.add(aDocument.documentURIObject, "offline-app",
-           Ci.nsIPermissionManager.ALLOW_ACTION);
+    Services.pm.add(aDocument.documentURIObject, "offline-app", Services.pm.ALLOW_ACTION);
 
     // When a site is enabled while loading, manifest resources will
     // start fetching immediately.  This one time we need to do it
     // ourselves.
     this._startFetching(aDocument);
   },
 
   disallowSite: function(aDocument) {
-    var pm = Cc["@mozilla.org/permissionmanager;1"].
-             getService(Ci.nsIPermissionManager);
-    pm.add(aDocument.documentURIObject, "offline-app",
-           Ci.nsIPermissionManager.DENY_ACTION);
+    Services.pm.add(aDocument.documentURIObject, "offline-app", Services.pm.DENY_ACTION);
   },
 
   manage: function() {
     openAdvancedPreferences("networkTab");
   },
 
   _startFetching: function(aDocument) {
     if (!aDocument.documentElement)
@@ -5799,32 +5724,31 @@ function WindowIsClosing()
  */
 function warnAboutClosingWindow() {
   // Popups aren't considered full browser windows.
   if (!toolbar.visible)
     return gBrowser.warnAboutClosingTabs(true);
 
   // Figure out if there's at least one other browser window around.
   let foundOtherBrowserWindow = false;
-  let wm = Cc["@mozilla.org/appshell/window-mediator;1"].getService(Ci.nsIWindowMediator);
-  let e = wm.getEnumerator("navigator:browser");
+  let e = Services.wm.getEnumerator("navigator:browser");
   while (e.hasMoreElements() && !foundOtherBrowserWindow) {
     let win = e.getNext();
     if (win != window && win.toolbar.visible)
       foundOtherBrowserWindow = true;
   }
   if (foundOtherBrowserWindow)
     return gBrowser.warnAboutClosingTabs(true);
 
-  let os = Cc["@mozilla.org/observer-service;1"].getService(Ci.nsIObserverService);
+  let os = Services.obs;
 
   let closingCanceled = Cc["@mozilla.org/supports-PRBool;1"].
                         createInstance(Ci.nsISupportsPRBool);
   os.notifyObservers(closingCanceled,
-                                   "browser-lastwindow-close-requested", null);
+                     "browser-lastwindow-close-requested", null);
   if (closingCanceled.data)
     return false;
 
   os.notifyObservers(null, "browser-lastwindow-close-granted", null);
 
 #ifdef XP_MACOSX
   // OS X doesn't quit the application when the last window is closed, but keeps
   // the session alive. Hence don't prompt users to save tabs, but warn about
@@ -5855,46 +5779,42 @@ var MailIntegration = {
     this._launchExternalUrl(uri);
   },
 
   // a generic method which can be used to pass arbitrary urls to the operating
   // system.
   // aURL --> a nsIURI which represents the url to launch
   _launchExternalUrl: function (aURL) {
     var extProtocolSvc =
-       Components.classes["@mozilla.org/uriloader/external-protocol-service;1"]
-                 .getService(Components.interfaces.nsIExternalProtocolService);
+       Cc["@mozilla.org/uriloader/external-protocol-service;1"]
+         .getService(Ci.nsIExternalProtocolService);
     if (extProtocolSvc)
       extProtocolSvc.loadUrl(aURL);
   }
 };
 
-function BrowserOpenAddonsMgr(aPane)
-{
+function BrowserOpenAddonsMgr(aPane) {
   const EMTYPE = "Extension:Manager";
-  var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"]
-                     .getService(Components.interfaces.nsIWindowMediator);
-  var theEM = wm.getMostRecentWindow(EMTYPE);
+  var theEM = Services.wm.getMostRecentWindow(EMTYPE);
   if (theEM) {
     theEM.focus();
     if (aPane)
       theEM.showView(aPane);
     return;
   }
 
   const EMURL = "chrome://mozapps/content/extensions/extensions.xul";
   const EMFEATURES = "chrome,menubar,extra-chrome,toolbar,dialog=no,resizable";
   if (aPane)
     window.openDialog(EMURL, "", EMFEATURES, aPane);
   else
     window.openDialog(EMURL, "", EMFEATURES);
 }
 
-function AddKeywordForSearchField()
-{
+function AddKeywordForSearchField() {
   var node = document.popupNode;
 
   var charset = node.ownerDocument.characterSet;
 
   var docURI = makeURI(node.ownerDocument.URL,
                        charset);
 
   var formURI = makeURI(node.form.getAttribute("action"),
@@ -7151,37 +7071,34 @@ function getNotificationBox(aWindow) {
   return null;
 };
 
 /* DEPRECATED */
 function getBrowser() gBrowser;
 function getNavToolbox() gNavToolbox;
 
 let gPrivateBrowsingUI = {
-  _observerService: null,
   _privateBrowsingService: null,
   _searchBarValue: null,
   _findBarValue: null,
 
   init: function PBUI_init() {
-    this._observerService = Cc["@mozilla.org/observer-service;1"].
-                            getService(Ci.nsIObserverService);
-    this._observerService.addObserver(this, "private-browsing", false);
-    this._observerService.addObserver(this, "private-browsing-transition-complete", false);
+    Services.obs.addObserver(this, "private-browsing", false);
+    Services.obs.addObserver(this, "private-browsing-transition-complete", false);
 
     this._privateBrowsingService = Cc["@mozilla.org/privatebrowsing;1"].
                                    getService(Ci.nsIPrivateBrowsingService);
 
     if (this.privateBrowsingEnabled)
       this.onEnterPrivateBrowsing(true);
   },
 
   uninit: function PBUI_unint() {
-    this._observerService.removeObserver(this, "private-browsing");
-    this._observerService.removeObserver(this, "private-browsing-transition-complete");
+    Services.obs.removeObserver(this, "private-browsing");
+    Services.obs.removeObserver(this, "private-browsing-transition-complete");
   },
 
   get _disableUIOnToggle PBUI__disableUIOnTogle() {
     if (this._privateBrowsingService.autoStarted)
       return false;
 
     try {
       return !gPrefService.getBoolPref("browser.privatebrowsing.keep_current_session");
@@ -7230,32 +7147,31 @@ let gPrivateBrowsingUI = {
     var dialogTitle = pbBundle.GetStringFromName("privateBrowsingMessageHeader");
     var header = "";
 #else
     var dialogTitle = pbBundle.GetStringFromName("privateBrowsingDialogTitle");
     var header = pbBundle.GetStringFromName("privateBrowsingMessageHeader") + "\n\n";
 #endif
     var message = pbBundle.formatStringFromName("privateBrowsingMessage", [appName], 1);
 
-    var promptService = Cc["@mozilla.org/embedcomp/prompt-service;1"].
-                        getService(Ci.nsIPromptService);
-
-    var flags = promptService.BUTTON_TITLE_IS_STRING * promptService.BUTTON_POS_0 +
-                promptService.BUTTON_TITLE_IS_STRING * promptService.BUTTON_POS_1 +
-                promptService.BUTTON_POS_0_DEFAULT;
+    var ps = Services.prompt;
+
+    var flags = ps.BUTTON_TITLE_IS_STRING * ps.BUTTON_POS_0 +
+                ps.BUTTON_TITLE_IS_STRING * ps.BUTTON_POS_1 +
+                ps.BUTTON_POS_0_DEFAULT;
 
     var neverAsk = {value:false};
     var button0Title = pbBundle.GetStringFromName("privateBrowsingYesTitle");
     var button1Title = pbBundle.GetStringFromName("privateBrowsingNoTitle");
     var neverAskText = pbBundle.GetStringFromName("privateBrowsingNeverAsk");
 
     var result;
-    var choice = promptService.confirmEx(null, dialogTitle, header + message,
-                               flags, button0Title, button1Title, null,
-                               neverAskText, neverAsk);
+    var choice = ps.confirmEx(null, dialogTitle, header + message,
+                              flags, button0Title, button1Title, null,
+                              neverAskText, neverAsk);
 
     switch (choice) {
     case 0: // Start Private Browsing
       result = true;
       if (neverAsk.value)
         gPrefService.setBoolPref("browser.privatebrowsing.dont_prompt_on_enter", true);
       break;
     case 1: // Keep
@@ -7530,17 +7446,17 @@ var LightWeightThemeWebInstaller = {
     this._previewWindow.removeEventListener("pagehide", this, true);
     this._previewWindow = null;
     gBrowser.tabContainer.removeEventListener("TabSelect", this, false);
 
     this._manager.resetPreview();
   },
 
   _isAllowed: function (node) {
-    var pm = Cc["@mozilla.org/permissionmanager;1"].getService(Ci.nsIPermissionManager);
+    var pm = Services.pm;
 
     var prefs = [["xpinstall.whitelist.add", pm.ALLOW_ACTION],
                  ["xpinstall.whitelist.add.36", pm.ALLOW_ACTION],
                  ["xpinstall.blacklist.add", pm.DENY_ACTION]];
     prefs.forEach(function ([pref, permission]) {
       try {
         var hosts = gPrefService.getCharPref(pref);
       } catch (e) {}
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/browser_utilityOverlay.js
@@ -0,0 +1,35 @@
+var gTestTab;
+
+function test() {
+  waitForExplicitFinish();
+
+  is(getTopWin(), window, "got top window");
+  is(getBoolPref("general.startup.browser", false), true, "getBoolPref");
+  is(getBoolPref("this.pref.doesnt.exist", true), true, "getBoolPref fallback");
+  is(getBoolPref("this.pref.doesnt.exist", false), false, "getBoolPref fallback #2");
+
+
+  gTestTab = openNewTabWith("http://example.com");
+  gBrowser.selectedTab = gTestTab;
+  gTestTab.linkedBrowser.addEventListener("load", function () {
+    gTestTab.linkedBrowser.removeEventListener("load", arguments.callee, true);
+
+    is(gTestTab.linkedBrowser.currentURI.spec, "http://example.com/", "example.com loaded");
+
+    test_openUILink();
+  }, true);
+}
+
+function test_openUILink() {
+  gTestTab.linkedBrowser.addEventListener("load", function () {
+    gTestTab.linkedBrowser.removeEventListener("load", arguments.callee, true);
+
+    is(gTestTab.linkedBrowser.currentURI.spec, "http://example.org/", "example.org loaded");
+
+    gBrowser.removeTab(gTestTab);
+    finish();
+  }, true);
+
+  //openUILink(url, e, ignoreButton, ignoreAlt, allowKeywordFixup, postData, referrerUrl);
+  openUILink("http://example.org"); // defaults to "current"
+}
--- a/browser/base/content/utilityOverlay.js
+++ b/browser/base/content/utilityOverlay.js
@@ -17,35 +17,34 @@
 # The Initial Developer of the Original Code is
 # Netscape Communications Corporation.
 # Portions created by the Initial Developer are Copyright (C) 1998
 # the Initial Developer. All Rights Reserved.
 #
 # Contributor(s):
 #   Alec Flett <alecf@netscape.com>
 #   Ehsan Akhgari <ehsan.akhgari@gmail.com>
+#   Gavin Sharp <gavin@gavinsharp.com>
 #
 # Alternatively, the contents of this file may be used under the terms of
 # either the GNU General Public License Version 2 or later (the "GPL"), or
 # the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 # in which case the provisions of the GPL or the LGPL are applicable instead
 # of those above. If you wish to allow use of your version of this file only
 # under the terms of either the GPL or the LGPL, and not to allow others to
 # use your version of this file under the terms of the MPL, indicate your
 # decision by deleting the provisions above and replace them with the notice
 # and other provisions required by the GPL or the LGPL. If you do not delete
 # the provisions above, a recipient may use your version of this file under
 # the terms of any one of the MPL, the GPL or the LGPL.
 #
 # ***** END LICENSE BLOCK *****
 
-/**
- * Communicator Shared Utility Library
- * for shared application glue for the Communicator suite of applications
- **/
+// Services = object with smart getters for common XPCOM services
+Components.utils.import("resource://gre/modules/Services.jsm");
 
 var TAB_DROP_TYPE = "application/x-moz-tabbrowser-tab";
 
 var gBidiUI = false;
 
 function getBrowserURL()
 {
   return "chrome://browser/content/browser.xul";
@@ -64,32 +63,28 @@ function goToggleToolbar( id, elementID 
       element.setAttribute("checked", isHidden ? "true" : "false");
       document.persist(elementID, 'checked');
     }
   }
 }
 
 function getTopWin()
 {
-  var windowManager = Components.classes['@mozilla.org/appshell/window-mediator;1']
-                                .getService(Components.interfaces.nsIWindowMediator);
-  return windowManager.getMostRecentWindow("navigator:browser");
+  return Services.wm.getMostRecentWindow("navigator:browser");
 }
 
 function openTopWin( url )
 {
   openUILink(url, {})
 }
 
-function getBoolPref ( prefname, def )
+function getBoolPref(prefname, def)
 {
-  try { 
-    var pref = Components.classes["@mozilla.org/preferences-service;1"]
-                       .getService(Components.interfaces.nsIPrefBranch);
-    return pref.getBoolPref(prefname);
+  try {
+    return Services.prefs.getBoolPref(prefname);
   }
   catch(er) {
     return def;
   }
 }
 
 // openUILink handles clicks on UI elements that cause URLs to load.
 function openUILink( url, e, ignoreButton, ignoreAlt, allowKeywordFixup, postData, referrerUrl )
@@ -226,17 +221,17 @@ function openUILinkIn(url, where, aAllow
                   getBrowserURL(),
                   null,
                   "chrome,dialog=no,all",
                   sa);
 
     return;
   }
 
-  var loadInBackground = getBoolPref("browser.tabs.loadBookmarksInBackground", false);
+  var loadInBackground = getBoolPref("browser.tabs.loadBookmarksInBackground");
 
   switch (where) {
   case "current":
     w.loadURI(url, aReferrerURI, aPostData, aAllowThirdPartyFixup);
     break;
   case "tabshifted":
     loadInBackground = !loadInBackground;
     // fall through
@@ -370,50 +365,44 @@ function isBidiEnabled() {
 
     switch (systemLocale) {
       case "ar-":
       case "he-":
       case "fa-":
       case "ur-":
       case "syr":
         rv = true;
-        var pref = Components.classes["@mozilla.org/preferences-service;1"]
-                             .getService(Components.interfaces.nsIPrefBranch);
-        pref.setBoolPref("bidi.browser.ui", true);
+        Services.prefs.setBoolPref("bidi.browser.ui", true);
     }
   } catch (e) {}
 
   return rv;
 }
 
 function openAboutDialog()
 {
 #ifdef XP_MACOSX
-  var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"]
-                     .getService(Components.interfaces.nsIWindowMediator);
-  var win = wm.getMostRecentWindow("Browser:About");
+  var win = Services.wm.getMostRecentWindow("Browser:About");
   if (win)
     win.focus();
   else {
     window.openDialog("chrome://browser/content/aboutDialog.xul", "About",
                       "chrome, resizable=no, minimizable=no");
   }
 #else
   window.openDialog("chrome://browser/content/aboutDialog.xul", "About", "centerscreen,chrome,resizable=no");
 #endif
 }
 
 function openPreferences(paneID, extraArgs)
 {
   var instantApply = getBoolPref("browser.preferences.instantApply", false);
   var features = "chrome,titlebar,toolbar,centerscreen" + (instantApply ? ",dialog=no" : ",modal");
 
-  var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"]
-                     .getService(Components.interfaces.nsIWindowMediator);
-  var win = wm.getMostRecentWindow("Browser:Preferences");
+  var win = Services.wm.getMostRecentWindow("Browser:Preferences");
   if (win) {
     win.focus();
     if (paneID) {
       var pane = win.document.getElementById(paneID);
       win.document.documentElement.showPane(pane);
     }
 
     if (extraArgs && extraArgs["advancedTab"]) {
@@ -582,27 +571,17 @@ function makeURLAbsolute(aBase, aUrl)
  *        There will be no security check.
  */ 
 function openNewTabWith(aURL, aDocument, aPostData, aEvent,
                         aAllowThirdPartyFixup, aReferrer)
 {
   if (aDocument)
     urlSecurityCheck(aURL, aDocument.nodePrincipal);
 
-  var prefSvc = Components.classes["@mozilla.org/preferences-service;1"]
-                          .getService(Components.interfaces.nsIPrefService);
-  prefSvc = prefSvc.getBranch(null);
-
-  // should we open it in a new tab?
-  var loadInBackground = true;
-  try {
-    loadInBackground = prefSvc.getBoolPref("browser.tabs.loadInBackground");
-  }
-  catch(ex) {
-  }
+  var loadInBackground = getBoolPref("browser.tabs.loadInBackground");
 
   if (aEvent && aEvent.shiftKey)
     loadInBackground = !loadInBackground;
 
   // As in openNewWindowWith(), we want to pass the charset of the
   // current document over to a new tab. 
   var wintype = document.documentElement.getAttribute("windowtype");
   var originCharset;
@@ -683,18 +662,15 @@ function openHelpLink(aHelpTopic, aCalle
                       .formatURLPref("app.support.baseURL");
   url += aHelpTopic;
 
   var where = aCalledFromModal ? "window" : "tab";
   openUILinkIn(url, where);
 }
 
 function openPrefsHelp() {
-  var prefs = Components.classes["@mozilla.org/preferences-service;1"]
-                        .getService(Components.interfaces.nsIPrefBranch2);
-
   // non-instant apply prefwindows are usually modal, so we can't open in the topmost window, 
   // since its probably behind the window.
-  var instantApply = prefs.getBoolPref("browser.preferences.instantApply");
+  var instantApply = getBoolPref("browser.preferences.instantApply");
 
   var helpTopic = document.getElementsByTagName("prefwindow")[0].currentPane.helpTopic;
   openHelpLink(helpTopic, !instantApply);
 }
--- a/toolkit/content/Makefile.in
+++ b/toolkit/content/Makefile.in
@@ -74,16 +74,17 @@ ifeq (http,$(patsubst http%,http,$(SOURC
 DEFINES += -DSOURCE_REPO="$(SOURCE_REPO)"
 endif
 
 ifdef ENABLE_TESTS
 DIRS += tests
 endif
 
 EXTRA_JS_MODULES = \
+  Services.jsm \
   InlineSpellChecker.jsm \
   WindowDraggingUtils.jsm \
   $(NULL)
 
 EXTRA_PP_JS_MODULES = \
   debug.js \
   LightweightThemeConsumer.jsm \
   $(NULL)
new file mode 100644
--- /dev/null
+++ b/toolkit/content/Services.jsm
@@ -0,0 +1,74 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Gavin Sharp <gavin@gavinsharp.com> (original author)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+let EXPORTED_SYMBOLS = ["Services"];
+
+const Ci = Components.interfaces;
+const Cc = Components.classes;
+
+Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
+
+let Services = {};
+
+XPCOMUtils.defineLazyGetter(Services, "prefs", function () {
+  return Cc["@mozilla.org/preferences-service;1"]
+           .getService(Ci.nsIPrefService)
+           .QueryInterface(Ci.nsIPrefBranch2);
+});
+
+XPCOMUtils.defineLazyServiceGetter(Services, "wm",
+                                   "@mozilla.org/appshell/window-mediator;1",
+                                   "nsIWindowMediator");
+
+XPCOMUtils.defineLazyServiceGetter(Services, "obs",
+                                   "@mozilla.org/observer-service;1",
+                                   "nsIObserverService");
+
+XPCOMUtils.defineLazyServiceGetter(Services, "pm",
+                                   "@mozilla.org/permissionmanager;1",
+                                   "nsIPermissionManager");
+
+XPCOMUtils.defineLazyServiceGetter(Services, "io",
+                                   "@mozilla.org/network/io-service;1",
+                                   "nsIIOService2");
+
+XPCOMUtils.defineLazyServiceGetter(Services, "prompt",
+                                   "@mozilla.org/embedcomp/prompt-service;1",
+                                   "nsIPromptService");
+
+XPCOMUtils.defineLazyServiceGetter(Services, "search",
+                                   "@mozilla.org/browser/search-service;1",
+                                   "nsIBrowserSearchService");
--- a/toolkit/content/tests/browser/Makefile.in
+++ b/toolkit/content/tests/browser/Makefile.in
@@ -49,12 +49,13 @@ DIRS = \
   $(NULL)
 
 include $(topsrcdir)/config/rules.mk
 
 _BROWSER_TEST_FILES = \
   browser_bug471962.js \
   browser_keyevents_during_autoscrolling.js \
   browser_bug295977_autoscroll_overflow.js \
+  browser_Services.js \
   $(NULL)
 
 libs:: $(_BROWSER_TEST_FILES)
 	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir)
new file mode 100644
--- /dev/null
+++ b/toolkit/content/tests/browser/browser_Services.js
@@ -0,0 +1,52 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Gavin Sharp <gavin@gavinsharp.com> (original author)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+function test() {
+  /** Tests for Services.jsm (Bug 512784) **/
+  ok(Services, "Services object exists");
+  checkServices();
+}
+
+function checkServices() {
+  ok(Services.prefs instanceof Ci.nsIPrefBranch2, "Services.prefs is an nsIPrefBranch2");
+  ok(Services.prefs instanceof Ci.nsIPrefService, "Services.prefs is an nsIPrefService");
+  ok(Services.wm instanceof Ci.nsIWindowMediator, "Services.wm is an nsIWindowMediator");
+  ok(Services.pm instanceof Ci.nsIPermissionManager, "Services.pm is an nsIPermissionManager");
+  ok(Services.io instanceof Ci.nsIIOService, "Services.io is an nsIIOService");
+  ok(Services.io instanceof Ci.nsIIOService2, "Services.io is an nsIIOService2");
+  ok(Services.prompt instanceof Ci.nsIPromptService, "Services.prompt is an nsIPromptService");
+  ok(Services.search instanceof Ci.nsIBrowserSearchService, "Services.search is an nsIBrowserSearchService");
+}