Bug 1436559 - stop doing busy-work in setOverLink and make textToSubURI available on Services.jsm, r?florian draft
authorGijs Kruitbosch <gijskruitbosch@gmail.com>
Wed, 07 Feb 2018 22:07:08 +0000 (2018-02-07)
changeset 753054 2a9e91f38261c33922d8af5e167b0d1a434f1925
parent 752970 d49553765a743ebbd4f08e92a93c9d811ee064c2
push id98471
push usergijskruitbosch@gmail.com
push dateFri, 09 Feb 2018 15:49:51 +0000 (2018-02-09)
reviewersflorian
bugs1436559
milestone60.0a1
Bug 1436559 - stop doing busy-work in setOverLink and make textToSubURI available on Services.jsm, r?florian MozReview-Commit-ID: F63kE4GE67B
browser/base/content/browser.js
browser/base/content/nsContextMenu.js
browser/base/content/tabbrowser.xml
browser/components/search/test/browser_426329.js
toolkit/components/places/UnifiedComplete.js
toolkit/components/search/nsSearchService.js
toolkit/content/contentAreaUtils.js
toolkit/content/tests/chrome/file_autocomplete_with_composition.js
toolkit/content/tests/chrome/test_autocomplete2.xul
toolkit/content/tests/chrome/test_autocomplete3.xul
toolkit/content/tests/chrome/test_autocomplete4.xul
toolkit/content/tests/chrome/test_autocomplete5.xul
toolkit/content/tests/chrome/test_autocomplete_delayOnPaste.xul
toolkit/content/tests/chrome/test_autocomplete_emphasis.xul
toolkit/content/tests/chrome/test_autocomplete_placehold_last_complete.xul
toolkit/content/widgets/autocomplete.xml
toolkit/modules/Finder.jsm
toolkit/modules/Services.jsm
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -4329,27 +4329,27 @@ var XULBrowserWindow = {
   },
 
   setDefaultStatus(status) {
     this.defaultStatus = status;
     this.updateStatusField();
   },
 
   setOverLink(url, anchorElt) {
-    const textToSubURI = Cc["@mozilla.org/intl/texttosuburi;1"].
-                         getService(Ci.nsITextToSubURI);
-    url = textToSubURI.unEscapeURIForUI("UTF-8", url);
-
-    // Encode bidirectional formatting characters.
-    // (RFC 3987 sections 3.2 and 4.1 paragraph 6)
-    url = url.replace(/[\u200e\u200f\u202a\u202b\u202c\u202d\u202e]/g,
-                      encodeURIComponent);
-
-    if (gURLBar && gURLBar._mayTrimURLs /* corresponds to browser.urlbar.trimURLs */)
-      url = trimURL(url);
+    if (url) {
+      url = Services.textToSubURI.unEscapeURIForUI("UTF-8", url);
+
+      // Encode bidirectional formatting characters.
+      // (RFC 3987 sections 3.2 and 4.1 paragraph 6)
+      url = url.replace(/[\u200e\u200f\u202a\u202b\u202c\u202d\u202e]/g,
+                        encodeURIComponent);
+
+      if (gURLBar && gURLBar._mayTrimURLs /* corresponds to browser.urlbar.trimURLs */)
+        url = trimURL(url);
+    }
 
     this.overLink = url;
     LinkTargetDisplay.update();
   },
 
   showTooltip(x, y, tooltip, direction, browser) {
     if (Cc["@mozilla.org/widget/dragservice;1"].getService(Ci.nsIDragService).
         getCurrentSession()) {
--- a/browser/base/content/nsContextMenu.js
+++ b/browser/base/content/nsContextMenu.js
@@ -1270,20 +1270,18 @@ nsContextMenu.prototype = {
     var addresses;
 
     // 7 == length of "mailto:"
     addresses = qmark > 7 ? url.substring(7, qmark) : url.substr(7);
 
     // Let's try to unescape it using a character set
     // in case the address is not ASCII.
     try {
-      const textToSubURI = Cc["@mozilla.org/intl/texttosuburi;1"].
-                           getService(Ci.nsITextToSubURI);
-      addresses = textToSubURI.unEscapeURIForUI(gContextMenuContentData.charSet,
-                                                addresses);
+      addresses = Services.textToSubURI.unEscapeURIForUI(gContextMenuContentData.charSet,
+                                                         addresses);
     } catch (ex) {
       // Do nothing.
     }
 
     var clipboard = Cc["@mozilla.org/widget/clipboardhelper;1"].
                     getService(Ci.nsIClipboardHelper);
     clipboard.copyString(addresses);
   },
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -1641,28 +1641,27 @@
                 }
               }
 
               if (title && !isBlankPageURL(title)) {
                 // At this point, we now have a URI.
                 // Let's try to unescape it using a character set
                 // in case the URI is not ASCII.
                 try {
-                  var characterSet = browser.characterSet;
-                  const textToSubURI = Components.classes["@mozilla.org/intl/texttosuburi;1"]
-                                                 .getService(Components.interfaces.nsITextToSubURI);
-                  title = textToSubURI.unEscapeNonAsciiURI(characterSet, title);
                   // If it's a long data: URI that uses base64 encoding, truncate to
                   // a reasonable length rather than trying to display the entire thing.
                   // We can't shorten arbitrary URIs like this, as bidi etc might mean
                   // we need the trailing characters for display. But a base64-encoded
                   // data-URI is plain ASCII, so this is OK for tab-title display.
                   // (See bug 1408854.)
                   if (title.length > 500 && title.match(/^data:[^,]+;base64,/)) {
                     title = title.substring(0, 500) + "\u2026";
+                  } else {
+                    var characterSet = browser.characterSet;
+                    title = Services.textToSubURI.unEscapeNonAsciiURI(characterSet, title);
                   }
                 } catch (ex) { /* Do nothing. */ }
               } else {
                 // Still no title? Fall back to our untitled string.
                 title = gTabBrowserBundle.GetStringFromName("tabs.emptyTabTitle");
               }
             }
 
--- a/browser/components/search/test/browser_426329.js
+++ b/browser/components/search/test/browser_426329.js
@@ -1,17 +1,15 @@
 /* eslint-disable mozilla/no-arbitrary-setTimeout */
 ChromeUtils.defineModuleGetter(this, "FormHistory",
   "resource://gre/modules/FormHistory.jsm");
 
 function expectedURL(aSearchTerms) {
   const ENGINE_HTML_BASE = "http://mochi.test:8888/browser/browser/components/search/test/test.html";
-  var textToSubURI = Cc["@mozilla.org/intl/texttosuburi;1"].
-                     getService(Ci.nsITextToSubURI);
-  var searchArg = textToSubURI.ConvertAndEscape("utf-8", aSearchTerms);
+  var searchArg = Services.textToSubURI.ConvertAndEscape("utf-8", aSearchTerms);
   return ENGINE_HTML_BASE + "?test=" + searchArg;
 }
 
 function simulateClick(aEvent, aTarget) {
   var event = document.createEvent("MouseEvent");
   var ctrlKeyArg  = aEvent.ctrlKey || false;
   var altKeyArg   = aEvent.altKey || false;
   var shiftKeyArg = aEvent.shiftKey || false;
--- a/toolkit/components/places/UnifiedComplete.js
+++ b/toolkit/components/places/UnifiedComplete.js
@@ -319,20 +319,16 @@ XPCOMUtils.defineLazyModuleGetters(this,
   OS: "resource://gre/modules/osfile.jsm",
   ExtensionSearchHandler: "resource://gre/modules/ExtensionSearchHandler.jsm",
   PlacesSearchAutocompleteProvider: "resource://gre/modules/PlacesSearchAutocompleteProvider.jsm",
   PlacesRemoteTabsAutocompleteProvider: "resource://gre/modules/PlacesRemoteTabsAutocompleteProvider.jsm",
   BrowserUtils: "resource://gre/modules/BrowserUtils.jsm",
   ProfileAge: "resource://gre/modules/ProfileAge.jsm",
 });
 
-XPCOMUtils.defineLazyServiceGetter(this, "textURIService",
-                                   "@mozilla.org/intl/texttosuburi;1",
-                                   "nsITextToSubURI");
-
 XPCOMUtils.defineLazyPreferenceGetter(this, "syncUsernamePref",
                                       "services.sync.username");
 
 function setTimeout(callback, ms) {
   let timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
   timer.initWithCallback(callback, ms, timer.TYPE_ONE_SHOT);
   return timer;
 }
@@ -795,17 +791,17 @@ function looksLikeUrl(str, ignoreAlphanu
 function Search(searchString, searchParam, autocompleteListener,
                 autocompleteSearch, prohibitSearchSuggestions, previousResult) {
   // We want to store the original string for case sensitive searches.
   this._originalSearchString = searchString;
   this._trimmedOriginalSearchString = searchString.trim();
   let strippedOriginalSearchString =
     stripPrefix(this._trimmedOriginalSearchString.toLowerCase());
   this._searchString =
-    textURIService.unEscapeURIForUI("UTF-8", strippedOriginalSearchString);
+    Services.textToSubURI.unEscapeURIForUI("UTF-8", strippedOriginalSearchString);
 
   // The protocol and the host are lowercased by nsIURI, so it's fine to
   // lowercase the typed prefix, to add it back to the results later.
   this._strippedPrefix = this._trimmedOriginalSearchString.slice(
     0, this._trimmedOriginalSearchString.length - strippedOriginalSearchString.length
   ).toLowerCase();
 
   this._matchBehavior = Prefs.get("matchBehavior");
@@ -1748,17 +1744,17 @@ Search.prototype = {
       return false;
 
     // getFixupURIInfo() escaped the URI, so it may not be pretty.  Embed the
     // escaped URL in the action URI since that URL should be "canonical".  But
     // pass the pretty, unescaped URL as the match comment, since it's likely
     // to be displayed to the user, and in any case the front-end should not
     // rely on it being canonical.
     let escapedURL = uri.displaySpec;
-    let displayURL = textURIService.unEscapeURIForUI("UTF-8", uri.displaySpec);
+    let displayURL = Services.textToSubURI.unEscapeURIForUI("UTF-8", escapedURL);
 
     let value = PlacesUtils.mozActionURI("visiturl", {
       url: escapedURL,
       input: this._originalSearchString,
     });
 
     let match = {
       value,
--- a/toolkit/components/search/nsSearchService.js
+++ b/toolkit/components/search/nsSearchService.js
@@ -17,17 +17,16 @@ XPCOMUtils.defineLazyModuleGetters(this,
   SearchStaticData: "resource://gre/modules/SearchStaticData.jsm",
   setTimeout: "resource://gre/modules/Timer.jsm",
   clearTimeout: "resource://gre/modules/Timer.jsm",
   Lz4: "resource://gre/modules/lz4.js",
   NetUtil: "resource://gre/modules/NetUtil.jsm",
 });
 
 XPCOMUtils.defineLazyServiceGetters(this, {
-  gTextToSubURI: ["@mozilla.org/intl/texttosuburi;1", "nsITextToSubURI"],
   gEnvironment: ["@mozilla.org/process/environment;1", "nsIEnvironment"],
   gChromeReg: ["@mozilla.org/chrome/chrome-registry;1", "nsIChromeRegistry"],
 });
 
 const BinaryInputStream = Components.Constructor(
   "@mozilla.org/binaryinputstream;1",
   "nsIBinaryInputStream", "setInputStream");
 
@@ -2415,20 +2414,20 @@ Engine.prototype = {
     if (!aData) {
       // Return a dummy submission object with our searchForm attribute
       return new Submission(makeURI(this._getSearchFormWithPurpose(aPurpose)));
     }
 
     LOG("getSubmission: In data: \"" + aData + "\"; Purpose: \"" + aPurpose + "\"");
     var data = "";
     try {
-      data = gTextToSubURI.ConvertAndEscape(this.queryCharset, aData);
+      data = Services.textToSubURI.ConvertAndEscape(this.queryCharset, aData);
     } catch (ex) {
       LOG("getSubmission: Falling back to default queryCharset!");
-      data = gTextToSubURI.ConvertAndEscape(DEFAULT_QUERY_CHARSET, aData);
+      data = Services.textToSubURI.ConvertAndEscape(DEFAULT_QUERY_CHARSET, aData);
     }
     LOG("getSubmission: Out data: \"" + data + "\"");
     return url.getSubmission(data, this, aPurpose);
   },
 
   // from nsISearchEngine
   supportsResponseType: function SRCH_ENG_supportsResponseType(type) {
     return (this._getURLOfType(type) != null);
@@ -4436,19 +4435,19 @@ SearchService.prototype = {
         break;
       }
       offset += param.length + 1;
     }
 
     // Decode the terms using the charset defined in the search engine.
     let terms;
     try {
-      terms = gTextToSubURI.UnEscapeAndConvert(
-                                       mapEntry.engine.queryCharset,
-                                       encodedTerms.replace(/\+/g, " "));
+      terms = Services.textToSubURI.UnEscapeAndConvert(
+        mapEntry.engine.queryCharset,
+        encodedTerms.replace(/\+/g, " "));
     } catch (ex) {
       // Decoding errors will cause this match to be ignored.
       LOG("Parameter decoding failed. Charset: " +
           mapEntry.engine.queryCharset);
       return gEmptyParseSubmissionResult;
     }
 
     LOG("Match found. Terms: " + terms);
--- a/toolkit/content/contentAreaUtils.js
+++ b/toolkit/content/contentAreaUtils.js
@@ -1000,19 +1000,17 @@ function getDefaultFileName(aDefaultFile
       return docTitle;
     }
   }
 
   try {
     var url = aURI.QueryInterface(Components.interfaces.nsIURL);
     if (url.fileName != "") {
       // 3) Use the actual file name, if present
-      var textToSubURI = Components.classes["@mozilla.org/intl/texttosuburi;1"]
-                                   .getService(Components.interfaces.nsITextToSubURI);
-      return validateFileName(textToSubURI.unEscapeURIForUI("UTF-8", url.fileName));
+      return validateFileName(Services.textToSubURI.unEscapeURIForUI("UTF-8", url.fileName));
     }
   } catch (e) {
     // This is something like a data: and so forth URI... no filename here.
   }
 
   if (docTitle)
     // 4) Use the document title
     return docTitle;
--- a/toolkit/content/tests/chrome/file_autocomplete_with_composition.js
+++ b/toolkit/content/tests/chrome/file_autocomplete_with_composition.js
@@ -1,11 +1,13 @@
 // nsDoTestsForAutoCompleteWithComposition tests autocomplete with composition.
 // Users must include SimpleTest.js and EventUtils.js.
 
+ChromeUtils.import("resource://gre/modules/Services.jsm");
+
 function waitForCondition(condition, nextTest) {
   var tries = 0;
   var interval = setInterval(function() {
     if (condition() || tries >= 30) {
       moveOn();
     }
     tries++;
   }, 100);
--- a/toolkit/content/tests/chrome/test_autocomplete2.xul
+++ b/toolkit/content/tests/chrome/test_autocomplete2.xul
@@ -12,16 +12,18 @@
 
 <textbox id="autocomplete" type="autocomplete"
          autocompletesearch="simple"
          onsearchcomplete="checkResult();"/>
 
 <script class="testbody" type="application/javascript">
 <![CDATA[
 
+ChromeUtils.import("resource://gre/modules/Services.jsm");
+
 // Set to indicate whether or not we want autoCompleteSimple to return a result
 var returnResult = false;
 
 const ACR = Components.interfaces.nsIAutoCompleteResult;
 
 // This result can't be constructed in-line, because otherwise we leak memory.
 function nsAutoCompleteSimpleResult(aString)
 {
--- a/toolkit/content/tests/chrome/test_autocomplete3.xul
+++ b/toolkit/content/tests/chrome/test_autocomplete3.xul
@@ -12,16 +12,18 @@
 
 <textbox id="autocomplete" type="autocomplete"
          autocompletesearch="simple"
          onsearchcomplete="checkResult();"/>
 
 <script class="testbody" type="application/javascript">
 <![CDATA[
 
+ChromeUtils.import("resource://gre/modules/Services.jsm");
+
 // Set to indicate whether or not we want autoCompleteSimple to return a result
 var returnResult = true;
 
 const ACR = Components.interfaces.nsIAutoCompleteResult;
 
 // This result can't be constructed in-line, because otherwise we leak memory.
 function nsAutoCompleteSimpleResult(aString)
 {
--- a/toolkit/content/tests/chrome/test_autocomplete4.xul
+++ b/toolkit/content/tests/chrome/test_autocomplete4.xul
@@ -15,16 +15,18 @@
          completedefaultindex="true"
 
          onsearchcomplete="searchComplete();"
          autocompletesearch="simple"/>
 
 <script class="testbody" type="application/javascript">
 <![CDATA[
 
+ChromeUtils.import("resource://gre/modules/Services.jsm");
+
 // Set to indicate whether or not we want autoCompleteSimple to return a result
 var returnResult = true;
 
 const ACR = Components.interfaces.nsIAutoCompleteResult;
 
 // This result can't be constructed in-line, because otherwise we leak memory.
 function nsAutoCompleteSimpleResult(aString)
 {
--- a/toolkit/content/tests/chrome/test_autocomplete5.xul
+++ b/toolkit/content/tests/chrome/test_autocomplete5.xul
@@ -15,16 +15,18 @@
          ontextentered="checkTextEntered();"
          ontextreverted="checkTextReverted();"
          onsearchbegin="checkSearchBegin();"
          onsearchcomplete="checkSearchCompleted();"/>
 
 <script class="testbody" type="application/javascript">
 <![CDATA[
 
+ChromeUtils.import("resource://gre/modules/Services.jsm");
+
 const ACR = Components.interfaces.nsIAutoCompleteResult;
 
 // This result can't be constructed in-line, because otherwise we leak memory.
 function nsAutoCompleteSimpleResult(aString)
 {
   this.searchString = aString;
   this.searchResult = ACR.RESULT_SUCCESS;
   this.matchCount = 1;
--- a/toolkit/content/tests/chrome/test_autocomplete_delayOnPaste.xul
+++ b/toolkit/content/tests/chrome/test_autocomplete_delayOnPaste.xul
@@ -19,16 +19,17 @@
          onsearchcomplete="searchComplete();"
          timeout="0"
          autocompletesearch="simple"/>
 
 <script class="testbody" type="application/javascript">
 <![CDATA[
 
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
+ChromeUtils.import("resource://gre/modules/Services.jsm");
 
 function autoCompleteSimpleResult(aString) {
   this.searchString = aString;
   this.searchResult = Components.interfaces.nsIAutoCompleteResult.RESULT_SUCCESS;
   this.matchCount = 1;
   this._param = "Result";
 }
 autoCompleteSimpleResult.prototype = {
--- a/toolkit/content/tests/chrome/test_autocomplete_emphasis.xul
+++ b/toolkit/content/tests/chrome/test_autocomplete_emphasis.xul
@@ -14,16 +14,18 @@
          autocompletesearch="simple"
          onsearchcomplete="checkSearchCompleted();"
          autocompletepopup="richpopup"/>
 <panel id="richpopup" type="autocomplete-richlistbox" noautofocus="true"/>
 
 <script class="testbody" type="application/javascript">
 <![CDATA[
 
+ChromeUtils.import("resource://gre/modules/Services.jsm");
+
 const ACR = Components.interfaces.nsIAutoCompleteResult;
 
 // A global variable to hold the search result for the current search.
 var resultText = "";
 
 // This result can't be constructed in-line, because otherwise we leak memory.
 function nsAutoCompleteSimpleResult(aString)
 {
--- a/toolkit/content/tests/chrome/test_autocomplete_placehold_last_complete.xul
+++ b/toolkit/content/tests/chrome/test_autocomplete_placehold_last_complete.xul
@@ -18,16 +18,17 @@
          completedefaultindex="true"
          timeout="0"
          autocompletesearch="simple"/>
 
 <script class="testbody" type="application/javascript">
 <![CDATA[
 
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
+ChromeUtils.import("resource://gre/modules/Services.jsm");
 
 function autoCompleteSimpleResult(aString, searchId) {
   this.searchString = aString;
   this.searchResult = Components.interfaces.nsIAutoCompleteResult.RESULT_SUCCESS;
   this.matchCount = 1;
   if (aString.startsWith('ret')) {
     this._param = autoCompleteSimpleResult.retireCompletion;
   } else {
--- a/toolkit/content/widgets/autocomplete.xml
+++ b/toolkit/content/widgets/autocomplete.xml
@@ -1768,27 +1768,21 @@
             } else {
               aDescriptionElement.appendChild(document.createTextNode(text));
             }
           }
           ]]>
         </body>
       </method>
 
-      <field name="_textToSubURI">null</field>
       <method name="_unescapeUrl">
         <parameter name="url"/>
         <body>
           <![CDATA[
-          if (!this._textToSubURI) {
-            this._textToSubURI =
-              Components.classes["@mozilla.org/intl/texttosuburi;1"]
-                        .getService(Components.interfaces.nsITextToSubURI);
-          }
-          return this._textToSubURI.unEscapeURIForUI("UTF-8", url);
+          return Services.textToSubURI.unEscapeURIForUI("UTF-8", url);
           ]]>
         </body>
       </method>
 
       <method name="_reuseAcItem">
         <body>
           <![CDATA[
             let action = this._parseActionUrl(this.getAttribute("url"));
--- a/toolkit/modules/Finder.jsm
+++ b/toolkit/modules/Finder.jsm
@@ -7,19 +7,16 @@ this.EXPORTED_SYMBOLS = ["Finder", "GetC
 
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 ChromeUtils.import("resource://gre/modules/Geometry.jsm");
 ChromeUtils.import("resource://gre/modules/Services.jsm");
 
 ChromeUtils.defineModuleGetter(this, "BrowserUtils",
   "resource://gre/modules/BrowserUtils.jsm");
 
-XPCOMUtils.defineLazyServiceGetter(this, "TextToSubURIService",
-                                         "@mozilla.org/intl/texttosuburi;1",
-                                         "nsITextToSubURI");
 XPCOMUtils.defineLazyServiceGetter(this, "Clipboard",
                                          "@mozilla.org/widget/clipboard;1",
                                          "nsIClipboard");
 XPCOMUtils.defineLazyServiceGetter(this, "ClipboardHelper",
                                          "@mozilla.org/widget/clipboardhelper;1",
                                          "nsIClipboardHelper");
 
 const kSelectionMaxLen = 150;
@@ -92,17 +89,17 @@ Finder.prototype = {
     let foundLink = this._fastFind.foundLink;
     let linkURL = null;
     if (foundLink) {
       let docCharset = null;
       let ownerDoc = foundLink.ownerDocument;
       if (ownerDoc)
         docCharset = ownerDoc.characterSet;
 
-      linkURL = TextToSubURIService.unEscapeURIForUI(docCharset, foundLink.href);
+      linkURL = Services.textToSubURI.unEscapeURIForUI(docCharset, foundLink.href);
     }
 
     options.linkURL = linkURL;
     options.rect = this._getResultRect();
     options.searchString = this._searchString;
 
     if (!this.iterator.continueRunning({
       caseSensitive: this._fastFind.caseSensitive,
--- a/toolkit/modules/Services.jsm
+++ b/toolkit/modules/Services.jsm
@@ -87,16 +87,17 @@ var initTable = {
   perms: ["@mozilla.org/permissionmanager;1", "nsIPermissionManager"],
   prompt: ["@mozilla.org/embedcomp/prompt-service;1", "nsIPromptService"],
   scriptloader: ["@mozilla.org/moz/jssubscript-loader;1", "mozIJSSubScriptLoader"],
   scriptSecurityManager: ["@mozilla.org/scriptsecuritymanager;1", "nsIScriptSecurityManager"],
   storage: ["@mozilla.org/storage/service;1", "mozIStorageService"],
   domStorageManager: ["@mozilla.org/dom/localStorage-manager;1", "nsIDOMStorageManager"],
   strings: ["@mozilla.org/intl/stringbundle;1", "nsIStringBundleService"],
   telemetry: ["@mozilla.org/base/telemetry;1", "nsITelemetry"],
+  textToSubURI: ["@mozilla.org/intl/texttosuburi;1", "nsITextToSubURI"],
   tm: ["@mozilla.org/thread-manager;1", "nsIThreadManager"],
   urlFormatter: ["@mozilla.org/toolkit/URLFormatterService;1", "nsIURLFormatter"],
   vc: ["@mozilla.org/xpcom/version-comparator;1", "nsIVersionComparator"],
   wm: ["@mozilla.org/appshell/window-mediator;1", "nsIWindowMediator"],
   ww: ["@mozilla.org/embedcomp/window-watcher;1", "nsIWindowWatcher"],
   startup: ["@mozilla.org/toolkit/app-startup;1", "nsIAppStartup"],
   sysinfo: ["@mozilla.org/system-info;1", "nsIPropertyBag2"],
   clipboard: ["@mozilla.org/widget/clipboard;1", "nsIClipboard"],