Merging cedar with mozilla-central.
authorMounir Lamouri <mounir.lamouri@gmail.com>
Sun, 05 Jun 2011 15:31:36 +0200
changeset 70575 e9a667cf1687a66c220906817e7a8dca249870a1
parent 70574 4bf1b8b34a61d5890f5a20b7eec7ba3cc45df6c6 (current diff)
parent 70564 e4da046972684de715792a5bc2ed59085a33e965 (diff)
child 70576 93911949517ca791ecc1749f199099154bc0a987
child 70683 a2007c44b66987867bace81ea725b8170797b8f2
push id109
push userrnewman@mozilla.com
push dateMon, 06 Jun 2011 19:35:56 +0000
treeherderservices-central@e8f788d0b653 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone7.0a1
Merging cedar with mozilla-central.
browser/themes/pinstripe/browser/Secure-background.gif
dom/interfaces/core/nsIDOM3TypeInfo.idl
dom/interfaces/core/nsIDOMNotation.idl
mobile/chrome/content/bindings/setting.xml
--- a/browser/base/content/aboutDialog.css
+++ b/browser/base/content/aboutDialog.css
@@ -67,38 +67,8 @@
   margin: 0 40px;
 }
 
 #currentChannel {
   margin: 0;
   padding: 0;
   font-weight: bold;
 }
-
-#channelSelector {
-  margin-top: 10px;
-}
-
-#channelSelectorStart {
-  -moz-margin-start: 0;
-}
-
-#channelMenulist {
-  margin: 0;
-}
-
-.channel-description {
-  margin: 10px 0;
-}
-
-#detailsBox,
-#channelSelector,
-.channel-description {
-  -moz-transition: opacity 250ms;
-}
-
-#contentDeck:not([selectedIndex="0"]) > #detailsBox,
-#contentDeck:not([selectedIndex="1"]) > #channelSelector,
-#channelDescriptionDeck:not([selectedIndex="0"]) > #releaseDescription,
-#channelDescriptionDeck:not([selectedIndex="1"]) > #betaDescription,
-#channelDescriptionDeck:not([selectedIndex="2"]) > #auroraDescription {
-  opacity: 0;
-}
--- a/browser/base/content/aboutDialog.js
+++ b/browser/base/content/aboutDialog.js
@@ -83,17 +83,19 @@ function init(aEvent)
     document.getElementById("extra-trademark").hidden = true;
   }
 #endif
 
 #ifdef MOZ_UPDATER
   gAppUpdater = new appUpdater();
 #endif
 
-  gChannelSelector.init();
+  let defaults = Services.prefs.getDefaultBranch("");
+  let channelLabel = document.getElementById("currentChannel");
+  channelLabel.value = defaults.getCharPref("app.update.channel");
 
 #ifdef XP_MACOSX
   // it may not be sized at this point, and we need its width to calculate its position
   window.sizeToContent();
   window.moveTo((screen.availWidth / 2) - (window.outerWidth / 2), screen.availHeight / 5);
 #endif
 }
 
@@ -568,77 +570,8 @@ appUpdater.prototype =
     if (!aIID.equals(Components.interfaces.nsIProgressEventSink) &&
         !aIID.equals(Components.interfaces.nsIRequestObserver) &&
         !aIID.equals(Components.interfaces.nsISupports))
       throw Components.results.NS_ERROR_NO_INTERFACE;
     return this;
   }
 };
 #endif
-
-var gChannelSelector = {
-  validChannels: { release: 1, beta: 1, aurora: 1 },
-  
-  init: function() {
-    try {
-      this.channelValue = Services.prefs.getCharPref("app.update.desiredChannel");
-    } catch (e) {
-      let defaults = Services.prefs.getDefaultBranch("");
-      this.channelValue = defaults.getCharPref("app.update.channel");
-    }
-
-    // Only show channel selector UI on valid update channels.
-    if (this.channelValue in this.validChannels) {
-      document.getElementById("currentChannelText").hidden = false;
-      this.setChannelLabel(this.channelValue);
-      this.setChannelMenuitem(this.channelValue);
-    }
-  },
-
-  selectChannel: function(aSelectedItem) {
-    document.getElementById("channelDescriptionDeck").selectedPanel =
-      document.getElementById(aSelectedItem.value + "Description");
-    document.getElementById("channelMenulist").setAttribute("aria-describedby",
-      aSelectedItem.value + "Description");
-  },
-
-  cancel: function() {
-    this.setChannelMenuitem(this.channelValue);
-    this.hide();
-  },
-
-  apply: function() {
-    this.channelValue = document.getElementById("channelMenulist").selectedItem.value;
-    this.setChannelLabel(this.channelValue);
-
-    // Change app update channel.
-    Services.prefs.setCharPref("app.update.desiredChannel", this.channelValue);
-
-    // Stop any downloads in progress
-    gAppUpdater.aus.pauseDownload();
-    // App updater will look at app.update.desiredChannel for new channel value
-    // and will clear it when the update is complete.
-    gAppUpdater.isChecking = true;
-    gAppUpdater.checker.checkForUpdates(gAppUpdater.updateCheckListener, true);
-
-    this.hide();
-  },
-
-  show: function() {
-    document.getElementById("contentDeck").selectedPanel =
-      document.getElementById("channelSelector");
-  },
-
-  hide: function() {
-    document.getElementById("contentDeck").selectedPanel =
-      document.getElementById("detailsBox");  
-  },
-
-  setChannelLabel: function(aValue) {
-    let channelLabel = document.getElementById("currentChannel");
-    channelLabel.value = document.getElementById(aValue + "Menuitem").label;
-  },
-
-  setChannelMenuitem: function(aValue) {
-    document.getElementById("channelMenulist").selectedItem =
-      document.getElementById(aValue + "Menuitem");
-  }
-}
--- a/browser/base/content/aboutDialog.xul
+++ b/browser/base/content/aboutDialog.xul
@@ -75,95 +75,60 @@
   <vbox id="aboutDialogContainer">
     <hbox id="clientBox">
       <vbox id="leftBox" flex="1"/>
       <vbox id="rightBox" flex="1">
 #expand <label id="version" value="__MOZ_APP_VERSION__"/>
         <label id="distribution" class="text-blurb"/>
         <label id="distributionId" class="text-blurb"/>
 
-        <!-- Make sure the selectedIndex attribute is always set so that the CSS
-             selectors for transitions work -->        
-        <deck id="contentDeck" selectedIndex="0">
-          <vbox id="detailsBox" aria-describedby="communityDesc contributeDesc">
-            <vbox id="updateBox">
+        <vbox id="detailsBox" aria-describedby="communityDesc contributeDesc">
+          <vbox id="updateBox">
 #ifdef MOZ_UPDATER
-              <deck id="updateDeck" orient="vertical">
-                <hbox id="updateButtonBox" align="center">
-                  <button id="updateButton" align="start"
-                          oncommand="gAppUpdater.buttonOnCommand();"/>
-                  <spacer flex="1"/>
-                </hbox>
-                <hbox id="checkingForUpdates" align="center">
-                  <image class="update-throbber"/><label>&update.checkingForUpdates;</label>
-                </hbox>
-                <hbox id="checkingAddonCompat" align="center">
-                  <image class="update-throbber"/><label>&update.checkingAddonCompat;</label>
-                </hbox>
-                <hbox id="downloading" align="center">
-                  <image class="update-throbber"/><label>&update.downloading.start;</label><label id="downloadStatus"/><label>&update.downloading.end;</label>
-                </hbox>
-                <hbox id="downloadFailed" align="center">
-                  <label>&update.failed.start;</label><label id="failedLink" class="text-link">&update.failed.linkText;</label><label>&update.failed.end;</label>
-                </hbox>
-                <hbox id="adminDisabled" align="center">
-                  <label>&update.adminDisabled;</label>
-                </hbox>
-                <hbox id="noUpdatesFound" align="center">
-                  <label>&update.noUpdatesFound;</label>
-                </hbox>
-                <hbox id="manualUpdate" align="center">
-                  <label>&update.manual.start;</label><label id="manualLink" class="text-link"/><label>&update.manual.end;</label>
-                </hbox>
-              </deck>
+            <deck id="updateDeck" orient="vertical">
+              <hbox id="updateButtonBox" align="center">
+                <button id="updateButton" align="start"
+                        oncommand="gAppUpdater.buttonOnCommand();"/>
+                <spacer flex="1"/>
+              </hbox>
+              <hbox id="checkingForUpdates" align="center">
+                <image class="update-throbber"/><label>&update.checkingForUpdates;</label>
+              </hbox>
+              <hbox id="checkingAddonCompat" align="center">
+                <image class="update-throbber"/><label>&update.checkingAddonCompat;</label>
+              </hbox>
+              <hbox id="downloading" align="center">
+                <image class="update-throbber"/><label>&update.downloading.start;</label><label id="downloadStatus"/><label>&update.downloading.end;</label>
+              </hbox>
+              <hbox id="downloadFailed" align="center">
+                <label>&update.failed.start;</label><label id="failedLink" class="text-link">&update.failed.linkText;</label><label>&update.failed.end;</label>
+              </hbox>
+              <hbox id="adminDisabled" align="center">
+                <label>&update.adminDisabled;</label>
+              </hbox>
+              <hbox id="noUpdatesFound" align="center">
+                <label>&update.noUpdatesFound;</label>
+              </hbox>
+              <hbox id="manualUpdate" align="center">
+                <label>&update.manual.start;</label><label id="manualLink" class="text-link"/><label>&update.manual.end;</label>
+              </hbox>
+            </deck>
 #endif
-            </vbox>
-
-            <description class="text-blurb" id="currentChannelText" hidden="true">
-              &channel.description.start;<label id="currentChannel"/>&channel.description.end;<label id="channelChangeLink" class="text-link" onclick="gChannelSelector.show();">&channel.change;</label>
-            </description>
-            <description class="text-blurb" id="communityDesc">
-              &community.start2;<label class="text-link" href="http://www.mozilla.org/">&community.mozillaLink;</label>&community.middle2;<label class="text-link" href="about:credits">&community.creditsLink;</label>&community.end2;
-            </description>
-            <description class="text-blurb" id="contributeDesc">
-              &contribute.start;<label class="text-link" href="http://www.mozilla.org/contribute/">&contribute.getInvolvedLink;</label>&contribute.end;
-            </description>
           </vbox>
 
-          <vbox id="channelSelector">
-            <hbox pack="start" align="center">
-              <label id="channelSelectorStart">&channel.selector.start;</label>
-              <menulist id="channelMenulist" onselect="gChannelSelector.selectChannel(this.selectedItem);" aria-labelledby="channelSelectorStart channelMenulist channelSelectorEnd">
-                <menupopup>
-                  <menuitem id="releaseMenuitem" label="Release" value="release"/>
-                  <menuitem id="betaMenuitem" label="Beta" value="beta"/>
-                  <menuseparator/>
-                  <menuitem id="auroraMenuitem" label="Aurora" value="aurora"/>
-                </menupopup>
-              </menulist>
-              <label id="channelSelectorEnd">&channel.selector.end;</label>
-            </hbox>
-
-            <deck id="channelDescriptionDeck" selectedIndex="0">
-              <description id="releaseDescription" class="channel-description">&channel.release.description;</description>
-              <description id="betaDescription" class="channel-description">&channel.beta.description;</description>
-              <description id="auroraDescription" class="channel-description">&channel.aurora.description;</description>
-            </deck>
-
-            <hbox id="channelSelectorButtons" pack="end">
-#ifdef XP_UNIX
-              <button oncommand="gChannelSelector.cancel();" label="&channel.selector.cancelButton;"/>
-              <button oncommand="gChannelSelector.apply();" label="&channel.selector.applyButton;"/>
-#else
-              <button oncommand="gChannelSelector.apply();" label="&channel.selector.applyButton;"/>
-              <button oncommand="gChannelSelector.cancel();" label="&channel.selector.cancelButton;"/>
-#endif
-            </hbox>
-          </vbox>
-        </deck>
+          <description class="text-blurb" id="currentChannelText">
+            &channel.description.start;<label id="currentChannel"/>&channel.description.end;
+          </description>
+          <description class="text-blurb" id="communityDesc">
+            &community.start2;<label class="text-link" href="http://www.mozilla.org/">&community.mozillaLink;</label>&community.middle2;<label class="text-link" href="about:credits">&community.creditsLink;</label>&community.end2;
+          </description>
+          <description class="text-blurb" id="contributeDesc">
+            &contribute.start;<label class="text-link" href="http://www.mozilla.org/contribute/">&contribute.getInvolvedLink;</label>&contribute.end;
+          </description>
+        </vbox>
       </vbox>
     </hbox>
     <vbox id="bottomBox">
       <hbox pack="center">
         <label class="text-link bottom-link" href="about:license">&bottomLinks.license;</label>
         <label class="text-link bottom-link" href="about:rights">&bottomLinks.rights;</label>
         <label class="text-link bottom-link" href="http://www.mozilla.com/legal/privacy/">&bottomLinks.privacy;</label>
       </hbox>
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -2252,17 +2252,21 @@ function loadURI(uri, referrer, postData
     if (allowThirdPartyFixup) {
       flags = nsIWebNavigation.LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP;
     }
     gBrowser.loadURIWithFlags(uri, flags, referrer, null, postData);
   } catch (e) {
   }
 }
 
-function getShortcutOrURI(aURL, aPostDataRef) {
+function getShortcutOrURI(aURL, aPostDataRef, aMayInheritPrincipal) {
+  // Initialize outparam to false
+  if (aMayInheritPrincipal)
+    aMayInheritPrincipal.value = false;
+
   var shortcutURL = null;
   var keyword = aURL;
   var param = "";
 
   var offset = aURL.indexOf(" ");
   if (offset > 0) {
     keyword = aURL.substr(0, offset);
     param = aURL.substr(offset + 1);
@@ -2324,16 +2328,21 @@ function getShortcutOrURI(aURL, aPostDat
   else if (param) {
     // This keyword doesn't take a parameter, but one was provided. Just return
     // the original URL.
     aPostDataRef.value = null;
 
     return aURL;
   }
 
+  // This URL came from a bookmark, so it's safe to let it inherit the current
+  // document's principal.
+  if (aMayInheritPrincipal)
+    aMayInheritPrincipal.value = true;
+
   return shortcutURL;
 }
 
 function getPostDataStream(aStringData, aKeyword, aEncKeyword, aType) {
   var dataStream = Cc["@mozilla.org/io/string-input-stream;1"].
                    createInstance(Ci.nsIStringInputStream);
   aStringData = aStringData.replace(/%s/g, aEncKeyword).replace(/%S/g, aKeyword);
   dataStream.data = aStringData;
--- a/browser/base/content/tabview/iq.js
+++ b/browser/base/content/tabview/iq.js
@@ -603,17 +603,17 @@ iQClass.prototype = {
       let cStyle = window.getComputedStyle(elem, null);
       for (let prop in css) {
         prop = prop.replace(rupper, "-$1").toLowerCase();
         iQ(elem).css(prop, cStyle.getPropertyValue(prop));
       }
     });
 
     this.css({
-      '-moz-transition-property': 'all', // TODO: just animate the properties we're changing
+      '-moz-transition-property': Object.keys(css).join(", "),
       '-moz-transition-duration': (duration / 1000) + 's',
       '-moz-transition-timing-function': easing
     });
 
     this.css(css);
 
     let self = this;
     setTimeout(function() {
--- a/browser/base/content/tabview/ui.js
+++ b/browser/base/content/tabview/ui.js
@@ -994,17 +994,17 @@ let UI = {
 
     [
 #ifdef XP_UNIX
       "quitApplication",
 #else
       "redo",
 #endif
 #ifdef XP_MACOSX
-      "preferencesCmdMac", "minimizeWindow",
+      "preferencesCmdMac", "minimizeWindow", "hideThisAppCmdMac",
 #endif
       "newNavigator", "newNavigatorTab", "undo", "cut", "copy", "paste", 
       "selectAll", "find"
     ].forEach(function(key) {
       let element = gWindow.document.getElementById("key_" + key);
       keys[key] = element.getAttribute("key").toLocaleLowerCase().charCodeAt(0);
     });
 
@@ -1038,16 +1038,20 @@ let UI = {
         Keys.meta = false;
     });
 
     iQ(window).keypress(function(event) {
       if (event.metaKey)
         Keys.meta = true;
 
       function processBrowserKeys(evt) {
+        // let any keys with alt to pass through
+        if (evt.altKey)
+          return;
+
 #ifdef XP_MACOSX
         if (evt.metaKey) {
 #else
         if (evt.ctrlKey) {
 #endif
           let preventDefault = true;
           if (evt.shiftKey) {
             switch (evt.charCode) {
@@ -1072,16 +1076,17 @@ let UI = {
 #ifdef XP_UNIX
               case self._browserKeys.quitApplication:
 #else
               case self._browserKeys.redo:
 #endif
 #ifdef XP_MACOSX
               case self._browserKeys.preferencesCmdMac:
               case self._browserKeys.minimizeWindow:
+              case self._browserKeys.hideThisAppCmdMac:
 #endif
               case self._browserKeys.newNavigator:
               case self._browserKeys.newNavigatorTab:
               case self._browserKeys.undo:
               case self._browserKeys.cut:
               case self._browserKeys.copy:
               case self._browserKeys.paste:
               case self._browserKeys.selectAll:
--- a/browser/base/content/test/Makefile.in
+++ b/browser/base/content/test/Makefile.in
@@ -168,16 +168,17 @@ endif
                  browser_bug599325.js \
                  browser_bug609700.js \
                  browser_bug616836.js \
                  browser_bug623893.js \
                  browser_bug624734.js \
                  browser_bug647886.js \
                  browser_bug655584.js \
                  browser_findbarClose.js \
+                 browser_keywordBookmarklets.js \
                  browser_contextSearchTabPosition.js \
                  browser_ctrlTab.js \
                  browser_customize_popupNotification.js \
                  browser_disablechrome.js \
                  browser_discovery.js \
                  browser_duplicateIDs.js \
                  browser_gestureSupport.js \
                  browser_getshortcutoruri.js \
--- a/browser/base/content/test/browser_bug581242.js
+++ b/browser/base/content/test/browser_bug581242.js
@@ -39,14 +39,15 @@ function test() {
   let blanktab = gBrowser.addTab();
   gBrowser.selectedTab = blanktab;
   BrowserOpenAddonsMgr();
 
   is(blanktab, gBrowser.selectedTab, "Current tab should be blank tab");
   // Verify that about:addons loads
   waitForExplicitFinish();
   gBrowser.selectedBrowser.addEventListener("load", function() {
+    gBrowser.selectedBrowser.removeEventListener("load", arguments.callee, true);
     let browser = blanktab.linkedBrowser;
     is(browser.currentURI.spec, "about:addons", "about:addons should load into blank tab.");
     gBrowser.removeTab(blanktab);
     finish();
   }, true);
 }
--- a/browser/base/content/test/browser_getshortcutoruri.js
+++ b/browser/base/content/test/browser_getshortcutoruri.js
@@ -6,19 +6,20 @@ function getPostDataString(aIS) {
             createInstance(Ci.nsIScriptableInputStream);
   sis.init(aIS);
   var dataLines = sis.read(aIS.available()).split("\n");
 
   // only want the last line
   return dataLines[dataLines.length-1];
 }
 
-function keywordResult(aURL, aPostData) {
+function keywordResult(aURL, aPostData, aIsUnsafe) {
   this.url = aURL;
   this.postData = aPostData;
+  this.isUnsafe = aIsUnsafe;
 }
 
 function keyWordData() {}
 keyWordData.prototype = {
   init: function(aKeyWord, aURL, aPostData, aSearchWord) {
     this.keyword = aKeyWord;
     this.uri = makeURI(aURL);
     this.postData = aPostData;
@@ -47,64 +48,72 @@ var testData = [
 
   [new bmKeywordData("bmpostget", "http://bmpostget/search1=%s", "search2=%s", "foo3"),
    new keywordResult("http://bmpostget/search1=foo3", "search2=foo3")],
 
   [new bmKeywordData("bmget-nosearch", "http://bmget-nosearch/", null, ""),
    new keywordResult("http://bmget-nosearch/", null)],
 
   [new searchKeywordData("searchget", "http://searchget/?search={searchTerms}", null, "foo4"),
-   new keywordResult("http://searchget/?search=foo4", null)],
+   new keywordResult("http://searchget/?search=foo4", null, true)],
 
   [new searchKeywordData("searchpost", "http://searchpost/", "search={searchTerms}", "foo5"),
-   new keywordResult("http://searchpost/", "search=foo5")],
+   new keywordResult("http://searchpost/", "search=foo5", true)],
 
   [new searchKeywordData("searchpostget", "http://searchpostget/?search1={searchTerms}", "search2={searchTerms}", "foo6"),
-   new keywordResult("http://searchpostget/?search1=foo6", "search2=foo6")],
+   new keywordResult("http://searchpostget/?search1=foo6", "search2=foo6", true)],
 
   // Bookmark keywords that don't take parameters should not be activated if a
   // parameter is passed (bug 420328).
   [new bmKeywordData("bmget-noparam", "http://bmget-noparam/", null, "foo7"),
-   new keywordResult(null, null)],
+   new keywordResult(null, null, true)],
   [new bmKeywordData("bmpost-noparam", "http://bmpost-noparam/", "not_a=param", "foo8"),
-   new keywordResult(null, null)],
+   new keywordResult(null, null, true)],
 
   // Test escaping (%s = escaped, %S = raw)
   // UTF-8 default
   [new bmKeywordData("bmget-escaping", "http://bmget/?esc=%s&raw=%S", null, "fo"),
    new keywordResult("http://bmget/?esc=fo%C3%A9&raw=fo", null)],
   // Explicitly-defined ISO-8859-1
   [new bmKeywordData("bmget-escaping2", "http://bmget/?esc=%s&raw=%S&mozcharset=ISO-8859-1", null, "fo"),
    new keywordResult("http://bmget/?esc=fo%E9&raw=fo", null)],
 
   // Bug 359809: Test escaping +, /, and @
   // UTF-8 default
   [new bmKeywordData("bmget-escaping", "http://bmget/?esc=%s&raw=%S", null, "+/@"),
    new keywordResult("http://bmget/?esc=%2B%2F%40&raw=+/@", null)],
   // Explicitly-defined ISO-8859-1
   [new bmKeywordData("bmget-escaping2", "http://bmget/?esc=%s&raw=%S&mozcharset=ISO-8859-1", null, "+/@"),
    new keywordResult("http://bmget/?esc=%2B%2F%40&raw=+/@", null)],
+
+  // Test using a non-bmKeywordData object, to test the behavior of
+  // getShortcutOrURI for non-keywords (setupKeywords only adds keywords for
+  // bmKeywordData objects)
+  [{keyword: "http://gavinsharp.com"},
+   new keywordResult(null, null, true)]
 ];
 
 function test() {
   setupKeywords();
 
   for each (var item in testData) {
     var [data, result] = item;
 
     var postData = {};
     var query = data.keyword;
     if (data.searchWord)
       query += " " + data.searchWord;
-    var url = getShortcutOrURI(query, postData);
+    var mayInheritPrincipal = {};
+    var url = getShortcutOrURI(query, postData, mayInheritPrincipal);
 
     // null result.url means we should expect the same query we sent in
     var expected = result.url || query;
     is(url, expected, "got correct URL for " + data.keyword);
     is(getPostDataString(postData.value), result.postData, "got correct postData for " + data.keyword);
+    is(mayInheritPrincipal.value, !result.isUnsafe, "got correct mayInheritPrincipal for " + data.keyword);
   }
 
   cleanupKeywords();
 }
 
 var gBMFolder = null;
 var gAddedEngines = [];
 function setupKeywords() {
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/browser_keywordBookmarklets.js
@@ -0,0 +1,38 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+function test() {
+  waitForExplicitFinish();
+
+  let bmFolder = Application.bookmarks.menu.addFolder("keyword-test");
+  let tab = gBrowser.selectedTab = gBrowser.addTab();
+
+  registerCleanupFunction (function () {
+    bmFolder.remove();
+    gBrowser.removeTab(tab);
+  });
+
+  let bm = bmFolder.addBookmark("bookmarklet", makeURI("javascript:1;"));
+  bm.keyword = "bm";
+
+  addPageShowListener(function () {
+    let originalPrincipal = gBrowser.contentPrincipal;
+
+    // Enter bookmarklet keyword in the URL bar
+    gURLBar.value = "bm";
+    gURLBar.focus();
+    EventUtils.synthesizeKey("VK_RETURN", {});
+
+    addPageShowListener(function () {
+      ok(gBrowser.contentPrincipal.equals(originalPrincipal), "javascript bookmarklet should inherit principal");
+      finish();
+    });
+  });
+}
+
+function addPageShowListener(func) {
+  gBrowser.selectedBrowser.addEventListener("pageshow", function loadListener() {
+    gBrowser.selectedBrowser.removeEventListener("pageshow", loadListener, false);
+    func();
+  });
+}
--- a/browser/base/content/urlbarBindings.xml
+++ b/browser/base/content/urlbarBindings.xml
@@ -255,16 +255,17 @@
 
       <method name="handleCommand">
         <parameter name="aTriggeringEvent"/>
         <body><![CDATA[
           if (aTriggeringEvent instanceof MouseEvent && aTriggeringEvent.button == 2)
             return; // Do nothing for right clicks
 
           var url = this.value;
+          var mayInheritPrincipal = false;
           var postData = null;
 
           var action = this._parseActionUrl(url);
           if (action) {
             url = action.param;
             if (this.hasAttribute("actiontype")) {
               if (action.type == "switchtab") {
                 this.handleRevert();
@@ -272,36 +273,39 @@
                 if (switchToTabHavingURI(url) &&
                     isTabEmpty(prevTab))
                   gBrowser.removeTab(prevTab);
               }
               return;
             }
           }
           else {
-            [url, postData] = this._canonizeURL(aTriggeringEvent);
+            [url, postData, mayInheritPrincipal] = this._canonizeURL(aTriggeringEvent);
             if (!url)
               return;
           }
 
           this.value = url;
           gBrowser.userTypedValue = url;
           try {
             addToUrlbarHistory(url);
           } catch (ex) {
             // Things may go wrong when adding url to session history,
             // but don't let that interfere with the loading of the url.
             Cu.reportError(ex);
           }
 
           function loadCurrent() {
+            let flags = Ci.nsIWebNavigation.LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP;
             // Pass LOAD_FLAGS_DISALLOW_INHERIT_OWNER to prevent any loads from
-            // inheriting the currently loaded document's principal.
-            let flags = Ci.nsIWebNavigation.LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP |
-                        Ci.nsIWebNavigation.LOAD_FLAGS_DISALLOW_INHERIT_OWNER;
+            // inheriting the currently loaded document's principal, unless this
+            // URL is marked as safe to inherit (e.g. came from a bookmark
+            // keyword).
+            if (!mayInheritPrincipal)
+              flags |= Ci.nsIWebNavigation.LOAD_FLAGS_DISALLOW_INHERIT_OWNER;
             gBrowser.loadURIWithFlags(url, flags, null, null, postData);
           }
 
           if (aTriggeringEvent instanceof MouseEvent) {
             // We have a mouse event (from the go button), so use the standard
             // UI link behaviors
             let where = whereToOpenLink(aTriggeringEvent, false, false);
             if (where == "current") {
@@ -334,17 +338,17 @@
         ]]></body>
       </method>
 
       <method name="_canonizeURL">
         <parameter name="aTriggeringEvent"/>
         <body><![CDATA[
           var url = this.value;
           if (!url)
-            return ["", null];
+            return ["", null, false];
 
           // Only add the suffix when the URL bar value isn't already "URL-like",
           // and only if we get a keyboard event, to match user expectations.
           if (!/^\s*(www|https?)\b|\/\s*$/i.test(url) &&
               (aTriggeringEvent instanceof KeyEvent)) {
 #ifdef XP_MACOSX
             let accel = aTriggeringEvent.metaKey;
 #else
@@ -397,19 +401,20 @@
               } else
                 url = url + (existingSuffix == -1 ? suffix : "/");
 
               url = "http://www." + url;
             }
           }
 
           var postData = {};
-          url = getShortcutOrURI(url, postData);
+          var mayInheritPrincipal = { value: false };
+          url = getShortcutOrURI(url, postData, mayInheritPrincipal);
 
-          return [url, postData.value];
+          return [url, postData.value, mayInheritPrincipal.value];
         ]]></body>
       </method>
 
       <field name="_contentIsCropped">false</field>
 
       <method name="_initURLTooltip">
         <body><![CDATA[
           if (this.focused || !this._contentIsCropped)
--- a/browser/branding/aurora/content/jar.mn
+++ b/browser/branding/aurora/content/jar.mn
@@ -1,11 +1,10 @@
 browser.jar:
 % content branding %content/branding/ contentaccessible=yes
   content/branding/about.png                     (about.png)
   content/branding/about-background.png          (about-background.png)
   content/branding/about-logo.png                (about-logo.png)
   content/branding/about-wordmark.png            (about-wordmark.png)
   content/branding/icon48.png                    (icon48.png)
   content/branding/icon64.png                    (icon64.png)
-  content/branding/icon128.png                   (../mozicon128.png)
   content/branding/icon16.png                    (../default16.png)
   content/branding/aboutDialog.css               (aboutDialog.css)
--- a/browser/branding/nightly/content/jar.mn
+++ b/browser/branding/nightly/content/jar.mn
@@ -1,11 +1,10 @@
 browser.jar:
 % content branding %content/branding/ contentaccessible=yes
   content/branding/about.png                     (about.png)
   content/branding/about-background.png          (about-background.png)
   content/branding/about-logo.png                (about-logo.png)
   content/branding/about-wordmark.png            (about-wordmark.png)
   content/branding/icon48.png                    (icon48.png)
   content/branding/icon64.png                    (icon64.png)
-  content/branding/icon128.png                   (../mozicon128.png)
   content/branding/icon16.png                    (../default16.png)
   content/branding/aboutDialog.css               (aboutDialog.css)
--- a/browser/branding/official/content/jar.mn
+++ b/browser/branding/official/content/jar.mn
@@ -1,10 +1,9 @@
 browser.jar:
 % content branding %content/branding/ contentaccessible=yes
   content/branding/about.png                     (about.png)
   content/branding/about-logo.png                (about-logo.png)
   content/branding/about-wordmark.png            (about-wordmark.png)
   content/branding/icon48.png                    (icon48.png)
   content/branding/icon64.png                    (icon64.png)
-  content/branding/icon128.png                   (../mozicon128.png)
   content/branding/icon16.png                    (../default16.png)
   content/branding/aboutDialog.css               (aboutDialog.css)
--- a/browser/branding/unofficial/content/jar.mn
+++ b/browser/branding/unofficial/content/jar.mn
@@ -1,11 +1,10 @@
 browser.jar:
 % content branding %content/branding/ contentaccessible=yes
   content/branding/about.png                     (about.png)
   content/branding/about-background.png          (about-background.png)
   content/branding/about-logo.png                (about-logo.png)
   content/branding/about-wordmark.png            (about-wordmark.png)
   content/branding/icon48.png                    (icon48.png)
   content/branding/icon64.png                    (icon64.png)
-  content/branding/icon128.png                   (../mozicon128.png)
   content/branding/icon16.png                    (../default16.png)
   content/branding/aboutDialog.css               (aboutDialog.css)
--- a/browser/components/preferences/aboutPermissions.js
+++ b/browser/components/preferences/aboutPermissions.js
@@ -140,16 +140,25 @@ Site.prototype = {
    *        The permission type string stored in permission manager.
    *        e.g. "cookie", "geo", "indexedDB", "popup", "image"
    * @param aResultObj
    *        An object that stores the permission value set for aType.
    *
    * @return A boolean indicating whether or not a permission is set.
    */
   getPermission: function Site_getPermission(aType, aResultObj) {
+    // Password saving isn't a nsIPermissionManager permission type, so handle
+    // it seperately.
+    if (aType == "password") {
+      aResultObj.value =  this.loginSavingEnabled ?
+                          Ci.nsIPermissionManager.ALLOW_ACTION :
+                          Ci.nsIPermissionManager.DENY_ACTION;
+      return true;
+    }
+
     let permissionValue;
     if (TEST_EXACT_PERM_TYPES.indexOf(aType) == -1) {
       permissionValue = Services.perms.testPermission(this.httpURI, aType);
     } else {
       permissionValue = Services.perms.testExactPermission(this.httpURI, aType);
     }
     aResultObj.value = permissionValue;
 
@@ -162,16 +171,23 @@ Site.prototype = {
    * @param aType
    *        The permission type string stored in permission manager.
    *        e.g. "cookie", "geo", "indexedDB", "popup", "image"
    * @param aPerm
    *        The permission value to set for the permission type. This should
    *        be one of the constants defined in nsIPermissionManager.
    */
   setPermission: function Site_setPermission(aType, aPerm) {
+    // Password saving isn't a nsIPermissionManager permission type, so handle
+    // it seperately.
+    if (aType == "password") {
+      this.loginSavingEnabled = aPerm == Ci.nsIPermissionManager.ALLOW_ACTION;
+      return;
+    }
+
     // Using httpURI is kind of bogus, but the permission manager stores the
     // permission for the host, so the right thing happens in the end.
     Services.perms.add(this.httpURI, aType, aPerm);
   },
 
   /**
    * Clears a user-set permission value for the site given a permission type.
    *
@@ -680,43 +696,34 @@ let AboutPermissions = {
       return;
     }
 
     allowItem.hidden = false;
 
     let permissionMenulist = document.getElementById(aType + "-menulist");
     let permissionValue;    
     if (!this._selectedSite) {
-
       // If there is no selected site, we are updating the default permissions interface.
       permissionValue = PermissionDefaults[aType];
-    } else if (aType == "password") {
-      // Services.logins.getLoginSavingEnabled already looks at the default
-      // permission, so we don't need to.
-      permissionValue = this._selectedSite.loginSavingEnabled ?
-                        PermissionDefaults.ALLOW : PermissionDefaults.DENY;
     } else {
       let result = {};
       permissionValue = this._selectedSite.getPermission(aType, result) ?
                         result.value : PermissionDefaults[aType];
     }
 
     permissionMenulist.selectedItem = document.getElementById(aType + "-" + permissionValue);
   },
 
   onPermissionCommand: function(event) {
     let permissionType = event.currentTarget.getAttribute("type");
     let permissionValue = event.target.value;
 
     if (!this._selectedSite) {
       // If there is no selected site, we are setting the default permission.
       PermissionDefaults[permissionType] = permissionValue;
-    } else if (permissionType == "password") {
-      let isEnabled = permissionValue == PermissionDefaults.ALLOW;
-      this._selectedSite.loginSavingEnabled = isEnabled;
     } else {
       this._selectedSite.setPermission(permissionType, permissionValue);
     }
   },
 
   updateVisitCount: function() {
     this._selectedSite.getVisitCount(function(aCount) {
       let visitForm = AboutPermissions._stringBundle.GetStringFromName("visitCount");
--- a/browser/locales/en-US/chrome/browser/aboutDialog.dtd
+++ b/browser/locales/en-US/chrome/browser/aboutDialog.dtd
@@ -55,26 +55,8 @@
 <!ENTITY update.downloading.start   "Downloading update — ">
 <!ENTITY update.downloading.end     "">
 
 <!-- LOCALIZATION NOTE (channel.description.start,channel.description.end): channel.description.start and
      channel.description.end create one sentence, with the current channel label inserted in between.
      example: You are currently on the _Stable_ update channel. -->
 <!ENTITY channel.description.start  "You are currently on the ">
 <!ENTITY channel.description.end    " update channel. ">
-
-<!ENTITY channel.change             "Change">
-
-<!ENTITY channel.release.description       "Enjoy the tried and tested final release being used by hundreds of millions around the world. Stay in control of your online experience with super speed, easy customization and the latest Web technologies.">
-<!ENTITY channel.beta.description          "Experience cutting edge features with more stability. Provide feedback to help refine and polish what will be in the final release.">
-<!ENTITY channel.aurora.description        "Experience the newest innovations in an unstable environment that's not for the faint of heart. Provide feedback on features and performance to help determine what makes the final release.">
-
-<!-- LOCALIZATION NOTE (channel.selector.start,channel.selector.end): channel.selector.start and
-     channel.selector.end create one sentence, with a channel selection menulist instered in between.
-     This is all in one line, so try to make the localized text short.
-     example: Switch to the [Stable] update channel. -->
-<!ENTITY channel.selector.start          "Switch to the">
-<!ENTITY channel.selector.end            "update channel.">
-
-<!-- LOCALIZATION NOTE (channel.selector.applyButton): This button applies the user's choice to switch
-     to a new update channel and starts the application update process. -->
-<!ENTITY channel.selector.applyButton    "Apply and Update">
-<!ENTITY channel.selector.cancelButton   "Cancel">
--- a/browser/locales/en-US/chrome/browser/aboutRobots.dtd
+++ b/browser/locales/en-US/chrome/browser/aboutRobots.dtd
@@ -18,10 +18,10 @@
 <!-- Book: Hitchhiker's Guide To The Galaxy. What the Sirius Cybernetics Corporation calls robots. -->
 <!ENTITY robots.errorLongDesc3 "Robots are Your Plastic Pal Who's Fun To Be With.">
 <!-- TV: Futurama. Bender's first line is "Bite my shiny metal ass." -->
 <!ENTITY robots.errorLongDesc4 "Robots have shiny metal posteriors which should not be bitten.">
 <!-- TV: Battlestar Galactica (2004 series). From the opening text. -->
 <!ENTITY robots.errorTrailerDescText "And they have a plan.">
 <!-- TV: Battlestar Galactica (2004 series). Common expletive referring to Cylons. -->
 <!ENTITY robots.imgtitle "Frakkin' Toasters">
-<!-- Book: Hitchiker's Guide To The Galaxy. Arthur presses a button and it warns him. -->
+<!-- Book: Hitchhiker's Guide To The Galaxy. Arthur presses a button and it warns him. -->
 <!ENTITY robots.dontpress "Please do not press this button again.">
deleted file mode 100644
index 2f4ed89d0cd7c03c8348342a5bb4a72ca9ac8cda..0000000000000000000000000000000000000000
GIT binary patch
literal 0
Hc$@<O00001
--- a/browser/themes/pinstripe/browser/jar.mn
+++ b/browser/themes/pinstripe/browser/jar.mn
@@ -37,17 +37,16 @@ browser.jar:
   skin/classic/browser/reload-stop-go.png
   skin/classic/browser/searchbar-dropmarker.png
   skin/classic/browser/searchbar.css
   skin/classic/browser/Search.png
   skin/classic/browser/section_collapsed.png
   skin/classic/browser/section_collapsed-rtl.png
   skin/classic/browser/section_expanded.png
   skin/classic/browser/Secure-Glyph-White.png
-  skin/classic/browser/Secure-background.gif
   skin/classic/browser/Toolbar.png
   skin/classic/browser/toolbarbutton-dropmarker.png
   skin/classic/browser/urlbar-arrow.png
   skin/classic/browser/urlbar-popup-blocked.png
   skin/classic/browser/feeds/subscribe.css                  (feeds/subscribe.css)
   skin/classic/browser/feeds/subscribe-ui.css               (feeds/subscribe-ui.css)
   skin/classic/browser/feeds/feedIcon.png                   (feeds/feedIcon.png)
   skin/classic/browser/feeds/feedIcon16.png                 (feeds/feedIcon16.png)
--- a/content/base/src/nsDOMAttribute.cpp
+++ b/content/base/src/nsDOMAttribute.cpp
@@ -550,22 +550,16 @@ nsDOMAttribute::GetIsId(PRBool* aReturn)
     *aReturn = PR_FALSE;
     return NS_OK;
   }
 
   *aReturn = mNodeInfo->Equals(idAtom, kNameSpaceID_None);
   return NS_OK;
 }
 
-NS_IMETHODIMP
-nsDOMAttribute::GetSchemaTypeInfo(nsIDOM3TypeInfo** aReturn)
-{
-  return NS_ERROR_NOT_IMPLEMENTED;
-}
-
 PRBool
 nsDOMAttribute::IsNodeOfType(PRUint32 aFlags) const
 {
     return !(aFlags & ~eATTRIBUTE);
 }
 
 PRUint32
 nsDOMAttribute::GetChildCount() const
--- a/content/events/src/nsEventStateManager.cpp
+++ b/content/events/src/nsEventStateManager.cpp
@@ -858,16 +858,19 @@ nsEventStateManager::Init()
   nsCOMPtr<nsIObserverService> observerService =
     mozilla::services::GetObserverService();
   if (!observerService)
     return NS_ERROR_FAILURE;
 
   observerService->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, PR_TRUE);
 
   if (sESMInstanceCount == 1) {
+    sKeyCausesActivation =
+      Preferences::GetBool("accessibility.accesskeycausesactivation",
+                           sKeyCausesActivation);
     sLeftClickOnly =
       Preferences::GetBool("nglayout.events.dispatchLeftClickOnly",
                            sLeftClickOnly);
     sChromeAccessModifier =
       GetAccessModifierMaskFromPref(nsIDocShellTreeItem::typeChrome);
     sContentAccessModifier =
       GetAccessModifierMaskFromPref(nsIDocShellTreeItem::typeContent);
   }
--- a/content/html/content/src/nsGenericHTMLElement.cpp
+++ b/content/html/content/src/nsGenericHTMLElement.cpp
@@ -17,16 +17,17 @@
  *
  * 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):
  *   Mats Palmgren <mats.palmgren@bredband.net>
+ *   Ms2ger <ms2ger@gmail.com>
  *
  * Alternatively, the contents of this file may be used under the terms of
  * either of 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
@@ -439,37 +440,17 @@ nsGenericHTMLElement::SetLang(const nsAS
 }
 
 static const nsAttrValue::EnumTable kDirTable[] = {
   { "ltr", NS_STYLE_DIRECTION_LTR },
   { "rtl", NS_STYLE_DIRECTION_RTL },
   { 0 }
 };
 
-nsresult
-nsGenericHTMLElement::GetDir(nsAString& aDir)
-{
-  const nsAttrValue* attr = mAttrsAndChildren.GetAttr(nsGkAtoms::dir);
-
-  if (attr && attr->Type() == nsAttrValue::eEnum) {
-    attr->ToString(aDir);
-  }
-  else {
-    aDir.Truncate();
-  }
-
-  return NS_OK;
-}
-
-nsresult
-nsGenericHTMLElement::SetDir(const nsAString& aDir)
-{
-  SetAttr(kNameSpaceID_None, nsGkAtoms::dir, aDir, PR_TRUE);
-  return NS_OK;
-}
+NS_IMPL_ENUM_ATTR_DEFAULT_VALUE(nsGenericHTMLElement, Dir, dir, NULL)
 
 nsresult
 nsGenericHTMLElement::GetClassName(nsAString& aClassName)
 {
   GetAttr(kNameSpaceID_None, nsGkAtoms::_class, aClassName);
   return NS_OK;
 }
 
@@ -2326,17 +2307,17 @@ nsGenericHTMLElement::GetEnumAttr(nsIAto
                                   nsAString& aResult)
 {
   const nsAttrValue* attrVal = mAttrsAndChildren.GetAttr(aAttr);
 
   aResult.Truncate();
 
   if (attrVal && attrVal->Type() == nsAttrValue::eEnum) {
     attrVal->GetEnumString(aResult, PR_TRUE);
-  } else {
+  } else if (aDefault) {
     AppendASCIItoUTF16(nsDependentCString(aDefault), aResult);
   }
 
   return NS_OK;
 }
 
 nsresult
 nsGenericHTMLElement::GetContentEditable(nsAString& aContentEditable)
--- a/content/html/content/src/nsGenericHTMLElement.h
+++ b/content/html/content/src/nsGenericHTMLElement.h
@@ -111,18 +111,18 @@ public:
   // methods, implementations are expected to forward calls to these
   // methods.
   nsresult GetId(nsAString& aId);
   nsresult SetId(const nsAString& aId);
   nsresult GetTitle(nsAString& aTitle);
   nsresult SetTitle(const nsAString& aTitle);
   nsresult GetLang(nsAString& aLang);
   nsresult SetLang(const nsAString& aLang);
-  nsresult GetDir(nsAString& aDir);
-  nsresult SetDir(const nsAString& aDir);
+  NS_IMETHOD GetDir(nsAString& aDir);
+  NS_IMETHOD SetDir(const nsAString& aDir);
   nsresult GetClassName(nsAString& aClassName);
   nsresult SetClassName(const nsAString& aClassName);
 
   // nsIDOMNSHTMLElement methods. Note that these are non-virtual
   // methods, implementations are expected to forward calls to these
   // methods.
   nsresult GetOffsetTop(PRInt32* aOffsetTop);
   nsresult GetOffsetLeft(PRInt32* aOffsetLeft);
--- a/content/html/content/src/nsHTMLBodyElement.cpp
+++ b/content/html/content/src/nsHTMLBodyElement.cpp
@@ -16,16 +16,17 @@
  *
  * 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):
  *   Daniel Glazman <glazman@netscape.com>
+ *   Ms2ger <ms2ger@gmail.com>
  *
  * Alternatively, the contents of this file may be used under the terms of
  * either of 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
@@ -309,18 +310,17 @@ NS_INTERFACE_TABLE_HEAD(nsHTMLBodyElemen
   NS_HTML_CONTENT_INTERFACE_TABLE1(nsHTMLBodyElement, nsIDOMHTMLBodyElement)
   NS_HTML_CONTENT_INTERFACE_TABLE_TO_MAP_SEGUE(nsHTMLBodyElement,
                                                nsGenericHTMLElement)
 NS_HTML_CONTENT_INTERFACE_TABLE_TAIL_CLASSINFO(HTMLBodyElement)
 
 NS_IMPL_ELEMENT_CLONE(nsHTMLBodyElement)
 
 
-NS_IMPL_URI_ATTR(nsHTMLBodyElement, Background, background)
-
+NS_IMPL_STRING_ATTR(nsHTMLBodyElement, Background, background)
 NS_IMPL_STRING_ATTR(nsHTMLBodyElement, VLink, vlink)
 NS_IMPL_STRING_ATTR(nsHTMLBodyElement, ALink, alink)
 NS_IMPL_STRING_ATTR(nsHTMLBodyElement, Link, link)
 NS_IMPL_STRING_ATTR(nsHTMLBodyElement, Text, text)
 NS_IMPL_STRING_ATTR(nsHTMLBodyElement, BgColor, bgcolor)
 
 PRBool
 nsHTMLBodyElement::ParseAttribute(PRInt32 aNamespaceID,
--- a/content/html/content/test/Makefile.in
+++ b/content/html/content/test/Makefile.in
@@ -275,13 +275,14 @@ include $(topsrcdir)/config/rules.mk
 		test_bug643051.html \
 		test_bug583514.html \
 		test_bug514437.html \
 		test_bug560112.html \
 		test_bug649134.html \
 		test_bug658746.html \
 		test_bug659596.html \
 		test_bug659743.xml \
+		test_bug660663.html \
 		test_restore_from_parser_fragment.html \
 		$(NULL)
 
 libs:: $(_TEST_FILES)
 	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/tests/$(relativesrcdir)
--- a/content/html/content/test/reflect.js
+++ b/content/html/content/test/reflect.js
@@ -88,8 +88,52 @@ function reflectUnsignedInt(aElement, aA
   is(aElement.getAttribute(aAttr), 0, "@" + aAttr + " should be equals to 0");
   if (aNonNull) {
     is(aElement[aAttr], aDefault,
        "." + aAttr + " should be equals to " + aDefault);
   } else {
     is(aElement[aAttr], 0, "." + aAttr + " should be equals to 0");
   }
 }
+
+/**
+ * @param aElement            Element     node to test on
+ * @param aAttr               String      name of the attribute
+ * @param aSupportedValues    Array       values we supported
+ * @param aUnsupportedValues  Array       values we don't support
+ */
+function reflectLimitedEnumerated(aElement, aAttr, aSupportedValues,
+                                  aUnsupportedValues)
+{
+  aSupportedValues.forEach(function (v) {
+    aElement.setAttribute(aAttr, v);
+    is(aElement[aAttr], v);
+    is(aElement.getAttribute(aAttr), v);
+    aElement.removeAttribute(aAttr);
+
+    aElement.setAttribute(aAttr, v.toUpperCase());
+    is(aElement[aAttr], v);
+    is(aElement.getAttribute(aAttr), v.toUpperCase());
+    aElement.removeAttribute(aAttr);
+
+    aElement[aAttr] = v;
+    is(aElement[aAttr], v);
+    is(aElement.getAttribute(aAttr), v);
+    aElement.removeAttribute(aAttr);
+
+    aElement[aAttr] = v.toUpperCase();
+    is(aElement[aAttr], v);
+    is(aElement.getAttribute(aAttr), v.toUpperCase());
+    aElement.removeAttribute(aAttr);
+  });
+  ["cheesecake"].concat(aUnsupportedValues).forEach(function (v) {
+    aElement.setAttribute(aAttr, v);
+    is(aElement[aAttr], "");
+    is(aElement.getAttribute(aAttr), v);
+    aElement.removeAttribute(aAttr);
+
+    aElement[aAttr] = v;
+    is(aElement[aAttr], "");
+    is(aElement.getAttribute(aAttr), v);
+    aElement.removeAttribute(aAttr);
+  });
+}
+
new file mode 100644
--- /dev/null
+++ b/content/html/content/test/test_bug660663.html
@@ -0,0 +1,29 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=660663
+-->
+<head>
+  <title>Test for Bug 660663</title>
+  <script type="application/javascript" src="/MochiKit/packed.js"></script>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script src="reflect.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=660663">Mozilla Bug 660663</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+  
+</div>
+<pre id="test">
+<script type="application/javascript">
+/** Test for Bug 660663 **/
+reflectLimitedEnumerated(document.createElement("div"),
+                         "dir",
+                         ["ltr", "rtl"],
+                         ["auto"]);
+</script>
+</pre>
+</body>
+</html>
--- a/content/html/document/src/nsHTMLDocument.cpp
+++ b/content/html/document/src/nsHTMLDocument.cpp
@@ -986,17 +986,17 @@ nsHTMLDocument::StopDocumentLoad()
     if (mWriteState == eDocumentOpened) {
       NS_ASSERTION(IsHTML(), "document.open()ed doc is not HTML?");
 
       // Marking the document as closed, since pending scripts will be
       // stopped by nsDocument::StopDocumentLoad() below
       mWriteState = eDocumentClosed;
 
       // Remove the wyciwyg channel request from the document load group
-      // that we added in OpenCommon().
+      // that we added in Open().
       NS_ASSERTION(mWyciwygChannel, "nsHTMLDocument::StopDocumentLoad(): "
                    "Trying to remove nonexistent wyciwyg channel!");
       RemoveWyciwygChannel();
       NS_ASSERTION(!mWyciwygChannel, "nsHTMLDocument::StopDocumentLoad(): "
                    "nsIWyciwygChannel could not be removed!");
     }
     nsDocument::StopDocumentLoad();
     UnblockOnload(PR_FALSE);
@@ -1511,45 +1511,62 @@ nsHTMLDocument::SetCookie(const nsAStrin
 
     NS_LossyConvertUTF16toASCII cookie(aCookie);
     service->SetCookieString(codebaseURI, prompt, cookie.get(), mChannel);
   }
 
   return NS_OK;
 }
 
-nsresult
-nsHTMLDocument::OpenCommon(JSContext* cx, const nsAString& aContentType,
-                           PRBool aReplace)
+NS_IMETHODIMP
+nsHTMLDocument::Open(const nsAString& aContentTypeOrUrl,
+                     const nsAString& aReplaceOrName,
+                     const nsAString& aFeatures,
+                     JSContext* cx, PRUint8 aOptionalArgCount,
+                     nsISupports** aReturn)
 {
+  NS_ASSERTION(nsContentUtils::CanCallerAccess(static_cast<nsIDOMHTMLDocument*>(this)),
+               "XOW should have caught this!");
+
+  // When called with 3 or more arguments, document.open() calls window.open().
+  if (aOptionalArgCount > 2) {
+    nsCOMPtr<nsIDOMWindowInternal> window = GetWindowInternal();
+    if (!window) {
+      return NS_OK;
+    }
+    nsCOMPtr<nsIDOMWindow> newWindow;
+    nsresult rv = window->Open(aContentTypeOrUrl, aReplaceOrName, aFeatures,
+                               getter_AddRefs(newWindow));
+    *aReturn = newWindow.forget().get();
+    return rv;
+  }
+
   if (!IsHTML() || mDisableDocWrite) {
     // No calling document.open() on XHTML
-
     return NS_ERROR_DOM_INVALID_STATE_ERR;
   }
 
-  PRBool loadAsHtml5 = nsHtml5Module::sEnabled;
-
-  nsresult rv = NS_OK;
+  nsCAutoString contentType;
+  contentType.AssignLiteral("text/html");
+  if (aOptionalArgCount > 0) {
+    nsAutoString type;
+    ToLowerCase(aContentTypeOrUrl, type);
+    nsCAutoString actualType, dummy;
+    NS_ParseContentType(NS_ConvertUTF16toUTF8(type), actualType, dummy);
+    if (!actualType.EqualsLiteral("text/html") &&
+        !type.EqualsLiteral("replace")) {
+      contentType.AssignLiteral("text/plain");
+    }
+  }
 
   // If we already have a parser we ignore the document.open call.
   if (mParser) {
-
     return NS_OK;
   }
 
-  NS_ASSERTION(nsContentUtils::CanCallerAccess(static_cast<nsIDOMHTMLDocument*>(this)),
-               "XOW should have caught this!");
-
-  if (!aContentType.EqualsLiteral("text/html") &&
-      !aContentType.EqualsLiteral("text/plain")) {
-    NS_WARNING("Unsupported type; fix the caller");
-    return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
-  }
-
   // check whether we're in the middle of unload.  If so, ignore this call.
   nsCOMPtr<nsIDocShell> shell = do_QueryReferent(mDocumentContainer);
   if (!shell) {
     // We won't be able to create a parser anyway.
     return NS_OK;
   }
 
   PRBool inUnload;
@@ -1597,32 +1614,30 @@ nsHTMLDocument::OpenCommon(JSContext* cx
     nsCAutoString callerSpec;
     nsCAutoString thisSpec;
     if (callerDocURI) {
       callerDocURI->GetSpec(callerSpec);
     }
     if (thisURI) {
       thisURI->GetSpec(thisSpec);
     }
-    printf("nsHTMLDocument::OpenCommon callerDoc %s this %s\n", callerSpec.get(), thisSpec.get());
+    printf("nsHTMLDocument::Open callerDoc %s this %s\n", callerSpec.get(), thisSpec.get());
 #endif
 
     return NS_ERROR_DOM_SECURITY_ERR;
   }
 
   // Stop current loads targeted at the window this document is in.
   if (mScriptGlobalObject) {
     nsCOMPtr<nsIContentViewer> cv;
     shell->GetContentViewer(getter_AddRefs(cv));
 
     if (cv) {
       PRBool okToUnload;
-      rv = cv->PermitUnload(PR_FALSE, &okToUnload);
-
-      if (NS_SUCCEEDED(rv) && !okToUnload) {
+      if (NS_SUCCEEDED(cv->PermitUnload(PR_FALSE, &okToUnload)) && !okToUnload) {
         // We don't want to unload, so stop here, but don't throw an
         // exception.
         return NS_OK;
       }
     }
 
     nsCOMPtr<nsIWebNavigation> webnav(do_QueryInterface(shell));
     webnav->Stop(nsIWebNavigation::STOP_NETWORK);
@@ -1634,17 +1649,17 @@ nsHTMLDocument::OpenCommon(JSContext* cx
     EnsureOnloadBlocker();
   }
 
   // The open occurred after the document finished loading.
   // So we reset the document and create a new one.
   nsCOMPtr<nsIChannel> channel;
   nsCOMPtr<nsILoadGroup> group = do_QueryReferent(mDocumentLoadGroup);
 
-  rv = NS_NewChannel(getter_AddRefs(channel), uri, nsnull, group);
+  nsresult rv = NS_NewChannel(getter_AddRefs(channel), uri, nsnull, group);
 
   if (NS_FAILED(rv)) {
     return rv;
   }
 
   // We can't depend on channels implementing property bags, so do our
   // base URI manually after reset.
 
@@ -1695,25 +1710,26 @@ nsHTMLDocument::OpenCommon(JSContext* cx
   if (baseURI) {
     mDocumentBaseURI = baseURI;
   }
 
   // Store the security info of the caller now that we're done
   // resetting the document.
   mSecurityInfo = securityInfo;
 
+  PRBool loadAsHtml5 = nsHtml5Module::sEnabled;
   if (loadAsHtml5) {
     mParser = nsHtml5Module::NewHtml5Parser();
     rv = NS_OK;
   } else {
     mParser = do_CreateInstance(kCParserCID, &rv);  
   }
 
   // This will be propagated to the parser when someone actually calls write()
-  SetContentTypeInternal(NS_ConvertUTF16toUTF8(aContentType));
+  SetContentTypeInternal(contentType);
 
   mWriteState = eDocumentOpened;
 
   if (NS_SUCCEEDED(rv)) {
     if (loadAsHtml5) {
       nsHtml5Module::Initialize(mParser, this, uri, shell, channel);
     } else {
       nsCOMPtr<nsIHTMLContentSink> sink;
@@ -1734,79 +1750,42 @@ nsHTMLDocument::OpenCommon(JSContext* cx
   // Prepare the docshell and the document viewer for the impending
   // out of band document.write()
   shell->PrepareForNewContentModel();
 
   // Now check whether we were opened with a "replace" argument.  If
   // so, we need to tell the docshell to not create a new history
   // entry for this load. Otherwise, make sure that we're doing a normal load,
   // not whatever type of load was previously done on this docshell.
-  shell->SetLoadType(aReplace ? LOAD_NORMAL_REPLACE : LOAD_NORMAL);
+  shell->SetLoadType(
+    (aOptionalArgCount > 1 && aReplaceOrName.EqualsLiteral("replace"))
+    ? LOAD_NORMAL_REPLACE : LOAD_NORMAL);
 
   nsCOMPtr<nsIContentViewer> cv;
   shell->GetContentViewer(getter_AddRefs(cv));
   nsCOMPtr<nsIDocumentViewer> docViewer = do_QueryInterface(cv);
   if (docViewer) {
     docViewer->LoadStart(static_cast<nsIHTMLDocument *>(this));
   }
 
   // Add a wyciwyg channel request into the document load group
-  NS_ASSERTION(!mWyciwygChannel, "nsHTMLDocument::OpenCommon(): wyciwyg "
+  NS_ASSERTION(!mWyciwygChannel, "nsHTMLDocument::Open(): wyciwyg "
                "channel already exists!");
 
   // In case the editor is listening and will see the new channel
   // being added, make sure mWriteLevel is non-zero so that the editor
   // knows that document.open/write/close() is being called on this
   // document.
   ++mWriteLevel;
 
   CreateAndAddWyciwygChannel();
 
   --mWriteLevel;
 
-  return rv;
-}
-
-NS_IMETHODIMP
-nsHTMLDocument::Open(const nsAString& aContentTypeOrUrl,
-                     const nsAString& aReplaceOrName,
-                     const nsAString& aFeatures,
-                     JSContext* cx, PRUint8 aOptionalArgCount,
-                     nsISupports** aReturn)
-{
-  // When called with 3 or more arguments, document.open() calls window.open().
-  if (aOptionalArgCount > 2) {
-    nsCOMPtr<nsIDOMWindowInternal> window = GetWindowInternal();
-    if (!window) {
-      return NS_OK;
-    }
-    nsCOMPtr<nsIDOMWindow> newWindow;
-    nsresult rv = window->Open(aContentTypeOrUrl, aReplaceOrName, aFeatures,
-                               getter_AddRefs(newWindow));
-    *aReturn = newWindow.forget().get();
-    return rv;
-  }
-
-  nsAutoString contentType;
-  contentType.AssignLiteral("text/html");
-  if (aOptionalArgCount > 0) {
-    nsAutoString type;
-    ToLowerCase(aContentTypeOrUrl, type);
-    nsCAutoString actualType, dummy;
-    NS_ParseContentType(NS_ConvertUTF16toUTF8(type), actualType, dummy);
-    if (!actualType.EqualsLiteral("text/html") &&
-        !type.EqualsLiteral("replace")) {
-      contentType.AssignLiteral("text/plain");
-    }
-  }
-
-  nsresult rv = OpenCommon(cx, contentType,
-    aOptionalArgCount > 1 && aReplaceOrName.EqualsLiteral("replace"));
   NS_ENSURE_SUCCESS(rv, rv);
-
   return CallQueryInterface(this, aReturn);
 }
 
 NS_IMETHODIMP
 nsHTMLDocument::Clear()
 {
   // This method has been deprecated
   return NS_OK;
--- a/content/html/document/src/nsHTMLDocument.h
+++ b/content/html/document/src/nsHTMLDocument.h
@@ -228,18 +228,16 @@ protected:
   static void* UseExistingNameString(nsINode* aRootNode, const nsString* aName);
 
   static void DocumentWriteTerminationFunc(nsISupports *aRef);
 
   void GetDomainURI(nsIURI **uri);
 
   nsresult WriteCommon(JSContext *cx, const nsAString& aText,
                        PRBool aNewlineTerminate);
-  nsresult OpenCommon(JSContext *cx, const nsAString& aContentType,
-                      PRBool aReplace);
 
   nsresult CreateAndAddWyciwygChannel(void);
   nsresult RemoveWyciwygChannel(void);
 
   /**
    * Like IsEditingOn(), but will flush as needed first.
    */
   PRBool IsEditingOnAfterFlush();
--- a/dom/base/nsDOMClassInfo.cpp
+++ b/dom/base/nsDOMClassInfo.cpp
@@ -223,17 +223,16 @@
 #include "nsIDOMDocumentType.h"
 #include "nsIDOMDOMImplementation.h"
 #include "nsIDOMDocumentFragment.h"
 #include "nsDOMAttribute.h"
 #include "nsIDOMText.h"
 #include "nsIDOMComment.h"
 #include "nsIDOMCDATASection.h"
 #include "nsIDOMProcessingInstruction.h"
-#include "nsIDOMNotation.h"
 #include "nsIDOMNSEvent.h"
 #include "nsIDOMDataContainerEvent.h"
 #include "nsIDOMKeyEvent.h"
 #include "nsIDOMMouseEvent.h"
 #include "nsIDOMMouseScrollEvent.h"
 #include "nsIDOMDragEvent.h"
 #include "nsIDOMCommandEvent.h"
 #include "nsIDOMPopupBlockedEvent.h"
@@ -602,18 +601,16 @@ DOMCI_DATA(SmartCardEvent, void)
 DOMCI_DATA(ContentFrameMessageManager, void)
 
 DOMCI_DATA(DOMPrototype, void)
 DOMCI_DATA(DOMConstructor, void)
 
 DOMCI_DATA(Worker, void)
 DOMCI_DATA(ChromeWorker, void)
 
-DOMCI_DATA(Notation, void)
-
 #define NS_DEFINE_CLASSINFO_DATA_WITH_NAME(_class, _name, _helper,            \
                                            _flags)                            \
   { #_name,                                                                   \
     nsnull,                                                                   \
     { _helper::doCreate },                                                    \
     nsnull,                                                                   \
     nsnull,                                                                   \
     nsnull,                                                                   \
@@ -727,17 +724,16 @@ static nsDOMClassInfoData sClassInfoData
                            NODE_SCRIPTABLE_FLAGS)
   NS_DEFINE_CLASSINFO_DATA(Text, nsNodeSH,
                            NODE_SCRIPTABLE_FLAGS)
   NS_DEFINE_CLASSINFO_DATA(Comment, nsNodeSH,
                            NODE_SCRIPTABLE_FLAGS)
   NS_DEFINE_CLASSINFO_DATA(CDATASection, nsNodeSH, NODE_SCRIPTABLE_FLAGS)
   NS_DEFINE_CLASSINFO_DATA(ProcessingInstruction, nsNodeSH,
                            NODE_SCRIPTABLE_FLAGS)
-  NS_DEFINE_CLASSINFO_DATA(Notation, nsNodeSH, NODE_SCRIPTABLE_FLAGS)
   NS_DEFINE_CLASSINFO_DATA(NodeList, nsNodeListSH, ARRAY_SCRIPTABLE_FLAGS)
   NS_DEFINE_CLASSINFO_DATA(NamedNodeMap, nsNamedNodeMapSH,
                            ARRAY_SCRIPTABLE_FLAGS)
 
   // Misc Core related classes
 
   // Event
   NS_DEFINE_CLASSINFO_DATA(Event, nsDOMGenericSH,
@@ -2553,20 +2549,16 @@ nsDOMClassInfo::Init()
 
   DOM_CLASSINFO_MAP_BEGIN(ProcessingInstruction, nsIDOMProcessingInstruction)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMProcessingInstruction)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMNSEventTarget)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMEventTarget)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOM3Node)
   DOM_CLASSINFO_MAP_END
 
-  DOM_CLASSINFO_MAP_BEGIN(Notation, nsIDOMNotation)
-    DOM_CLASSINFO_MAP_ENTRY(nsIDOMNotation)
-  DOM_CLASSINFO_MAP_END
-
   DOM_CLASSINFO_MAP_BEGIN(NodeList, nsIDOMNodeList)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMNodeList)
   DOM_CLASSINFO_MAP_END
 
   DOM_CLASSINFO_MAP_BEGIN(NamedNodeMap, nsIDOMNamedNodeMap)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMNamedNodeMap)
   DOM_CLASSINFO_MAP_END
 
--- a/dom/base/nsDOMClassInfoClasses.h
+++ b/dom/base/nsDOMClassInfoClasses.h
@@ -58,17 +58,16 @@ DOMCI_CLASS(DOMTokenList)
 DOMCI_CLASS(DOMSettableTokenList)
 DOMCI_CLASS(DocumentFragment)
 DOMCI_CLASS(Element)
 DOMCI_CLASS(Attr)
 DOMCI_CLASS(Text)
 DOMCI_CLASS(Comment)
 DOMCI_CLASS(CDATASection)
 DOMCI_CLASS(ProcessingInstruction)
-DOMCI_CLASS(Notation)
 DOMCI_CLASS(NodeList)
 DOMCI_CLASS(NamedNodeMap)
 
 // Event classes
 DOMCI_CLASS(Event)
 DOMCI_CLASS(MutationEvent)
 DOMCI_CLASS(UIEvent)
 DOMCI_CLASS(MouseEvent)
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -185,17 +185,17 @@
 #include "nsIXULAppInfo.h"
 #include "nsNetUtil.h"
 #include "nsFocusManager.h"
 #include "nsIXULWindow.h"
 #include "nsEventStateManager.h"
 #ifdef MOZ_XUL
 #include "nsXULPopupManager.h"
 #include "nsIDOMXULControlElement.h"
-#include "nsIFrame.h"
+#include "nsMenuPopupFrame.h"
 #endif
 
 #include "xpcprivate.h"
 
 #ifdef NS_PRINTING
 #include "nsIPrintSettings.h"
 #include "nsIPrintSettingsService.h"
 #include "nsIWebBrowserPrint.h"
@@ -10209,19 +10209,38 @@ nsGlobalChromeWindow::GetAttentionWithCy
   if (widget) {
     rv = widget->GetAttention(aCycleCount);
   }
 
   return rv;
 }
 
 NS_IMETHODIMP
-nsGlobalChromeWindow::BeginWindowMove(nsIDOMEvent *aMouseDownEvent)
-{
-  nsCOMPtr<nsIWidget> widget = GetMainWidget();
+nsGlobalChromeWindow::BeginWindowMove(nsIDOMEvent *aMouseDownEvent, nsIDOMElement* aPanel)
+{
+  nsCOMPtr<nsIWidget> widget;
+
+  // if a panel was supplied, use its widget instead.
+#ifdef MOZ_XUL
+  if (aPanel) {
+    nsCOMPtr<nsIContent> panel = do_QueryInterface(aPanel);
+    NS_ENSURE_TRUE(panel, NS_ERROR_FAILURE);
+
+    nsIFrame* frame = panel->GetPrimaryFrame();
+    NS_ENSURE_TRUE(frame && frame->GetType() == nsGkAtoms::menuPopupFrame, NS_OK);
+
+    (static_cast<nsMenuPopupFrame*>(frame))->GetWidget(getter_AddRefs(widget));
+  }
+  else {
+#endif
+    widget = GetMainWidget();
+#ifdef MOZ_XUL
+  }
+#endif
+
   if (!widget) {
     return NS_OK;
   }
 
   nsCOMPtr<nsIPrivateDOMEvent> privEvent = do_QueryInterface(aMouseDownEvent);
   NS_ENSURE_TRUE(privEvent, NS_ERROR_FAILURE);
   nsEvent *internalEvent = privEvent->GetInternalNSEvent();
   NS_ENSURE_TRUE(internalEvent &&
--- a/dom/interfaces/base/domstubs.idl
+++ b/dom/interfaces/base/domstubs.idl
@@ -50,17 +50,16 @@ interface nsIDOMDOMImplementation;
 interface nsIDOMDocument;
 interface nsIDOMDocumentFragment;
 interface nsIDOMDocumentType;
 interface nsIDOMElement;
 interface nsIDOMNSElement;
 interface nsIDOMNamedNodeMap;
 interface nsIDOMNode;
 interface nsIDOMNodeList;
-interface nsIDOMNotation;
 interface nsIDOMProcessingInstruction;
 interface nsIDOMText;
 interface nsIDOMDOMStringList;
 interface nsIDOMDOMTokenList;
 interface nsIDOMNameList;
 interface nsIDOMClientRect;
 interface nsIDOMClientRectList;
 
--- a/dom/interfaces/base/nsIDOMChromeWindow.idl
+++ b/dom/interfaces/base/nsIDOMChromeWindow.idl
@@ -38,17 +38,17 @@
 
 #include "domstubs.idl"
 
 interface nsIBrowserDOMWindow;
 interface nsIDOMElement;
 interface nsIDOMEvent;
 interface nsIChromeFrameMessageManager;
 
-[scriptable, uuid(ec38cbaf-372f-4874-bc7a-dbf1f0b3d755)]
+[scriptable, uuid(7cfbc355-cbf9-4408-8e4c-a3c603ff1428)]
 interface nsIDOMChromeWindow : nsISupports
 {
   const unsigned short STATE_MAXIMIZED = 1;
   const unsigned short STATE_MINIMIZED = 2;
   const unsigned short STATE_NORMAL = 3;
   const unsigned short STATE_FULLSCREEN = 4;
 
   readonly attribute unsigned short              windowState;
@@ -79,13 +79,15 @@ interface nsIDOMChromeWindow : nsISuppor
   readonly attribute nsIChromeFrameMessageManager messageManager;
 
   /**
    * On some operating systems, we must allow the window manager to
    * handle window dragging. This function tells the window manager to
    * start dragging the window. This function will fail unless called
    * while the left mouse button is held down, callers must check this.
    *
+   * The optional panel argument should be set when moving a panel.
+   *
    * Returns NS_ERROR_NOT_IMPLEMENTED (and thus throws in JS) if the OS
    * doesn't support this.
    */
-  void beginWindowMove(in nsIDOMEvent mouseDownEvent);
+  void beginWindowMove(in nsIDOMEvent mouseDownEvent, [optional] in nsIDOMElement panel);
 };
--- a/dom/interfaces/core/Makefile.in
+++ b/dom/interfaces/core/Makefile.in
@@ -55,23 +55,21 @@ SDK_XPIDLSRCS =                         
 	nsIDOMDOMImplementation.idl		\
 	nsIDOMDocument.idl			\
 	nsIDOMDocumentFragment.idl		\
 	nsIDOMDocumentType.idl			\
 	nsIDOMElement.idl			\
 	nsIDOMNamedNodeMap.idl			\
 	nsIDOMNode.idl				\
 	nsIDOMNodeList.idl			\
-	nsIDOMNotation.idl			\
 	nsIDOMProcessingInstruction.idl		\
 	nsIDOMText.idl				\
 	$(NULL)
 XPIDLSRCS =                                     \
 	nsIDOM3Node.idl				\
-	nsIDOM3TypeInfo.idl			\
 	nsIDOM3Attr.idl				\
 	nsIDOMDOMStringList.idl			\
 	nsIDOMNameList.idl			\
 	nsIDOMXMLDocument.idl			\
 	nsIDOMUserDataHandler.idl		\
 	nsIDOMNSEditableElement.idl		\
 	nsIDOMNSElement.idl			\
 	nsIDOMNodeSelector.idl			\
--- a/dom/interfaces/core/nsIDOM3Attr.idl
+++ b/dom/interfaces/core/nsIDOM3Attr.idl
@@ -30,18 +30,15 @@
  * 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 ***** */
 
 #include "domstubs.idl"
-#include "nsIDOM3TypeInfo.idl"
 
-[scriptable, uuid(274ef301-065c-450d-a75b-e7673d257293)]
+[scriptable, uuid(dc3ac0ee-9afb-4d1e-a49c-f5042e5bcf65)]
 interface nsIDOM3Attr : nsISupports
 {
     // Introduced in DOM Level 3:
-    readonly attribute nsIDOM3TypeInfo        schemaTypeInfo;
-    // Introduced in DOM Level 3:
     readonly attribute boolean                isId;
 };
deleted file mode 100644
--- a/dom/interfaces/core/nsIDOM3TypeInfo.idl
+++ /dev/null
@@ -1,55 +0,0 @@
-/* ***** 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
- * Alexander J. Vincent <ajvincent@gmail.com>.
- * Portions created by the Initial Developer are Copyright (C) 2006
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either of 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 ***** */
-
-#include "domstubs.idl"
-
-// Introduced in DOM Level 3:
-[scriptable, uuid(2a1088c7-499a-49a7-9d3b-1970d21532ab)]
-interface nsIDOM3TypeInfo : nsISupports
-{
-    readonly attribute DOMString       typeName;
-    readonly attribute DOMString       typeNamespace;
-
-    // DerivationMethods
-    const unsigned long       DERIVATION_RESTRICTION         = 0x00000001;
-    const unsigned long       DERIVATION_EXTENSION           = 0x00000002;
-    const unsigned long       DERIVATION_UNION               = 0x00000004;
-    const unsigned long       DERIVATION_LIST                = 0x00000008;
-
-    boolean            isDerivedFrom(in DOMString typeNamespaceArg, 
-                                     in DOMString typeNameArg, 
-                                     in unsigned long derivationMethod);
-};
deleted file mode 100644
--- a/dom/interfaces/core/nsIDOMNotation.idl
+++ /dev/null
@@ -1,56 +0,0 @@
-/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* ***** 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
- * Netscape Communications Corporation.
- * Portions created by the Initial Developer are Copyright (C) 2000
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *   Vidur Apparao <vidur@netscape.com> (original author)
- *   Johnny Stenback <jst@netscape.com>
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either of 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 ***** */
-
-#include "nsIDOMNode.idl"
-
-/**
- * The nsIDOMNotation interface represents a notation declared in the DTD.
- * A notation  either declares, by name, the format of an unparsed entity, 
- * or is used for formal declaration of processing instruction targets.
- *
- * For more information on this interface please see 
- * http://www.w3.org/TR/DOM-Level-2-Core/
- */
-
-[scriptable, uuid(43658ade-a157-447a-8dcd-aa7fc824ef0a)]
-interface nsIDOMNotation : nsIDOMNode
-{
-  readonly attribute DOMString            publicId;
-  readonly attribute DOMString            systemId;
-};
--- a/dom/plugins/ipc/PluginInstanceParent.cpp
+++ b/dom/plugins/ipc/PluginInstanceParent.cpp
@@ -598,18 +598,22 @@ PluginInstanceParent::GetImage(ImageCont
     if (!mFrontSurface && !mIOSurface)
 #else
     if (!mFrontSurface)
 #endif
         return NS_ERROR_NOT_AVAILABLE;
 
     Image::Format format = Image::CAIRO_SURFACE;
 #ifdef XP_MACOSX
-    if (mIOSurface)
+    if (mIOSurface) {
         format = Image::MAC_IO_SURFACE;
+        if (!aContainer->Manager()) {
+            return NS_ERROR_FAILURE;
+        }
+    }
 #endif
 
     nsRefPtr<Image> image;
     image = aContainer->CreateImage(&format, 1);
     if (!image) {
         return NS_ERROR_FAILURE;
     }
 
--- a/dom/tests/mochitest/dom-level2-html/test_HTMLBodyElement02.html
+++ b/dom/tests/mochitest/dom-level2-html/test_HTMLBodyElement02.html
@@ -90,19 +90,17 @@ function HTMLBodyElement02() {
       if (typeof(this.doc) != 'undefined') {
         docRef = this.doc;
       }
       doc = load(docRef, "doc", "body");
       nodeList = doc.getElementsByTagName("body");
       assertSize("Asize",1,nodeList);
 testNode = nodeList.item(0);
       vbackground = testNode.background;
-      // its not clear if this test is valid
-      //assertEquals("backgroundLink","./pix/back1.gif",vbackground);
-      todo_is(vbackground, "./pix/back1.gif", "backgroundLink");
+      assertEquals("backgroundLink","./pix/back1.gif",vbackground);
        
 }
 
 </script>
 </head>
 <body>
 <h2>Test http://www.w3.org/2001/DOM-Test-Suite/level2/html/HTMLBodyElement02</h2>
 <p>&lt;test name='HTMLBodyElement02' schemaLocation='http://www.w3.org/2001/DOM-Test-Suite/Level-1 dom1.xsd'&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;metadata&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;title&gt;HTMLBodyElement02&lt;/title&gt;
--- a/ipc/chromium/src/chrome/common/ipc_channel.h
+++ b/ipc/chromium/src/chrome/common/ipc_channel.h
@@ -54,16 +54,30 @@ class Channel : public Message::Sender {
   // client mode.  In server mode, the Channel is responsible for setting up the
   // IPC object, whereas in client mode, the Channel merely connects to the
   // already established IPC object.
   // |listener| receives a callback on the current thread for each newly
   // received message.
   //
   Channel(const std::wstring& channel_id, Mode mode, Listener* listener);
 
+#if defined(CHROMIUM_MOZILLA_BUILD)
+  // XXX it would nice not to have yet more platform-specific code in
+  // here but it's just not worth the trouble.
+# if defined(OS_POSIX)
+  // Connect to a pre-created channel |fd| as |mode|.
+  Channel(int fd, Mode mode, Listener* listener);
+# elif defined(OS_WIN)
+  // Connect to a pre-created channel as |mode|.  Clients connect to
+  // the pre-existing server pipe, and servers take over |server_pipe|.
+  Channel(const std::wstring& channel_id, void* server_pipe,
+	  Mode mode, Listener* listener);
+# endif
+#endif
+
   ~Channel();
 
   // Connect the pipe.  On the server side, this will initiate
   // waiting for connections.  On the client, it attempts to
   // connect to a pre-existing pipe.  Note, calling Connect()
   // will not block the calling thread and may complete
   // asynchronously.
   bool Connect();
@@ -94,16 +108,26 @@ class Channel : public Message::Sender {
   // FD # for the client end of the socket and the equivalent FD# to use for
   // mapping it into the Child process.
   // This method may only be called on the server side of a channel.
   //
   // If the kTestingChannelID flag is specified on the command line then
   // a named FIFO is used as the channel transport mechanism rather than a
   // socketpair() in which case this method returns -1 for both parameters.
   void GetClientFileDescriptorMapping(int *src_fd, int *dest_fd) const;
+
+# if defined(CHROMIUM_MOZILLA_BUILD)
+  // Return the server side of the socketpair.
+  int GetServerFileDescriptor() const;
+# endif
+#elif defined(OS_WIN)
+# if defined(CHROMIUM_MOZILLA_BUILD)
+  // Return the server pipe handle.
+  void* GetServerPipeHandle() const;
+# endif
 #endif  // defined(OS_POSIX)
 
  private:
   // PIMPL to which all channel calls are delegated.
   class ChannelImpl;
   ChannelImpl *channel_impl_;
 
   // The Hello message is internal to the Channel class.  It is sent
--- a/ipc/chromium/src/chrome/common/ipc_channel_posix.cc
+++ b/ipc/chromium/src/chrome/common/ipc_channel_posix.cc
@@ -252,36 +252,50 @@ bool SetCloseOnExec(int fd) {
 }
 #endif
 
 }  // namespace
 //------------------------------------------------------------------------------
 
 Channel::ChannelImpl::ChannelImpl(const std::wstring& channel_id, Mode mode,
                                   Listener* listener)
-    : mode_(mode),
-      is_blocked_on_write_(false),
-      message_send_bytes_written_(0),
-      uses_fifo_(CommandLine::ForCurrentProcess()->HasSwitch(
-                     switches::kIPCUseFIFO)),
-      server_listen_pipe_(-1),
-      pipe_(-1),
-      client_pipe_(-1),
-      listener_(listener),
-      waiting_connect_(true),
-      processing_incoming_(false),
-      factory_(this) {
+    : factory_(this) {
+  Init(mode, listener);
+  uses_fifo_ = CommandLine::ForCurrentProcess()->HasSwitch(switches::kIPCUseFIFO);
+
   if (!CreatePipe(channel_id, mode)) {
     // The pipe may have been closed already.
     LOG(WARNING) << "Unable to create pipe named \"" << channel_id <<
                     "\" in " << (mode == MODE_SERVER ? "server" : "client") <<
                     " mode error(" << strerror(errno) << ").";
   }
 }
 
+Channel::ChannelImpl::ChannelImpl(int fd, Mode mode, Listener* listener)
+    : factory_(this) {
+  Init(mode, listener);
+  pipe_ = fd;
+  waiting_connect_ = (MODE_SERVER == mode);
+
+  EnqueueHelloMessage();
+}
+
+void Channel::ChannelImpl::Init(Mode mode, Listener* listener) {
+  mode_ = mode;
+  is_blocked_on_write_ = false;
+  message_send_bytes_written_ = 0;
+  uses_fifo_ = false;
+  server_listen_pipe_ = -1;
+  pipe_ = -1;
+  client_pipe_ = -1;
+  listener_ = listener;
+  waiting_connect_ = true;
+  processing_incoming_ = false;
+}
+
 bool Channel::ChannelImpl::CreatePipe(const std::wstring& channel_id,
                                       Mode mode) {
   DCHECK(server_listen_pipe_ == -1 && pipe_ == -1);
 
   if (uses_fifo_) {
     // This only happens in unit tests; see the comment above PipeMap.
     // TODO(playmobil): We shouldn't need to create fifos on disk.
     // TODO(playmobil): If we do, they should be in the user data directory.
@@ -329,16 +343,20 @@ bool Channel::ChannelImpl::CreatePipe(co
     } else {
       pipe_ = ChannelNameToClientFD(pipe_name_);
       DCHECK(pipe_ > 0);
       waiting_connect_ = false;
     }
   }
 
   // Create the Hello message to be sent when Connect is called
+  return EnqueueHelloMessage();
+}
+
+bool Channel::ChannelImpl::EnqueueHelloMessage() {
   scoped_ptr<Message> msg(new Message(MSG_ROUTING_NONE,
                                       HELLO_MESSAGE_TYPE,
                                       IPC::Message::PRIORITY_NORMAL));
   if (!msg->WriteInt(base::GetCurrentProcId())) {
     Close();
     return false;
   }
 
@@ -791,16 +809,22 @@ void Channel::ChannelImpl::Close() {
 
 //------------------------------------------------------------------------------
 // Channel's methods simply call through to ChannelImpl.
 Channel::Channel(const std::wstring& channel_id, Mode mode,
                  Listener* listener)
     : channel_impl_(new ChannelImpl(channel_id, mode, listener)) {
 }
 
+#if defined(CHROMIUM_MOZILLA_BUILD)
+Channel::Channel(int fd, Mode mode, Listener* listener)
+    : channel_impl_(new ChannelImpl(fd, mode, listener)) {
+}
+#endif
+
 Channel::~Channel() {
   delete channel_impl_;
 }
 
 bool Channel::Connect() {
   return channel_impl_->Connect();
 }
 
@@ -821,9 +845,15 @@ void Channel::set_listener(Listener* lis
 bool Channel::Send(Message* message) {
   return channel_impl_->Send(message);
 }
 
 void Channel::GetClientFileDescriptorMapping(int *src_fd, int *dest_fd) const {
   return channel_impl_->GetClientFileDescriptorMapping(src_fd, dest_fd);
 }
 
+#ifdef CHROMIUM_MOZILLA_BUILD
+int Channel::GetServerFileDescriptor() const {
+  return channel_impl_->GetServerFileDescriptor();
+}
+#endif
+
 }  // namespace IPC
--- a/ipc/chromium/src/chrome/common/ipc_channel_posix.h
+++ b/ipc/chromium/src/chrome/common/ipc_channel_posix.h
@@ -19,33 +19,42 @@
 namespace IPC {
 
 // An implementation of ChannelImpl for POSIX systems that works via
 // socketpairs.  See the .cc file for an overview of the implementation.
 class Channel::ChannelImpl : public MessageLoopForIO::Watcher {
  public:
   // Mirror methods of Channel, see ipc_channel.h for description.
   ChannelImpl(const std::wstring& channel_id, Mode mode, Listener* listener);
+  ChannelImpl(int fd, Mode mode, Listener* listener);
   ~ChannelImpl() { Close(); }
   bool Connect();
   void Close();
 #ifdef CHROMIUM_MOZILLA_BUILD
   Listener* set_listener(Listener* listener) {
     Listener* old = listener_;
     listener_ = listener;
     return old;
   }
 #else
   void set_listener(Listener* listener) { listener_ = listener; }
 #endif
   bool Send(Message* message);
   void GetClientFileDescriptorMapping(int *src_fd, int *dest_fd) const;
+#ifdef CHROMIUM_MOZILLA_BUILD
+  int GetServerFileDescriptor() const {
+    DCHECK(mode_ == MODE_SERVER);
+    return pipe_;
+  }
+#endif
 
  private:
+  void Init(Mode mode, Listener* listener);
   bool CreatePipe(const std::wstring& channel_id, Mode mode);
+  bool EnqueueHelloMessage();
 
   bool ProcessIncomingMessages();
   bool ProcessOutgoingMessages();
 
   // MessageLoopForIO::Watcher implementation.
   virtual void OnFileCanReadWithoutBlocking(int fd);
   virtual void OnFileCanWriteWithoutBlocking(int fd);
 
--- a/ipc/chromium/src/chrome/common/ipc_channel_win.cc
+++ b/ipc/chromium/src/chrome/common/ipc_channel_win.cc
@@ -30,28 +30,57 @@ Channel::ChannelImpl::State::~State() {
 }
 
 //------------------------------------------------------------------------------
 
 Channel::ChannelImpl::ChannelImpl(const std::wstring& channel_id, Mode mode,
                               Listener* listener)
     : ALLOW_THIS_IN_INITIALIZER_LIST(input_state_(this)),
       ALLOW_THIS_IN_INITIALIZER_LIST(output_state_(this)),
-      pipe_(INVALID_HANDLE_VALUE),
-      listener_(listener),
-      waiting_connect_(mode == MODE_SERVER),
-      processing_incoming_(false),
       ALLOW_THIS_IN_INITIALIZER_LIST(factory_(this)) {
+  Init(mode, listener);
+
   if (!CreatePipe(channel_id, mode)) {
     // The pipe may have been closed already.
     LOG(WARNING) << "Unable to create pipe named \"" << channel_id <<
                     "\" in " << (mode == 0 ? "server" : "client") << " mode.";
   }
 }
 
+#if defined(CHROMIUM_MOZILLA_BUILD)
+Channel::ChannelImpl::ChannelImpl(const std::wstring& channel_id,
+                                  HANDLE server_pipe,
+                                  Mode mode, Listener* listener)
+    : ALLOW_THIS_IN_INITIALIZER_LIST(input_state_(this)),
+      ALLOW_THIS_IN_INITIALIZER_LIST(output_state_(this)),
+      ALLOW_THIS_IN_INITIALIZER_LIST(factory_(this)) {
+  Init(mode, listener);
+
+  if (mode == MODE_SERVER) {
+    // Use the existing handle that was dup'd to us
+    pipe_ = server_pipe;
+    EnqueueHelloMessage();
+  } else {
+    // Take the normal init path to connect to the server pipe
+    CreatePipe(channel_id, mode);
+  }
+}
+
+void Channel::ChannelImpl::Init(Mode mode, Listener* listener) {
+  pipe_ = INVALID_HANDLE_VALUE;
+  listener_ = listener;
+  waiting_connect_ = (mode == MODE_SERVER);
+  processing_incoming_ = false;
+}
+
+HANDLE Channel::ChannelImpl::GetServerPipeHandle() const {
+  return pipe_;
+}
+#endif
+
 void Channel::ChannelImpl::Close() {
   if (thread_check_.get()) {
     DCHECK(thread_check_->CalledOnValidThread());
   }
 
   bool waited = false;
   if (input_state_.is_pending || output_state_.is_pending) {
     CancelIo(pipe_);
@@ -159,16 +188,20 @@ bool Channel::ChannelImpl::CreatePipe(co
   }
   if (pipe_ == INVALID_HANDLE_VALUE) {
     // If this process is being closed, the pipe may be gone already.
     LOG(WARNING) << "failed to create pipe: " << GetLastError();
     return false;
   }
 
   // Create the Hello message to be sent when Connect is called
+  return EnqueueHelloMessage();
+}
+
+bool Channel::ChannelImpl::EnqueueHelloMessage() {
   scoped_ptr<Message> m(new Message(MSG_ROUTING_NONE,
                                     HELLO_MESSAGE_TYPE,
                                     IPC::Message::PRIORITY_NORMAL));
   if (!m->WriteInt(GetCurrentProcessId())) {
     CloseHandle(pipe_);
     pipe_ = INVALID_HANDLE_VALUE;
     return false;
   }
@@ -421,29 +454,40 @@ void Channel::ChannelImpl::OnIOCompleted
 
 //------------------------------------------------------------------------------
 // Channel's methods simply call through to ChannelImpl.
 Channel::Channel(const std::wstring& channel_id, Mode mode,
                  Listener* listener)
     : channel_impl_(new ChannelImpl(channel_id, mode, listener)) {
 }
 
+#ifdef CHROMIUM_MOZILLA_BUILD
+Channel::Channel(const std::wstring& channel_id, void* server_pipe,
+                 Mode mode, Listener* listener)
+   : channel_impl_(new ChannelImpl(channel_id, server_pipe, mode, listener)) {
+}
+#endif
+
 Channel::~Channel() {
   delete channel_impl_;
 }
 
 bool Channel::Connect() {
   return channel_impl_->Connect();
 }
 
 void Channel::Close() {
   channel_impl_->Close();
 }
 
 #ifdef CHROMIUM_MOZILLA_BUILD
+void* Channel::GetServerPipeHandle() const {
+  return channel_impl_->GetServerPipeHandle();
+}
+
 Channel::Listener* Channel::set_listener(Listener* listener) {
   return channel_impl_->set_listener(listener);
 }
 #else
 void Channel::set_listener(Listener* listener) {
   channel_impl_->set_listener(listener);
 }
 #endif
--- a/ipc/chromium/src/chrome/common/ipc_channel_win.h
+++ b/ipc/chromium/src/chrome/common/ipc_channel_win.h
@@ -15,36 +15,42 @@
 class NonThreadSafe;
 
 namespace IPC {
 
 class Channel::ChannelImpl : public MessageLoopForIO::IOHandler {
  public:
   // Mirror methods of Channel, see ipc_channel.h for description.
   ChannelImpl(const std::wstring& channel_id, Mode mode, Listener* listener);
+  ChannelImpl(const std::wstring& channel_id, HANDLE server_pipe,
+              Mode mode, Listener* listener);
   ~ChannelImpl() { 
     if (pipe_ != INVALID_HANDLE_VALUE) {
       Close();
     }
   }
   bool Connect();
   void Close();
 #ifdef CHROMIUM_MOZILLA_BUILD
+  HANDLE GetServerPipeHandle() const;
+
   Listener* set_listener(Listener* listener) {
     Listener* old = listener_;
     listener_ = listener;
     return old;
   }
 #else
   void set_listener(Listener* listener) { listener_ = listener; }
 #endif
   bool Send(Message* message);
  private:
+  void Init(Mode mode, Listener* listener);
   const std::wstring PipeName(const std::wstring& channel_id) const;
   bool CreatePipe(const std::wstring& channel_id, Mode mode);
+  bool EnqueueHelloMessage();
 
   bool ProcessConnection();
   bool ProcessIncomingMessages(MessageLoopForIO::IOContext* context,
                                DWORD bytes_read);
   bool ProcessOutgoingMessages(MessageLoopForIO::IOContext* context,
                                DWORD bytes_written);
 
   // MessageLoop::IOHandler implementation.
--- a/ipc/glue/AsyncChannel.cpp
+++ b/ipc/glue/AsyncChannel.cpp
@@ -118,40 +118,46 @@ AsyncChannel::AsyncChannel(AsyncListener
 
 AsyncChannel::~AsyncChannel()
 {
     MOZ_COUNT_DTOR(AsyncChannel);
     Clear();
 }
 
 bool
-AsyncChannel::Open(Transport* aTransport, MessageLoop* aIOLoop)
+AsyncChannel::Open(Transport* aTransport, MessageLoop* aIOLoop, Side aSide)
 {
     NS_PRECONDITION(!mTransport, "Open() called > once");
     NS_PRECONDITION(aTransport, "need transport layer");
 
     // FIXME need to check for valid channel
 
     mTransport = aTransport;
     mExistingListener = mTransport->set_listener(this);
 
     // FIXME figure out whether we're in parent or child, grab IO loop
     // appropriately
     bool needOpen = true;
-    if(!aIOLoop) {
+    if(aIOLoop) {
+        // We're a child or using the new arguments.  Either way, we
+        // need an open.
+        needOpen = true;
+        mChild = (aSide == Unknown) || (aSide == Child);
+    } else {
+        NS_PRECONDITION(aSide == Unknown, "expected default side arg");
+
         // parent
+        mChild = false;
         needOpen = false;
         aIOLoop = XRE_GetIOMessageLoop();
         // FIXME assuming that the parent waits for the OnConnected event.
         // FIXME see GeckoChildProcessHost.cpp.  bad assumption!
         mChannelState = ChannelConnected;
     }
 
-    mChild = needOpen;
-
     mIOLoop = aIOLoop;
     mWorkerLoop = MessageLoop::current();
 
     NS_ASSERTION(mIOLoop, "need an IO loop");
     NS_ASSERTION(mWorkerLoop, "need a worker loop");
 
     if (needOpen) {             // child process
         MonitorAutoLock lock(mMonitor);
@@ -236,16 +242,43 @@ AsyncChannel::Send(Message* msg)
         }
 
         SendThroughTransport(msg);
     }
 
     return true;
 }
 
+bool
+AsyncChannel::Echo(Message* msg)
+{
+    AssertWorkerThread();
+    mMonitor.AssertNotCurrentThreadOwns();
+    NS_ABORT_IF_FALSE(MSG_ROUTING_NONE != msg->routing_id(), "need a route");
+
+    {
+        MonitorAutoLock lock(mMonitor);
+
+        if (!Connected()) {
+            ReportConnectionError("AsyncChannel");
+            return false;
+        }
+
+        // NB: Go through this OnMessageReceived indirection so that
+        // echoing this message does the right thing for SyncChannel
+        // and RPCChannel too
+        mIOLoop->PostTask(
+            FROM_HERE,
+            NewRunnableMethod(this, &AsyncChannel::OnEchoMessage, msg));
+        // OnEchoMessage takes ownership of |msg|
+    }
+
+    return true;
+}
+
 void
 AsyncChannel::OnDispatchMessage(const Message& msg)
 {
     AssertWorkerThread();
     NS_ASSERTION(!msg.is_reply(), "can't process replies here");
     NS_ASSERTION(!(msg.is_sync() || msg.is_rpc()), "async dispatch only");
 
     if (MSG_ROUTING_NONE == msg.routing_id()) {
@@ -460,16 +493,24 @@ AsyncChannel::OnMessageReceived(const Me
     if (!MaybeInterceptSpecialIOMessage(msg))
         // wake up the worker, there's work to do
         mWorkerLoop->PostTask(
             FROM_HERE,
             NewRunnableMethod(this, &AsyncChannel::OnDispatchMessage, msg));
 }
 
 void
+AsyncChannel::OnEchoMessage(Message* msg)
+{
+    AssertIOThread();
+    OnMessageReceived(*msg);
+    delete msg;
+}
+
+void
 AsyncChannel::OnChannelOpened()
 {
     AssertIOThread();
     {
         MonitorAutoLock lock(mMonitor);
         mChannelState = ChannelOpening;
     }
     /*assert*/mTransport->Connect();
--- a/ipc/glue/AsyncChannel.h
+++ b/ipc/glue/AsyncChannel.h
@@ -37,20 +37,19 @@
  *
  * ***** END LICENSE BLOCK ***** */
 
 #ifndef ipc_glue_AsyncChannel_h
 #define ipc_glue_AsyncChannel_h 1
 
 #include "base/basictypes.h"
 #include "base/message_loop.h"
-#include "chrome/common/ipc_channel.h"
 
 #include "mozilla/Monitor.h"
-
+#include "mozilla/ipc/Transport.h"
 
 //-----------------------------------------------------------------------------
 
 namespace mozilla {
 namespace ipc {
 
 struct HasResultCodes
 {
@@ -61,74 +60,80 @@ struct HasResultCodes
         MsgNotAllowed,
         MsgPayloadError,
         MsgProcessingError,
         MsgRouteError,
         MsgValueError,
     };
 };
 
-class AsyncChannel : public IPC::Channel::Listener, protected HasResultCodes
+class AsyncChannel : public Transport::Listener, protected HasResultCodes
 {
 protected:
     typedef mozilla::Monitor Monitor;
 
     enum ChannelState {
         ChannelClosed,
         ChannelOpening,
         ChannelConnected,
         ChannelTimeout,
         ChannelClosing,
         ChannelError
     };
 
 public:
-    typedef IPC::Channel Transport;
     typedef IPC::Message Message;
+    typedef mozilla::ipc::Transport Transport;
 
     class /*NS_INTERFACE_CLASS*/ AsyncListener: protected HasResultCodes
     {
     public:
         virtual ~AsyncListener() { }
 
         virtual void OnChannelClose() = 0;
         virtual void OnChannelError() = 0;
         virtual Result OnMessageReceived(const Message& aMessage) = 0;
         virtual void OnProcessingError(Result aError) = 0;
         virtual void OnChannelConnected(int32 peer_pid) {};
     };
 
+    enum Side { Parent, Child, Unknown };
+
 public:
     //
     // These methods are called on the "worker" thread
     //
     AsyncChannel(AsyncListener* aListener);
     virtual ~AsyncChannel();
 
     // "Open" from the perspective of the transport layer; the underlying
     // socketpair/pipe should already be created.
     //
     // Returns true iff the transport layer was successfully connected,
     // i.e., mChannelState == ChannelConnected.
-    bool Open(Transport* aTransport, MessageLoop* aIOLoop=0);
+    bool Open(Transport* aTransport, MessageLoop* aIOLoop=0, Side aSide=Unknown);
     
     // Close the underlying transport channel.
     void Close();
 
     // Asynchronously send a message to the other side of the channel
     virtual bool Send(Message* msg);
 
+    // Asynchronously deliver a message back to this side of the
+    // channel
+    virtual bool Echo(Message* msg);
+
     // Send OnChannelConnected notification to listeners.
     void DispatchOnChannelConnected(int32 peer_pid);
 
     //
     // These methods are called on the "IO" thread
     //
 
-    // Implement the IPC::Channel::Listener interface
+    // Implement the Transport::Listener interface
     NS_OVERRIDE virtual void OnMessageReceived(const Message& msg);
     NS_OVERRIDE virtual void OnChannelConnected(int32 peer_pid);
     NS_OVERRIDE virtual void OnChannelError();
 
 protected:
     // Can be run on either thread
     void AssertWorkerThread() const
     {
@@ -171,29 +176,30 @@ protected:
 
     virtual void Clear();
 
     // Run on the IO thread
 
     void OnChannelOpened();
     void OnCloseChannel();
     void PostErrorNotifyTask();
+    void OnEchoMessage(Message* msg);
 
     // Return true if |msg| is a special message targeted at the IO
     // thread, in which case it shouldn't be delivered to the worker.
     bool MaybeInterceptSpecialIOMessage(const Message& msg);
     void ProcessGoodbyeMessage();
 
     Transport* mTransport;
     AsyncListener* mListener;
     ChannelState mChannelState;
     Monitor mMonitor;
     MessageLoop* mIOLoop;       // thread where IO happens
     MessageLoop* mWorkerLoop;   // thread where work is done
     bool mChild;                // am I the child or parent?
     CancelableTask* mChannelErrorTask; // NotifyMaybeChannelError runnable
-    IPC::Channel::Listener* mExistingListener; // channel's previous listener
+    Transport::Listener* mExistingListener; // channel's previous listener
 };
 
 
 } // namespace ipc
 } // namespace mozilla
 #endif  // ifndef ipc_glue_AsyncChannel_h
--- a/ipc/glue/GeckoChildProcessHost.cpp
+++ b/ipc/glue/GeckoChildProcessHost.cpp
@@ -79,16 +79,22 @@ using mozilla::ipc::GeckoChildProcessHos
 
 #ifdef ANDROID
 // Like its predecessor in nsExceptionHandler.cpp, this is
 // the magic number of a file descriptor remapping we must
 // preserve for the child process.
 static const int kMagicAndroidSystemPropFd = 5;
 #endif
 
+static bool
+ShouldHaveDirectoryService()
+{
+  return GeckoProcessType_Default == XRE_GetProcessType();
+}
+
 template<>
 struct RunnableMethodTraits<GeckoChildProcessHost>
 {
     static void RetainCallee(GeckoChildProcessHost* obj) { }
     static void ReleaseCallee(GeckoChildProcessHost* obj) { }
 };
 
 GeckoChildProcessHost::GeckoChildProcessHost(GeckoProcessType aProcessType,
@@ -134,25 +140,30 @@ GeckoChildProcessHost::~GeckoChildProces
 
 void GetPathToBinary(FilePath& exePath)
 {
 #if defined(OS_WIN)
   exePath = FilePath::FromWStringHack(CommandLine::ForCurrentProcess()->program());
   exePath = exePath.DirName();
   exePath = exePath.AppendASCII(MOZ_CHILD_PROCESS_NAME);
 #elif defined(OS_POSIX)
-  nsCOMPtr<nsIProperties> directoryService(do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID));
-  nsCOMPtr<nsIFile> greDir;
-  nsresult rv = directoryService->Get(NS_GRE_DIR, NS_GET_IID(nsIFile), getter_AddRefs(greDir));
-  if (NS_SUCCEEDED(rv)) {
-    nsCString path;
-    greDir->GetNativePath(path);
-    exePath = FilePath(path.get());
+  if (ShouldHaveDirectoryService()) {
+    nsCOMPtr<nsIProperties> directoryService(do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID));
+    NS_ASSERTION(directoryService, "Expected XPCOM to be available");
+    if (directoryService) {
+      nsCOMPtr<nsIFile> greDir;
+      nsresult rv = directoryService->Get(NS_GRE_DIR, NS_GET_IID(nsIFile), getter_AddRefs(greDir));
+      if (NS_SUCCEEDED(rv)) {
+        nsCString path;
+        greDir->GetNativePath(path);
+        exePath = FilePath(path.get());
+      }
+    }
   }
-  else {
+  if (exePath.empty()) {
     exePath = FilePath(CommandLine::ForCurrentProcess()->argv()[0]);
     exePath = exePath.DirName();
   }
 
 #ifdef OS_MACOSX
   // We need to use an App Bundle on OS X so that we can hide
   // the dock icon. See Bug 557225
   exePath = exePath.AppendASCII(MOZ_CHILD_PROCESS_BUNDLE);
@@ -416,51 +427,60 @@ GeckoChildProcessHost::PerformAsyncLaunc
   // For POSIX, we have to be extremely anal about *not* using
   // std::wstring in code compiled with Mozilla's -fshort-wchar
   // configuration, because chromium is compiled with -fno-short-wchar
   // and passing wstrings from one config to the other is unsafe.  So
   // we split the logic here.
 
 #if defined(OS_LINUX) || defined(OS_MACOSX)
   base::environment_map newEnvVars;
-  nsCOMPtr<nsIProperties> directoryService(do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID));
-  nsCOMPtr<nsIFile> greDir;
-  nsresult rv = directoryService->Get(NS_GRE_DIR, NS_GET_IID(nsIFile), getter_AddRefs(greDir));
-  if (NS_SUCCEEDED(rv)) {
-    nsCString path;
-    greDir->GetNativePath(path);
-#ifdef OS_LINUX
-#ifdef ANDROID
-    path += "/lib";
-#endif
-    newEnvVars["LD_LIBRARY_PATH"] = path.get();
-#elif OS_MACOSX
-    newEnvVars["DYLD_LIBRARY_PATH"] = path.get();
-    // XXX DYLD_INSERT_LIBRARIES should only be set when launching a plugin
-    //     process, and has no effect on other subprocesses (the hooks in
-    //     libplugin_child_interpose.dylib become noops).  But currently it
-    //     gets set when launching any kind of subprocess.
-    //
-    // Trigger "dyld interposing" for the dylib that contains
-    // plugin_child_interpose.mm.  This allows us to hook OS calls in the
-    // plugin process (ones that don't work correctly in a background
-    // process).  Don't break any other "dyld interposing" that has already
-    // been set up by whatever may have launched the browser.
-    const char* prevInterpose = PR_GetEnv("DYLD_INSERT_LIBRARIES");
-    nsCString interpose;
-    if (prevInterpose) {
-      interpose.Assign(prevInterpose);
-      interpose.AppendLiteral(":");
+  // XPCOM may not be initialized in some subprocesses.  We don't want
+  // to initialize XPCOM just for the directory service, especially
+  // since LD_LIBRARY_PATH is already set correctly in subprocesses
+  // (meaning that we don't need to set that up in the environment).
+  if (ShouldHaveDirectoryService()) {
+    nsCOMPtr<nsIProperties> directoryService(do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID));
+    NS_ASSERTION(directoryService, "Expected XPCOM to be available");
+    if (directoryService) {
+      nsCOMPtr<nsIFile> greDir;
+      nsresult rv = directoryService->Get(NS_GRE_DIR, NS_GET_IID(nsIFile), getter_AddRefs(greDir));
+      if (NS_SUCCEEDED(rv)) {
+        nsCString path;
+        greDir->GetNativePath(path);
+# ifdef OS_LINUX
+#  ifdef ANDROID
+        path += "/lib";
+#  endif  // ANDROID
+        newEnvVars["LD_LIBRARY_PATH"] = path.get();
+# elif OS_MACOSX
+        newEnvVars["DYLD_LIBRARY_PATH"] = path.get();
+        // XXX DYLD_INSERT_LIBRARIES should only be set when launching a plugin
+        //     process, and has no effect on other subprocesses (the hooks in
+        //     libplugin_child_interpose.dylib become noops).  But currently it
+        //     gets set when launching any kind of subprocess.
+        //
+        // Trigger "dyld interposing" for the dylib that contains
+        // plugin_child_interpose.mm.  This allows us to hook OS calls in the
+        // plugin process (ones that don't work correctly in a background
+        // process).  Don't break any other "dyld interposing" that has already
+        // been set up by whatever may have launched the browser.
+        const char* prevInterpose = PR_GetEnv("DYLD_INSERT_LIBRARIES");
+        nsCString interpose;
+        if (prevInterpose) {
+          interpose.Assign(prevInterpose);
+          interpose.AppendLiteral(":");
+        }
+        interpose.Append(path.get());
+        interpose.AppendLiteral("/libplugin_child_interpose.dylib");
+        newEnvVars["DYLD_INSERT_LIBRARIES"] = interpose.get();
+# endif  // OS_LINUX
+      }
     }
-    interpose.Append(path.get());
-    interpose.AppendLiteral("/libplugin_child_interpose.dylib");
-    newEnvVars["DYLD_INSERT_LIBRARIES"] = interpose.get();
-#endif
   }
-#endif
+#endif  // OS_LINUX || OS_MACOSX
 
   FilePath exePath;
   GetPathToBinary(exePath);
 
 #ifdef ANDROID
   // The java wrapper unpacks this for us but can't make it executable
   chmod(exePath.value().c_str(), 0700);
   int cacheCount = 0;
@@ -504,28 +524,30 @@ GeckoChildProcessHost::PerformAsyncLaunc
   // other end of the socketpair() from us
 
   std::vector<std::string> childArgv;
 
   childArgv.push_back(exePath.value());
 
   childArgv.insert(childArgv.end(), aExtraOpts.begin(), aExtraOpts.end());
 
-  // Make sure the child process can find the omnijar
-  // See XRE_InitCommandLine in nsAppRunner.cpp
-  nsCAutoString path;
-  nsCOMPtr<nsIFile> file = mozilla::Omnijar::GetPath(mozilla::Omnijar::GRE);
-  if (file && NS_SUCCEEDED(file->GetNativePath(path))) {
-    childArgv.push_back("-greomni");
-    childArgv.push_back(path.get());
-  }
-  file = mozilla::Omnijar::GetPath(mozilla::Omnijar::APP);
-  if (file && NS_SUCCEEDED(file->GetNativePath(path))) {
-    childArgv.push_back("-appomni");
-    childArgv.push_back(path.get());
+  if (Omnijar::IsInitialized()) {
+    // Make sure that child processes can find the omnijar
+    // See XRE_InitCommandLine in nsAppRunner.cpp
+    nsCAutoString path;
+    nsCOMPtr<nsIFile> file = Omnijar::GetPath(Omnijar::GRE);
+    if (file && NS_SUCCEEDED(file->GetNativePath(path))) {
+      childArgv.push_back("-greomni");
+      childArgv.push_back(path.get());
+    }
+    file = Omnijar::GetPath(Omnijar::APP);
+    if (file && NS_SUCCEEDED(file->GetNativePath(path))) {
+      childArgv.push_back("-appomni");
+      childArgv.push_back(path.get());
+    }
   }
 
   childArgv.push_back(pidstring);
 
 #if defined(MOZ_CRASHREPORTER)
 #  if defined(OS_LINUX)
   int childCrashFd, childCrashRemapFd;
   if (!CrashReporter::CreateNotificationPipeForChild(
@@ -619,28 +641,30 @@ GeckoChildProcessHost::PerformAsyncLaunc
   for (std::vector<std::string>::iterator it = aExtraOpts.begin();
        it != aExtraOpts.end();
        ++it) {
       cmdLine.AppendLooseValue(UTF8ToWide(*it));
   }
 
   cmdLine.AppendLooseValue(std::wstring(mGroupId.get()));
 
-  // Make sure the child process can find the omnijar
-  // See XRE_InitCommandLine in nsAppRunner.cpp
-  nsAutoString path;
-  nsCOMPtr<nsIFile> file = mozilla::Omnijar::GetPath(mozilla::Omnijar::GRE);
-  if (file && NS_SUCCEEDED(file->GetPath(path))) {
-    cmdLine.AppendLooseValue(UTF8ToWide("-greomni"));
-    cmdLine.AppendLooseValue(path.get());
-  }
-  file = mozilla::Omnijar::GetPath(mozilla::Omnijar::APP);
-  if (file && NS_SUCCEEDED(file->GetPath(path))) {
-    cmdLine.AppendLooseValue(UTF8ToWide("-appomni"));
-    cmdLine.AppendLooseValue(path.get());
+  if (Omnijar::IsInitialized()) {
+    // Make sure the child process can find the omnijar
+    // See XRE_InitCommandLine in nsAppRunner.cpp
+    nsAutoString path;
+    nsCOMPtr<nsIFile> file = Omnijar::GetPath(Omnijar::GRE);
+    if (file && NS_SUCCEEDED(file->GetPath(path))) {
+      cmdLine.AppendLooseValue(UTF8ToWide("-greomni"));
+      cmdLine.AppendLooseValue(path.get());
+    }
+    file = Omnijar::GetPath(Omnijar::APP);
+    if (file && NS_SUCCEEDED(file->GetPath(path))) {
+      cmdLine.AppendLooseValue(UTF8ToWide("-appomni"));
+      cmdLine.AppendLooseValue(path.get());
+    }
   }
 
   cmdLine.AppendLooseValue(UTF8ToWide(pidstring));
 
 #if defined(MOZ_CRASHREPORTER)
   cmdLine.AppendLooseValue(
     UTF8ToWide(CrashReporter::GetChildNotificationPipe()));
 #endif
--- a/ipc/glue/Makefile.in
+++ b/ipc/glue/Makefile.in
@@ -63,50 +63,67 @@ EXPORTS_mozilla/ipc = \
   RPCChannel.h \
   SharedMemory.h \
   SharedMemoryBasic.h \
   SharedMemoryBasic_chromium.h \
   SharedMemorySysV.h \
   Shmem.h \
   SyncChannel.h \
   ScopedXREEmbed.h \
+  Transport.h \
   $(NULL)
 
+ifeq ($(OS_ARCH),WINNT) #{
+EXPORTS_mozilla/ipc += \
+  Transport_win.h \
+  $(NULL)
+else
+# POSIX
+EXPORTS_mozilla/ipc += \
+  Transport_posix.h \
+  $(NULL)
+endif #}
+
 ifeq ($(OS_TARGET),Android)
 # Android has its own,
 # almost-but-not-quite-compatible-with-POSIX-or-/dev/shm shared memory
 # impl.
 EXPORTS_mozilla/ipc += SharedMemoryBasic_android.h
 else
 EXPORTS_mozilla/ipc += SharedMemoryBasic_chromium.h
 endif #}
 
 CPPSRCS += \
   AsyncChannel.cpp \
   BrowserProcessSubThread.cpp \
   GeckoChildProcessHost.cpp \
   MessagePump.cpp \
   ProcessChild.cpp \
+  ProtocolUtils.cpp \
   RPCChannel.cpp \
   ScopedXREEmbed.cpp \
   SharedMemory.cpp \
   Shmem.cpp \
   StringUtil.cpp \
   SyncChannel.cpp \
   $(NULL)
 
-ifeq ($(OS_ARCH),WINNT)
+ifeq ($(OS_ARCH),WINNT) #{
 CPPSRCS += \
   SharedMemory_windows.cpp \
+  Transport_win.cpp \
   WindowsMessageLoop.cpp \
   $(NULL)
 else
-# This generic code works on android.
-CPPSRCS += SharedMemory_posix.cpp
-endif
+# POSIX
+CPPSRCS += \
+  SharedMemory_posix.cpp \
+  Transport_posix.cpp \
+  $(NULL)
+endif #}
 
 ifeq ($(OS_TARGET),Android)
 CPPSRCS += SharedMemoryBasic_android.cpp
 endif #}
 
 include $(topsrcdir)/ipc/app/defs.mk
 DEFINES += -DMOZ_CHILD_PROCESS_NAME=\"$(MOZ_CHILD_PROCESS_NAME)\"
 DEFINES += -DMOZ_CHILD_PROCESS_BUNDLE=\"$(MOZ_CHILD_PROCESS_BUNDLE)\"
new file mode 100644
--- /dev/null
+++ b/ipc/glue/ProtocolUtils.cpp
@@ -0,0 +1,161 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=8 et :
+ */
+/* ***** 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 Code.
+ *
+ * The Initial Developer of the Original Code is
+ *   The Mozilla Foundation
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Chris Jones <jones.chris.g@gmail.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 ***** */
+
+#include "base/process_util.h"
+
+#include "mozilla/ipc/AsyncChannel.h"
+#include "mozilla/ipc/ProtocolUtils.h"
+#include "mozilla/ipc/Transport.h"
+
+using namespace base;
+using namespace IPC;
+
+namespace mozilla {
+namespace ipc {
+
+class ChannelOpened : public IPC::Message
+{
+public:
+  ChannelOpened(TransportDescriptor aDescriptor,
+                ProcessId aOtherProcess,
+                ProtocolId aProtocol)
+    : IPC::Message(MSG_ROUTING_CONTROL, // these only go to top-level actors
+                   CHANNEL_OPENED_MESSAGE_TYPE,
+                   PRIORITY_NORMAL)
+  {
+    IPC::WriteParam(this, aDescriptor);
+    IPC::WriteParam(this, aOtherProcess);
+    IPC::WriteParam(this, static_cast<uint32>(aProtocol));
+  }
+
+  static bool Read(const IPC::Message& aMsg,
+                   TransportDescriptor* aDescriptor,
+                   ProcessId* aOtherProcess,
+                   ProtocolId* aProtocol)
+  {
+    void* iter = nsnull;
+    if (!IPC::ReadParam(&aMsg, &iter, aDescriptor) ||
+        !IPC::ReadParam(&aMsg, &iter, aOtherProcess) ||
+        !IPC::ReadParam(&aMsg, &iter, reinterpret_cast<uint32*>(aProtocol))) {
+      return false;
+    }
+    aMsg.EndRead(iter);
+    return true;
+  }
+};
+
+bool
+Bridge(const PrivateIPDLInterface&,
+       AsyncChannel* aParentChannel, ProcessHandle aParentProcess,
+       AsyncChannel* aChildChannel, ProcessHandle aChildProcess,
+       ProtocolId aProtocol)
+{
+  ProcessId parentId = GetProcId(aParentProcess);
+  ProcessId childId = GetProcId(aChildProcess);
+  if (!parentId || !childId) {
+    return false;
+  }
+
+  TransportDescriptor parentSide, childSide;
+  if (!CreateTransport(aParentProcess, aChildProcess,
+                       &parentSide, &childSide)) {
+    return false;
+  }
+
+  if (!aParentChannel->Send(new ChannelOpened(parentSide,
+                                              childId,
+                                              aProtocol)) ||
+      !aChildChannel->Send(new ChannelOpened(childSide,
+                                             parentId,
+                                             aProtocol))) {
+    CloseDescriptor(parentSide);
+    CloseDescriptor(childSide);
+    return false;
+  }
+  return true;
+}
+
+bool
+Open(const PrivateIPDLInterface&,
+     AsyncChannel* aOpenerChannel, ProcessHandle aOtherProcess,
+     Transport::Mode aOpenerMode,
+     ProtocolId aProtocol)
+{
+  bool isParent = (Transport::MODE_SERVER == aOpenerMode);
+  ProcessHandle thisHandle = GetCurrentProcessHandle();
+  ProcessHandle parentHandle = isParent ? thisHandle : aOtherProcess;
+  ProcessHandle childHandle = !isParent ? thisHandle : aOtherProcess;
+  ProcessId parentId = GetProcId(parentHandle);
+  ProcessId childId = GetProcId(childHandle);
+  if (!parentId || !childId) {
+    return false;
+  }
+
+  TransportDescriptor parentSide, childSide;
+  if (!CreateTransport(parentHandle, childHandle,
+                       &parentSide, &childSide)) {
+    return false;
+  }
+
+  Message* parentMsg = new ChannelOpened(parentSide, childId, aProtocol);
+  Message* childMsg = new ChannelOpened(childSide, parentId, aProtocol);
+  nsAutoPtr<Message> messageForUs(isParent ? parentMsg : childMsg);
+  nsAutoPtr<Message> messageForOtherSide(!isParent ? parentMsg : childMsg);
+  if (!aOpenerChannel->Echo(messageForUs.forget()) ||
+      !aOpenerChannel->Send(messageForOtherSide.forget())) {
+    CloseDescriptor(parentSide);
+    CloseDescriptor(childSide);
+    return false;
+  }
+  return true;
+}
+
+bool
+UnpackChannelOpened(const PrivateIPDLInterface&,
+                    const Message& aMsg,
+                    TransportDescriptor* aTransport,
+                    ProcessId* aOtherProcess,
+                    ProtocolId* aProtocol)
+{
+  return ChannelOpened::Read(aMsg, aTransport, aOtherProcess, aProtocol);
+}
+
+} // namespace ipc
+} // namespace mozilla
--- a/ipc/glue/ProtocolUtils.h
+++ b/ipc/glue/ProtocolUtils.h
@@ -41,33 +41,43 @@
 #define mozilla_ipc_ProtocolUtils_h 1
 
 #include "base/process.h"
 #include "base/process_util.h"
 #include "chrome/common/ipc_message_utils.h"
 
 #include "prenv.h"
 
+#include "IPCMessageStart.h"
 #include "mozilla/ipc/Shmem.h"
+#include "mozilla/ipc/Transport.h"
 
 // WARNING: this takes into account the private, special-message-type
 // enum in ipc_channel.h.  They need to be kept in sync.
 namespace {
+// XXX the max message ID is actually kuint32max now ... when this
+// changed, the assumptions of the special message IDs changed in that
+// they're not carving out messages from likely-unallocated space, but
+// rather carving out messages from the end of space allocated to
+// protocol 0.  Oops!  We can get away with this until protocol 0
+// starts approaching its 65,536th message.
 enum {
+    CHANNEL_OPENED_MESSAGE_TYPE = kuint16max - 6,
     SHMEM_DESTROYED_MESSAGE_TYPE = kuint16max - 5,
     UNBLOCK_CHILD_MESSAGE_TYPE = kuint16max - 4,
     BLOCK_CHILD_MESSAGE_TYPE   = kuint16max - 3,
     SHMEM_CREATED_MESSAGE_TYPE = kuint16max - 2,
     GOODBYE_MESSAGE_TYPE       = kuint16max - 1,
 };
 }
 
 namespace mozilla {
 namespace ipc {
 
+class AsyncChannel;
 
 // Used to pass references to protocol actors across the wire.
 // Actors created on the parent-side have a positive ID, and actors
 // allocated on the child side have a negative ID.
 struct ActorHandle
 {
     int mId;
 };
@@ -112,32 +122,52 @@ public:
 
     virtual Shmem::SharedMemory* CreateSharedMemory(
         size_t, SharedMemory::SharedMemoryType, bool, int32*) = 0;
     virtual bool AdoptSharedMemory(Shmem::SharedMemory*, int32*) = 0;
     virtual Shmem::SharedMemory* LookupSharedMemory(int32) = 0;
     virtual bool IsTrackingSharedMemory(Shmem::SharedMemory*) = 0;
     virtual bool DestroySharedMemory(Shmem&) = 0;
 
-    // XXX odd duck, acknowledged
+    // XXX odd ducks, acknowledged
     virtual ProcessHandle OtherProcess() const = 0;
+    virtual AsyncChannel* GetIPCChannel() = 0;
 };
 
 
 inline bool
 LoggingEnabled()
 {
 #if defined(DEBUG)
     return !!PR_GetEnv("MOZ_IPC_MESSAGE_LOG");
 #else
     return false;
 #endif
 }
 
 
+typedef IPCMessageStart ProtocolId;
+
+struct PrivateIPDLInterface {};
+
+bool
+Bridge(const PrivateIPDLInterface&,
+       AsyncChannel*, base::ProcessHandle, AsyncChannel*, base::ProcessHandle,
+       ProtocolId);
+
+bool
+Open(const PrivateIPDLInterface&,
+     AsyncChannel*, base::ProcessHandle, Transport::Mode,
+     ProtocolId);
+
+bool
+UnpackChannelOpened(const PrivateIPDLInterface&,
+                    const IPC::Message&,
+                    TransportDescriptor*, base::ProcessId*, ProtocolId*);
+
 } // namespace ipc
 } // namespace mozilla
 
 
 namespace IPC {
 
 template <>
 struct ParamTraits<mozilla::ipc::ActorHandle>
new file mode 100644
--- /dev/null
+++ b/ipc/glue/Transport.h
@@ -0,0 +1,70 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=8 et :
+ */
+/* ***** 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 Code.
+ *
+ * The Initial Developer of the Original Code is
+ *   The Mozilla Foundation
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Chris Jones <jones.chris.g@gmail.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 ***** */
+
+#ifndef mozilla_ipc_Transport_h
+#define mozilla_ipc_Transport_h 1
+
+#include "base/process_util.h"
+#include "chrome/common/ipc_channel.h"
+
+#ifdef OS_POSIX
+# include "mozilla/ipc/Transport_posix.h"
+#elif OS_WIN
+# include "mozilla/ipc/Transport_win.h"
+#endif
+
+namespace mozilla {
+namespace ipc {
+
+
+typedef IPC::Channel Transport;
+
+bool CreateTransport(base::ProcessHandle aProcOne, base::ProcessHandle aProcTwo,
+                     TransportDescriptor* aOne, TransportDescriptor* aTwo);
+
+Transport* OpenDescriptor(const TransportDescriptor& aTd,
+                          Transport::Mode aMode);
+
+void CloseDescriptor(const TransportDescriptor& aTd);
+
+} // namespace ipc
+} // namespace mozilla
+
+#endif  // mozilla_ipc_Transport_h
new file mode 100644
--- /dev/null
+++ b/ipc/glue/Transport_posix.cpp
@@ -0,0 +1,98 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=8 et :
+ */
+/* ***** 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 Code.
+ *
+ * The Initial Developer of the Original Code is
+ *   The Mozilla Foundation
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Chris Jones <jones.chris.g@gmail.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 ***** */
+
+#include <unistd.h>
+
+#include <string>
+
+#include "chrome/common/child_process_info.h"
+
+#include "mozilla/ipc/Transport.h"
+
+using namespace base;
+using namespace std;
+
+namespace mozilla {
+namespace ipc {
+
+bool
+CreateTransport(ProcessHandle /*unused*/, ProcessHandle /*unused*/,
+                TransportDescriptor* aOne, TransportDescriptor* aTwo)
+{
+  // Gecko doesn't care about this random ID, and the argument to this
+  // function isn't really necessary, it can be just any random
+  // pointer value
+  wstring id = ChildProcessInfo::GenerateRandomChannelID(aOne);
+  // Use MODE_SERVER to force creation of the socketpair
+  Transport t(id, Transport::MODE_SERVER, nsnull);
+  int fd1 = t.GetServerFileDescriptor();
+  int fd2, dontcare;
+  t.GetClientFileDescriptorMapping(&fd2, &dontcare);
+  if (fd1 < 0 || fd2 < 0) {
+    return false;
+  }
+
+  // The Transport closes these fds when it goes out of scope, so we
+  // dup them here
+  fd1 = dup(fd1);
+  fd2 = dup(fd2);
+  if (fd1 < 0 || fd2 < 0) {
+    return false;
+  }
+
+  aOne->mFd = FileDescriptor(fd1, true/*close after sending*/);
+  aTwo->mFd = FileDescriptor(fd2, true/*close after sending*/);
+  return true;
+}
+
+Transport*
+OpenDescriptor(const TransportDescriptor& aTd, Transport::Mode aMode)
+{
+  return new Transport(aTd.mFd.fd, aMode, nsnull);
+}
+
+void
+CloseDescriptor(const TransportDescriptor& aTd)
+{
+  close(aTd.mFd.fd);
+}
+
+} // namespace ipc
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/ipc/glue/Transport_posix.h
@@ -0,0 +1,78 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=8 et :
+ */
+/* ***** 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 Code.
+ *
+ * The Initial Developer of the Original Code is
+ *   The Mozilla Foundation
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Chris Jones <jones.chris.g@gmail.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 ***** */
+
+#ifndef mozilla_ipc_Transport_posix_h
+#define mozilla_ipc_Transport_posix_h 1
+
+#include "IPC/IPCMessageUtils.h"
+
+
+namespace mozilla {
+namespace ipc {
+
+struct TransportDescriptor
+{
+  base::FileDescriptor mFd;
+};
+
+} // namespace ipc
+} // namespace mozilla
+
+
+namespace IPC {
+
+template<>
+struct ParamTraits<mozilla::ipc::TransportDescriptor>
+{
+  typedef mozilla::ipc::TransportDescriptor paramType;
+  static void Write(Message* aMsg, const paramType& aParam)
+  {
+    WriteParam(aMsg, aParam.mFd);
+  }
+  static bool Read(const Message* aMsg, void** aIter, paramType* aResult)
+  {
+    return ReadParam(aMsg, aIter, &aResult->mFd);
+  }
+};
+
+} // namespace IPC
+
+
+#endif  // mozilla_ipc_Transport_posix_h
new file mode 100644
--- /dev/null
+++ b/ipc/glue/Transport_win.cpp
@@ -0,0 +1,101 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=8 et :
+ */
+/* ***** 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 Code.
+ *
+ * The Initial Developer of the Original Code is
+ *   The Mozilla Foundation
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Chris Jones <jones.chris.g@gmail.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 ***** */
+
+#include "base/message_loop.h"
+#include "chrome/common/child_process_info.h"
+
+#include "mozilla/ipc/Transport.h"
+
+using namespace base;
+using namespace std;
+
+namespace mozilla {
+namespace ipc {
+
+bool
+CreateTransport(ProcessHandle aProcOne, ProcessHandle /*unused*/,
+                TransportDescriptor* aOne, TransportDescriptor* aTwo)
+{
+  // This id is used to name the IPC pipe.  The pointer passed to this
+  // function isn't significant.
+  wstring id = ChildProcessInfo::GenerateRandomChannelID(aOne);
+  // Use MODE_SERVER to force creation of the pipe
+  Transport t(id, Transport::MODE_SERVER, nsnull);
+  HANDLE serverPipe = t.GetServerPipeHandle();
+  if (!serverPipe) {
+    return false;
+  }
+
+  // NB: we create the server pipe immediately, instead of just
+  // grabbing an ID, on purpose.  In the current setup, the client
+  // needs to connect to an existing server pipe, so to prevent race
+  // conditions, we create the server side here and then dup it to the
+  // eventual server process.
+  HANDLE serverDup;
+  DWORD access = 0;
+  DWORD options = DUPLICATE_SAME_ACCESS;
+  if (!DuplicateHandle(GetCurrentProcess(), serverPipe, aProcOne,
+                       &serverDup,
+                       access,
+                       FALSE/*not inheritable*/,
+                       options)) {
+    return false;
+  }
+
+  aOne->mPipeName = aTwo->mPipeName = id;
+  aOne->mServerPipe = serverDup;
+  aTwo->mServerPipe = 0;
+  return true;
+}
+
+Transport*
+OpenDescriptor(const TransportDescriptor& aTd, Transport::Mode aMode)
+{
+  return new Transport(aTd.mPipeName, aTd.mServerPipe, aMode, nsnull);
+}
+
+void
+CloseDescriptor(const TransportDescriptor& aTd)
+{
+  CloseHandle(aTd.mServerPipe);
+}
+
+} // namespace ipc
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/ipc/glue/Transport_win.h
@@ -0,0 +1,83 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=8 et :
+ */
+/* ***** 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 Code.
+ *
+ * The Initial Developer of the Original Code is
+ *   The Mozilla Foundation
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Chris Jones <jones.chris.g@gmail.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 ***** */
+
+#ifndef mozilla_ipc_Transport_win_h
+#define mozilla_ipc_Transport_win_h 1
+
+#include <string>
+
+#include "IPC/IPCMessageUtils.h"
+
+
+namespace mozilla {
+namespace ipc {
+
+struct TransportDescriptor
+{
+  std::wstring mPipeName;
+  HANDLE mServerPipe;
+};
+
+} // namespace ipc
+} // namespace mozilla
+
+
+namespace IPC {
+
+template<>
+struct ParamTraits<mozilla::ipc::TransportDescriptor>
+{
+  typedef mozilla::ipc::TransportDescriptor paramType;
+  static void Write(Message* aMsg, const paramType& aParam)
+  {
+    WriteParam(aMsg, aParam.mPipeName);
+    WriteParam(aMsg, aParam.mServerPipe);
+  }
+  static bool Read(const Message* aMsg, void** aIter, paramType* aResult)
+  {
+    return (ReadParam(aMsg, aIter, &aResult->mPipeName) &&
+            ReadParam(aMsg, aIter, &aResult->mServerPipe));
+  }
+};
+
+} // namespace IPC
+
+
+#endif  // mozilla_ipc_Transport_win_h
--- a/ipc/ipdl/Makefile.in
+++ b/ipc/ipdl/Makefile.in
@@ -92,16 +92,17 @@ ALL_IPDLSRCS :=
 PROTOCOLS :=
 
 $(foreach IPDLDIR,$(IPDLDIRS),$(eval $(ADD_IPDLDIR)))
 
 
 CPPSRCS =					\
   $(PROTOCOLS:%.ipdl=%Parent.cpp)		\
   $(PROTOCOLS:%.ipdl=%Child.cpp)		\
+  $(PROTOCOLS:%.ipdl=%.cpp)			\
   $(NULL)
 
 GARBAGE += $(CPPSRCS)
 
 LOCAL_INCLUDES += -I$(DEPTH)/ipc/ipdl/_ipdlheaders
 
 
 include $(topsrcdir)/config/config.mk
--- a/ipc/ipdl/ipdl.py
+++ b/ipc/ipdl/ipdl.py
@@ -70,45 +70,55 @@ includedirs = [ os.path.abspath(incdir) 
 if not len(files):
     op.error("No IPDL files specified")
 
 log(2, 'Generated C++ headers will be generated relative to "%s"', headersdir)
 log(2, 'Generated C++ sources will be generated in "%s"', cppdir)
 
 allprotocols = []
 
+def normalizedFilename(f):
+    if f == '-':
+        return '<stdin>'
+    return f
+
+# First pass: parse and type-check all protocols
 for f in files:
     log(1, os.path.basename(f))
+    filename = normalizedFilename(f)
     if f == '-':
         fd = sys.stdin
-        filename = '<stdin>'
     else:
         fd = open(f)
-        filename = f
 
     specstring = fd.read()
     fd.close()
 
     ast = ipdl.parse(specstring, filename, includedirs=includedirs)
     if ast is None:
         print >>sys.stderr, 'Specification could not be parsed.'
         sys.exit(1)
 
-    allprotocols.append('%sMsgStart' % ast.protocol.name)
-
     log(2, 'checking types')
     if not ipdl.typecheck(ast):
         print >>sys.stderr, 'Specification is not well typed.'
         sys.exit(1)
 
     if _verbosity > 2:
         log(3, '  pretty printed code:')
         ipdl.genipdl(ast, codedir)
 
+# Second pass: generate code
+for f in files:
+    # Read from parser cache
+    filename = normalizedFilename(f)
+    ast = ipdl.parse(None, filename, includedirs=includedirs)
     ipdl.gencxx(filename, ast, headersdir, cppdir)
+    
+    allprotocols.append('%sMsgStart' % ast.protocol.name)
 
 allprotocols.sort()
 
 ipcmsgstart = StringIO()
 
 print >>ipcmsgstart, """
 // CODE GENERATED by ipdl.py. Do not edit.
 
--- a/ipc/ipdl/ipdl/ast.py
+++ b/ipc/ipdl/ipdl/ast.py
@@ -72,16 +72,18 @@ class Visitor:
 
     def visitProtocol(self, p):
         for namespace in p.namespaces:
             namespace.accept(self)
         for spawns in p.spawnsStmts:
             spawns.accept(self)
         for bridges in p.bridgesStmts:
             bridges.accept(self)
+        for opens in p.opensStmts:
+            opens.accept(self)
         for mgr in p.managers:
             mgr.accept(self)
         for managed in p.managesStmts:
             managed.accept(self)
         for msgDecl in p.messageDecls:
             msgDecl.accept(self)
         for transitionStmt in p.transitionStmts:
             transitionStmt.accept(self)
@@ -90,16 +92,19 @@ class Visitor:
         pass
 
     def visitSpawnsStmt(self, spawns):
         pass
 
     def visitBridgesStmt(self, bridges):
         pass
 
+    def visitOpensStmt(self, opens):
+        pass
+
     def visitManager(self, mgr):
         pass
 
     def visitManagesStmt(self, mgs):
         pass
 
     def visitMessageDecl(self, md):
         for inParam in md.inParams:
@@ -264,16 +269,17 @@ class Namespace(Node):
         self.name = namespace
 
 class Protocol(NamespacedNode):
     def __init__(self, loc):
         NamespacedNode.__init__(self, loc)
         self.sendSemantics = ASYNC
         self.spawnsStmts = [ ]
         self.bridgesStmts = [ ]
+        self.opensStmts = [ ]
         self.managers = [ ]
         self.managesStmts = [ ]
         self.messageDecls = [ ]
         self.transitionStmts = [ ]
         self.startStates = [ ]
 
 class StructField(Node):
     def __init__(self, loc, type, name):
@@ -299,16 +305,22 @@ class SpawnsStmt(Node):
         self.spawnedAs = spawnedAs
 
 class BridgesStmt(Node):
     def __init__(self, loc, parentSide, childSide):
         Node.__init__(self, loc)
         self.parentSide = parentSide
         self.childSide = childSide
 
+class OpensStmt(Node):
+    def __init__(self, loc, side, proto):
+        Node.__init__(self, loc)
+        self.side = side
+        self.proto = proto
+
 class Manager(Node):
     def __init__(self, loc, managerName):
         Node.__init__(self, loc)
         self.name = managerName
 
 class ManagesStmt(Node):
     def __init__(self, loc, managedName):
         Node.__init__(self, loc)
--- a/ipc/ipdl/ipdl/cxx/ast.py
+++ b/ipc/ipdl/ipdl/cxx/ast.py
@@ -105,16 +105,22 @@ class Visitor:
             meth.typeop.accept(self)
         if meth.T is not None:
             meth.T.accept(self)
 
     def visitMethodDefn(self, meth):
         meth.decl.accept(self)
         self.visitBlock(meth)
 
+    def visitFunctionDecl(self, fun):
+        self.visitMethodDecl(fun)
+
+    def visitFunctionDefn(self, fd):
+        self.visitMethodDefn(fd)
+
     def visitConstructorDecl(self, ctor):
         self.visitMethodDecl(ctor)
 
     def visitConstructorDefn(self, cd):
         cd.decl.accept(self)
         for init in cd.memberinits:
             init.accept(self)
         self.visitBlock(cd)
@@ -504,16 +510,30 @@ class MethodDecl(Node):
             typeop=copy.deepcopy(self.typeop, memo),
             T=copy.deepcopy(self.T, memo))
 
 class MethodDefn(Block):
     def __init__(self, decl):
         Block.__init__(self)
         self.decl = decl
 
+class FunctionDecl(MethodDecl):
+    def __init__(self, name, params=[ ], ret=Type('void'),
+                 static=0, warn_unused=0,
+                 inline=0, force_inline=0,
+                 T=None):
+        MethodDecl.__init__(self, name, params=params, ret=ret,
+                            static=static, warn_unused=warn_unused,
+                            inline=inline, force_inline=force_inline,
+                            T=T)
+
+class FunctionDefn(MethodDefn):
+    def __init__(self, decl):
+        MethodDefn.__init__(self, decl)
+
 class ConstructorDecl(MethodDecl):
     def __init__(self, name, params=[ ], explicit=0, force_inline=0):
         MethodDecl.__init__(self, name, params=params, ret=None,
                             force_inline=force_inline)
         self.explicit = explicit
 
     def __deepcopy__(self, memo):
         return ConstructorDecl(self.name,
--- a/ipc/ipdl/ipdl/lower.py
+++ b/ipc/ipdl/ipdl/lower.py
@@ -30,17 +30,17 @@
 #
 # ***** END LICENSE BLOCK *****
 
 import os, re, sys
 from copy import deepcopy
 
 import ipdl.ast
 from ipdl.cxx.ast import *
-from ipdl.type import TypeVisitor
+from ipdl.type import ActorType, ProcessGraph, TypeVisitor
 
 # FIXME/cjones: the chromium Message logging code doesn't work on
 # gcc/POSIX, because it wprintf()s across the chromium/mozilla
 # boundary. one side builds with -fshort-wchar, the other doesn't.
 # this code will remain off until the chromium base lib is replaced
 EMIT_LOGGING_CODE = ('win32' == sys.platform)
 
 ##-----------------------------------------------------------------------------
@@ -50,37 +50,45 @@ class LowerToCxx:
     def lower(self, tu):
         '''returns |[ header: File ], [ cpp : File ]| representing the
 lowered form of |tu|'''
         # annotate the AST with IPDL/C++ IR-type stuff used later
         tu.accept(_DecorateWithCxxStuff())
 
         pname = tu.protocol.name
 
-        pheader = File(pname +'.h')
-        _GenerateProtocolHeader().lower(tu, pheader)
+        pheader, pcpp = File(pname +'.h'), File(pname +'.cpp')
+        _GenerateProtocolCode().lower(tu, pheader, pcpp)
 
         parentheader, parentcpp = File(pname +'Parent.h'), File(pname +'Parent.cpp')
         _GenerateProtocolParentCode().lower(
             tu, pname+'Parent', parentheader, parentcpp)
 
         childheader, childcpp = File(pname +'Child.h'), File(pname +'Child.cpp')
         _GenerateProtocolChildCode().lower(
             tu, pname+'Child', childheader, childcpp)
 
-        return [ pheader, parentheader, childheader ], [ parentcpp, childcpp ]
+        return [ pheader, parentheader, childheader ], [ pcpp, parentcpp, childcpp ]
 
 
 ##-----------------------------------------------------------------------------
 ## Helper code
 ##
 
 _NULL_ACTOR_ID = ExprLiteral.ZERO
 _FREED_ACTOR_ID = ExprLiteral.ONE
 
+_DISCLAIMER = Whitespace('''//
+// Automatically generated by ipdlc.
+// Edit at your own risk
+//
+
+''')
+
+
 class _struct: pass
 
 def _protocolHeaderName(p, side=''):
     if side: side = side.title()
     base = p.name + side
 
     
     pfx = '/'.join([ ns.name for ns in p.namespaces ])
@@ -129,16 +137,19 @@ def _actorChannel(actor):
     return ExprSelect(actor, '->', 'mChannel')
 
 def _actorManager(actor):
     return ExprSelect(actor, '->', 'mManager')
 
 def _actorState(actor):
     return ExprSelect(actor, '->', 'mState')
 
+def _backstagePass():
+    return ExprCall(ExprVar('mozilla::ipc::PrivateIPDLInterface'))
+
 def _nullState(proto=None):
     pfx = ''
     if proto is not None:  pfx = proto.name() +'::'
     return ExprVar(pfx +'__Null')
 
 def _errorState(proto=None):
     pfx = ''
     if proto is not None:  pfx = proto.name() +'::'
@@ -341,16 +352,21 @@ def _cxxArrayHasElementSorted(arr, elt):
         ExprSelect(arr, '.', 'NoIndex'), '!=',
         ExprCall(ExprSelect(arr, '.', 'BinaryIndexOf'), args=[ elt ]))
 
 def _otherSide(side):
     if side == 'child':  return 'parent'
     if side == 'parent':  return 'child'
     assert 0
 
+def _sideToTransportMode(side):
+    if side == 'parent':  mode = 'SERVER'
+    elif side == 'child': mode = 'CLIENT'
+    return ExprVar('mozilla::ipc::Transport::MODE_'+ mode)
+
 def _ifLogging(stmts):
     iflogging = StmtIf(ExprCall(ExprVar('mozilla::ipc::LoggingEnabled')))
     iflogging.addifstmts(stmts)
     return iflogging
 
 # We need the ASTs of structs and unions to generate pickling code for
 # them, but the pickling codegen only has their type info.  This map
 # allows the pickling code to get these ASTs given the type info.
@@ -1047,16 +1063,31 @@ class Protocol(ipdl.ast.Protocol):
         return ExprVar('IsTrackingSharedMemory')
 
     def destroySharedMemory(self):
         return ExprVar('DestroySharedMemory')
 
     def otherProcessMethod(self):
         return ExprVar('OtherProcess')
 
+    def callOtherProcess(self, actorThis=None):
+        fn = self.otherProcessMethod()
+        if actorThis is not None:
+            fn = ExprSelect(actorThis, '->', fn.name)
+        return ExprCall(fn)
+
+    def getChannelMethod(self):
+        return ExprVar('GetIPCChannel')
+
+    def callGetChannel(self, actorThis=None):
+        fn = self.getChannelMethod()
+        if actorThis is not None:
+            fn = ExprSelect(actorThis, '->', fn.name)
+        return ExprCall(fn)
+
     def processingErrorVar(self):
         assert self.decl.type.isToplevel()
         return ExprVar('ProcessingError')
 
     def shouldContinueFromTimeoutVar(self):
         assert self.decl.type.isToplevel()
         return ExprVar('ShouldContinueFromReplyTimeout')
 
@@ -1306,56 +1337,68 @@ with some new IPDL/C++ nodes that are tu
 
     def visitTransitionStmt(self, ts):
         name = ts.state.decl.progname
         ts.state.decl.cxxname = name
         ts.state.decl.cxxenum = ExprVar(self.protocolName +'::'+ name)
 
 ##-----------------------------------------------------------------------------
 
-class _GenerateProtocolHeader(ipdl.ast.Visitor):
-    '''Creates a header containing code common to both the parent and
-child actors.'''
+class _GenerateProtocolCode(ipdl.ast.Visitor):
+    '''Creates code common to both the parent and child actors.'''
     def __init__(self):
         self.protocol = None     # protocol we're generating a class for
-        self.file = None         # File stuff is stuck in
+        self.hdrfile = None      # what will become Protocol.h
+        self.cppfile = None      # what will become Protocol.cpp
+        self.cppIncludeHeaders = []
         self.structUnionDefns = []
-
-    def lower(self, tu, outcxxfile):
+        self.funcDefns = []
+
+    def lower(self, tu, cxxHeaderFile, cxxFile):
         self.protocol = tu.protocol
-        self.file = outcxxfile
+        self.hdrfile = cxxHeaderFile
+        self.cppfile = cxxFile
         tu.accept(self)
 
     def visitTranslationUnit(self, tu):
-        f = self.file
-
-        f.addthing(Whitespace('''//
-// Automatically generated by the IPDL compiler.
-// Edit at your own risk
-//
-
-'''))
-        f.addthings(_includeGuardStart(f))
-        f.addthing(Whitespace.NL)
+        hf = self.hdrfile
+
+        hf.addthing(_DISCLAIMER)
+        hf.addthings(_includeGuardStart(hf))
+        hf.addthing(Whitespace.NL)
 
         ipdl.ast.Visitor.visitTranslationUnit(self, tu)
 
-        f.addthings(self.structUnionDefns)
-
-        f.addthing(Whitespace.NL)
-        f.addthings(_includeGuardEnd(f))
+        hf.addthing(Whitespace.NL)
+        hf.addthings(_includeGuardEnd(hf))
+
+        cf = self.cppfile
+        cf.addthings((
+            [ _DISCLAIMER, Whitespace.NL ]
+            + [ CppDirective('include','"'+h+'.h"')
+                for h in self.cppIncludeHeaders ]
+            + [ Whitespace.NL ]
+        ))
+       
+        # construct the namespace into which we'll stick all our defns
+        ns = Namespace(self.protocol.name)
+        cf.addthing(_putInNamespaces(ns, self.protocol.namespaces))
+        ns.addstmts(([ Whitespace.NL]
+                     + self.funcDefns
+                     +[ Whitespace.NL ]))
+        cf.addthings(self.structUnionDefns)
 
 
     def visitCxxInclude(self, inc):
-        self.file.addthing(CppDirective('include', '"'+ inc.file +'"'))
+        self.hdrfile.addthing(CppDirective('include', '"'+ inc.file +'"'))
 
     def processStructOrUnionClass(self, su, which, forwarddecls, cls):
-        clsdecl, methoddefns = _splitClassDeclDefn(cls, inlinedefns=1)
+        clsdecl, methoddefns = _splitClassDeclDefn(cls)
         
-        self.file.addthings(
+        self.hdrfile.addthings(
             [  Whitespace.NL ]
             + forwarddecls
             + [ Whitespace("""
 //-----------------------------------------------------------------------------
 // Declaration of the IPDL type |%s %s|
 //
 """% (which, su.name)),
                 _putInNamespaces(clsdecl, su.namespaces),
@@ -1374,27 +1417,62 @@ child actors.'''
         return self.processStructOrUnionClass(sd, 'struct',
                                               *_generateCxxStruct(sd))
 
     def visitUnionDecl(self, ud):
         return self.processStructOrUnionClass(ud, 'union',
                                               *_generateCxxUnion(ud))
 
     def visitProtocol(self, p):
-        self.file.addthing(Whitespace("""
+        self.cppIncludeHeaders.append(_protocolHeaderName(self.protocol, ''))
+        bridges = ProcessGraph.bridgesOf(p.decl.type)
+        for bridge in bridges:
+            ppt, pside = bridge.parent.ptype, _otherSide(bridge.parent.side)
+            cpt, cside = bridge.child.ptype, _otherSide(bridge.child.side)
+            self.hdrfile.addthings([
+                Whitespace.NL,
+                _makeForwardDeclForActor(ppt, pside),
+                _makeForwardDeclForActor(cpt, cside)
+            ])
+            self.cppIncludeHeaders.append(_protocolHeaderName(ppt._p, pside))
+            self.cppIncludeHeaders.append(_protocolHeaderName(cpt._p, cside))
+
+        opens = ProcessGraph.opensOf(p.decl.type)
+        for o in opens:
+            optype, oside = o.opener.ptype, o.opener.side
+            self.hdrfile.addthings([
+                Whitespace.NL,
+                _makeForwardDeclForActor(optype, oside)
+            ])
+            self.cppIncludeHeaders.append(_protocolHeaderName(optype._p, oside))
+
+        self.hdrfile.addthing(Whitespace("""
 //-----------------------------------------------------------------------------
 // Code common to %sChild and %sParent
 //
 """% (p.name, p.name)))
 
         # construct the namespace into which we'll stick all our decls
         ns = Namespace(self.protocol.name)
-        self.file.addthing(_putInNamespaces(ns, p.namespaces))
+        self.hdrfile.addthing(_putInNamespaces(ns, p.namespaces))
         ns.addstmt(Whitespace.NL)
 
+        # user-facing methods for connecting two process with a new channel
+        for bridge in bridges:
+            bdecl, bdefn = _splitFuncDeclDefn(self.genBridgeFunc(bridge))
+            ns.addstmts([ bdecl, Whitespace.NL ])
+            self.funcDefns.append(bdefn)
+
+        # user-facing methods for opening a new channel across two
+        # existing endpoints
+        for o in opens:
+            odecl, odefn = _splitFuncDeclDefn(self.genOpenFunc(o))
+            ns.addstmts([ odecl, Whitespace.NL ])
+            self.funcDefns.append(odefn)
+
         # state information
         stateenum = TypeEnum('State')
         # NB: __Dead is the first state on purpose, so that it has
         # value '0'
         stateenum.addId(_deadState().name)
         stateenum.addId(_nullState().name)
         stateenum.addId(_errorState().name)
         for ts in p.transitionStmts:
@@ -1416,17 +1494,19 @@ child actors.'''
         for md in p.messageDecls:
             msgenum.addId(md.msgId())
             if md.hasReply():
                 msgenum.addId(md.replyId())
 
         msgenum.addId(self.protocol.name +'End')
         ns.addstmts([ StmtDecl(Decl(msgenum, '')), Whitespace.NL ])
 
-        ns.addstmts([ self.genTransitionFunc(), Whitespace.NL ])
+        tfDecl, tfDefn = _splitFuncDeclDefn(self.genTransitionFunc())
+        ns.addstmts([ tfDecl, Whitespace.NL ])
+        self.funcDefns.append(tfDefn)
 
         typedefs = self.protocol.decl.cxxtypedefs
         for md in p.messageDecls:
             ns.addstmts([
                 _generateMessageClass(md.msgClass(), md.msgId(),
                                       typedefs, md.prettyMsgName(p.name+'::')),
                 Whitespace.NL ])
             if md.hasReply():
@@ -1434,16 +1514,60 @@ child actors.'''
                     _generateMessageClass(
                         md.replyClass(), md.replyId(),
                         typedefs, md.prettyReplyName(p.name+'::')),
                     Whitespace.NL ])
 
         ns.addstmts([ Whitespace.NL, Whitespace.NL ])
 
 
+    def genBridgeFunc(self, bridge):
+        p = self.protocol
+        parentHandleType = _cxxBareType(ActorType(bridge.parent.ptype),
+                                        _otherSide(bridge.parent.side))
+        parentvar = ExprVar('parentHandle')
+
+        childHandleType = _cxxBareType(ActorType(bridge.child.ptype),
+                                       _otherSide(bridge.child.side))
+        childvar = ExprVar('childHandle')
+
+        bridgefunc = MethodDefn(MethodDecl(
+            'Bridge',
+            params=[ Decl(parentHandleType, parentvar.name),
+                     Decl(childHandleType, childvar.name) ],
+            ret=Type.BOOL))
+        bridgefunc.addstmt(StmtReturn(ExprCall(
+            ExprVar('mozilla::ipc::Bridge'),
+            args=[ _backstagePass(),
+                   p.callGetChannel(parentvar), p.callOtherProcess(parentvar),
+                   p.callGetChannel(childvar), p.callOtherProcess(childvar),
+                   _protocolId(p.decl.type)
+                   ])))
+        return bridgefunc
+
+
+    def genOpenFunc(self, o):
+        p = self.protocol
+        localside = o.opener.side
+        openertype = _cxxBareType(ActorType(o.opener.ptype), o.opener.side)
+        openervar = ExprVar('opener')
+        openfunc = MethodDefn(MethodDecl(
+            'Open',
+            params=[ Decl(openertype, openervar.name) ],
+            ret=Type.BOOL))
+        openfunc.addstmt(StmtReturn(ExprCall(
+            ExprVar('mozilla::ipc::Open'),
+            args=[ _backstagePass(),
+                   p.callGetChannel(openervar), p.callOtherProcess(openervar),
+                   _sideToTransportMode(localside),
+                   _protocolId(p.decl.type)
+                   ])))
+        return openfunc
+
+
     def genTransitionFunc(self):
         ptype = self.protocol.decl.type
         usesend, sendvar = set(), ExprVar('__Send')
         userecv, recvvar = set(), ExprVar('__Recv')
         
         def sameTrigger(trigger, actionexpr):
             if trigger is ipdl.ast.SEND or trigger is ipdl.ast.CALL:
                 usesend.add('yes')
@@ -1461,23 +1585,22 @@ child actors.'''
 
         # bool Transition(State from, Trigger trigger, State* next)
         fromvar = ExprVar('from')
         triggervar = ExprVar('trigger')
         nextvar = ExprVar('next')
         msgexpr = ExprSelect(triggervar, '.', 'mMsg')
         actionexpr = ExprSelect(triggervar, '.', 'mAction')
 
-        transitionfunc = MethodDefn(MethodDecl(
+        transitionfunc = FunctionDefn(FunctionDecl(
             'Transition',
             params=[ Decl(Type('State'), fromvar.name),
                      Decl(Type('mozilla::ipc::Trigger'), triggervar.name),
                      Decl(Type('State', ptr=1), nextvar.name) ],
-            ret=Type.BOOL,
-            inline=1))
+            ret=Type.BOOL))
 
         fromswitch = StmtSwitch(fromvar)
 
         for ts in self.protocol.transitionStmts:
             msgswitch = StmtSwitch(msgexpr)
 
             msgToTransitions = { }
 
@@ -2239,36 +2362,31 @@ class _GenerateProtocolActorCode(ipdl.as
         tu.accept(self)
 
     def standardTypedefs(self):
         return [
             Typedef(Type('IPC::Message'), 'Message'),
             Typedef(Type(self.protocol.channelName()), 'Channel'),
             Typedef(Type(self.protocol.fqListenerName()), 'ChannelListener'),
             Typedef(Type('base::ProcessHandle'), 'ProcessHandle'),
+            Typedef(Type('mozilla::ipc::AsyncChannel'), 'AsyncChannel'),
             Typedef(Type('mozilla::ipc::SharedMemory'), 'SharedMemory'),
             Typedef(Type('mozilla::ipc::Trigger'), 'Trigger')
         ]
 
 
     def visitTranslationUnit(self, tu):
         self.protocol = tu.protocol
 
         hf = self.hdrfile
         cf = self.cppfile
 
-        disclaimer = Whitespace('''//
-// Automatically generated by ipdlc.
-// Edit at your own risk
-//
-
-''')
         # make the C++ header
         hf.addthings(
-            [ disclaimer ]
+            [ _DISCLAIMER ]
             + _includeGuardStart(hf)
             +[
                 Whitespace.NL,
                 CppDirective(
                     'include',
                     '"'+ _protocolHeaderName(tu.protocol) +'.h"')
             ])
 
@@ -2313,17 +2431,17 @@ class _GenerateProtocolActorCode(ipdl.as
                 self.protocol.namespaces).fromclass(self.cls)
             +([
                 CppDirective('endif', '// if 0'),
                 Whitespace.NL ])
             + _includeGuardEnd(hf))
 
         # make the .cpp file
         cf.addthings([
-            disclaimer,
+            _DISCLAIMER,
             Whitespace.NL,
             CppDirective(
                 'include',
                 '"'+ _protocolHeaderName(self.protocol, self.side) +'.h"')
         ])
              
         if self.protocol.decl.type.isToplevel():
             cf.addthings([
@@ -2391,16 +2509,20 @@ class _GenerateProtocolActorCode(ipdl.as
             Whitespace.NL ])
 
         self.cls = Class(
             self.clsname,
             inherits=[ Inherit(Type(p.fqListenerName()), viz='protected'),
                        Inherit(p.managerInterfaceType(), viz='protected') ],
             abstract=True)
 
+        bridgeActorsCreated = ProcessGraph.bridgeEndpointsOf(ptype, self.side)
+        opensActorsCreated = ProcessGraph.opensEndpointsOf(ptype, self.side)
+        channelOpenedActors = bridgeActorsCreated + opensActorsCreated
+
         friends = _FindFriends().findFriends(ptype)
         if ptype.isManaged():
             friends.update(ptype.managers)
 
         # |friend| managed actors so that they can call our Dealloc*()
         friends.update(ptype.manages)
 
         # don't friend ourself if we're a self-managed protocol
@@ -2412,21 +2534,37 @@ class _GenerateProtocolActorCode(ipdl.as
                 _makeForwardDeclForActor(friend, self.prettyside),
                 Whitespace.NL
             ])
             self.cls.addstmts([
                 FriendClassDecl(_actorName(friend.fullname(),
                                            self.prettyside)),
                 Whitespace.NL ])
 
+        for actor in channelOpenedActors:
+            self.hdrfile.addthings([
+                Whitespace.NL,
+                _makeForwardDeclForActor(actor.ptype, actor.side),
+                Whitespace.NL
+            ])
+
         self.cls.addstmt(Label.PROTECTED)
         for typedef in p.cxxTypedefs():
             self.cls.addstmt(typedef)
         for typedef in self.includedActorTypedefs:
             self.cls.addstmt(typedef)
+        # XXX these don't really fit in the other lists; just include
+        # them here for now
+        self.cls.addstmts([
+            Typedef(Type('base::ProcessId'), 'ProcessId'),
+            Typedef(Type('mozilla::ipc::ProtocolId'), 'ProtocolId'),
+            Typedef(Type('mozilla::ipc::Transport'), 'Transport'),
+            Typedef(Type('mozilla::ipc::TransportDescriptor'), 'TransportDescriptor')
+        ])
+
         self.cls.addstmt(Whitespace.NL)
 
         self.cls.addstmts([ Typedef(p.fqStateType(), 'State'), Whitespace.NL ])
 
         # interface methods that the concrete subclass has to impl
         for md in p.messageDecls:
             isctor, isdtor = md.decl.type.isCtor(), md.decl.type.isDtor()
 
@@ -2462,16 +2600,27 @@ class _GenerateProtocolActorCode(ipdl.as
                 virtual=1, pure=1)))
 
             self.cls.addstmt(StmtDecl(MethodDecl(
                 _deallocMethod(managed).name,
                 params=[ Decl(actortype, 'actor') ],
                 ret=Type.BOOL,
                 virtual=1, pure=1)))
 
+        for actor in channelOpenedActors:
+            # add the Alloc interface for actors created when a
+            # new channel is opened
+            actortype = _cxxBareType(actor.asType(), actor.side)
+            self.cls.addstmt(StmtDecl(MethodDecl(
+                _allocMethod(actor.ptype).name,
+                params=[ Decl(Type('Transport', ptr=1), 'transport'),
+                         Decl(Type('ProcessId'), 'otherProcess') ],
+                ret=actortype,
+                virtual=1, pure=1)))
+
         # optional ActorDestroy() method; default is no-op
         self.cls.addstmts([
             Whitespace.NL,
             MethodDefn(MethodDecl(
                 _destroyMethod().name,
                 params=[ Decl(_DestroyReason.Type(), 'why') ],
                 virtual=1)),
             Whitespace.NL
@@ -2546,31 +2695,35 @@ class _GenerateProtocolActorCode(ipdl.as
 
         self.cls.addstmts([ dtor, Whitespace.NL ])
 
         if ptype.isToplevel():
             # Open()
             aTransportVar = ExprVar('aTransport')
             aThreadVar = ExprVar('aThread')
             processvar = ExprVar('aOtherProcess')
+            sidevar = ExprVar('aSide')
             openmeth = MethodDefn(
                 MethodDecl(
                     'Open',
                     params=[ Decl(Type('Channel::Transport', ptr=True),
                                       aTransportVar.name),
                              Decl(Type('ProcessHandle'), processvar.name),
                              Param(Type('MessageLoop', ptr=True),
                                    aThreadVar.name,
-                                   default=ExprLiteral.NULL) ],
+                                   default=ExprLiteral.NULL),
+                             Param(Type('AsyncChannel::Side'),
+                                   sidevar.name,
+                                   default=ExprVar('Channel::Unknown')) ],
                     ret=Type.BOOL))
 
             openmeth.addstmts([
                 StmtExpr(ExprAssn(p.otherProcessVar(), processvar)),
                 StmtReturn(ExprCall(ExprSelect(p.channelVar(), '.', 'Open'),
-                                    [ aTransportVar, aThreadVar ]))
+                                    [ aTransportVar, aThreadVar, sidevar ]))
             ])
             self.cls.addstmts([
                 openmeth,
                 Whitespace.NL ])
 
             # Close()
             closemeth = MethodDefn(MethodDecl('Close'))
             closemeth.addstmt(StmtExpr(
@@ -2651,16 +2804,21 @@ class _GenerateProtocolActorCode(ipdl.as
             if toplevel.talksRpc():
                 self.rpcSwitch = StmtSwitch(msgtype)
 
         # implement Send*() methods and add dispatcher cases to
         # message switch()es
         for md in p.messageDecls:
             self.visitMessageDecl(md)
 
+        # Handlers for the creation of actors when a new channel is
+        # opened
+        if len(channelOpenedActors):
+            self.makeChannelOpenedHandlers(channelOpenedActors)
+
         # add default cases
         default = StmtBlock()
         default.addstmt(StmtReturn(_Result.NotKnown))
         self.asyncSwitch.addcase(DefaultLabel(), default)
         if toplevel.talksSync():
             self.syncSwitch.addcase(DefaultLabel(), default)
             if toplevel.talksRpc():
                 self.rpcSwitch.addcase(DefaultLabel(), default)
@@ -2934,18 +3092,17 @@ class _GenerateProtocolActorCode(ipdl.as
             # if the error happens on the parent side, the parent
             # kills off the child
             fatalerror.addstmts([
                 _printErrorMessage(
                     '['+ actorname +'] killing child side as a result'),
                 Whitespace.NL
             ])
 
-            ifkill = StmtIf(ExprNot(
-                _killProcess(ExprCall(p.otherProcessMethod()))))
+            ifkill = StmtIf(ExprNot(_killProcess(p.callOtherProcess())))
             ifkill.addifstmt(
                 _printErrorMessage("  may have failed to kill child!"))
             fatalerror.addstmt(ifkill)
         else:
             # and if it happens on the child side, the child commits
             # seppuko
             fatalerror.addstmt(
                 _runtimeAbort('['+ actorname +'] abort()ing as a result'))
@@ -3173,16 +3330,20 @@ class _GenerateProtocolActorCode(ipdl.as
             params=[ Decl(_rawShmemType(ptr=1), rawvar.name) ],
             virtual=1))
 
         otherprocess = MethodDefn(MethodDecl(
             p.otherProcessMethod().name,
             ret=Type('ProcessHandle'),
             const=1,
             virtual=1))
+        getchannel = MethodDefn(MethodDecl(
+            p.getChannelMethod().name,
+            ret=Type('AsyncChannel', ptr=1),
+            virtual=1))
 
         if p.decl.type.isToplevel():
             tmpvar = ExprVar('tmp')
             
             register.addstmts([
                 StmtDecl(Decl(_actorIdType(), tmpvar.name),
                          p.nextActorIdExpr(self.side)),
                 StmtExpr(ExprCall(
@@ -3225,17 +3386,17 @@ class _GenerateProtocolActorCode(ipdl.as
             createshmem.addstmts([
                 StmtDecl(
                     Decl(_shmemType(), shmemvar.name),
                     initargs=[ _shmemBackstagePass(),
                                _autoptrGet(rawvar),
                                p.nextShmemIdExpr(self.side) ]),
                 StmtDecl(Decl(Type('Message', ptr=1), descriptorvar.name),
                          init=_shmemShareTo(shmemvar,
-                                            ExprCall(p.otherProcessMethod()),
+                                            p.callOtherProcess(),
                                             p.routingId()))
             ])
             failif = StmtIf(ExprNot(descriptorvar))
             failif.addifstmt(StmtReturn.FALSE)
             createshmem.addstmt(failif)
 
             failif = StmtIf(ExprNot(ExprCall(
                 ExprSelect(p.channelVar(), p.channelSel(), 'Send'),
@@ -3266,17 +3427,17 @@ class _GenerateProtocolActorCode(ipdl.as
             adoptshmem.addstmts([
                 StmtDecl(
                     Decl(_shmemType(), shmemvar.name),
                     initargs=[ _shmemBackstagePass(),
                                rawvar,
                                p.nextShmemIdExpr(self.side) ]),
                 StmtDecl(Decl(Type('Message', ptr=1), descriptorvar.name),
                          init=_shmemShareTo(shmemvar,
-                                            ExprCall(p.otherProcessMethod()),
+                                            p.callOtherProcess(),
                                             p.routingId()))
             ])
             failif = StmtIf(ExprNot(descriptorvar))
             failif.addifstmt(StmtReturn.FALSE)
             adoptshmem.addstmt(failif)
 
             failif = StmtIf(ExprNot(ExprCall(
                 ExprSelect(p.channelVar(), p.channelSel(), 'Send'),
@@ -3320,17 +3481,17 @@ class _GenerateProtocolActorCode(ipdl.as
 
             failif = StmtIf(ExprNot(rawvar))
             failif.addifstmt(StmtReturn.FALSE)
             destroyshmem.addstmts([
                 failif,
                 StmtDecl(Decl(Type('Message', ptr=1), descriptorvar.name),
                          init=_shmemUnshareFrom(
                              shmemvar,
-                             ExprCall(p.otherProcessMethod()),
+                             p.callOtherProcess(),
                              p.routingId())),
                 Whitespace.NL,
                 StmtExpr(p.removeShmemId(idvar)),
                 StmtExpr(_shmemDealloc(rawvar)),
                 Whitespace.NL,
                 StmtReturn(ExprBinary(
                     descriptorvar, '&&',
                     ExprCall(
@@ -3355,16 +3516,17 @@ class _GenerateProtocolActorCode(ipdl.as
                     StmtReturn(_Result.NotKnown)
                 ])
                 self.asyncSwitch.addcase(
                     CaseLabel('SHMEM_CREATED_MESSAGE_TYPE'), abort)
                 self.asyncSwitch.addcase(
                     CaseLabel('SHMEM_DESTROYED_MESSAGE_TYPE'), abort)
             
             otherprocess.addstmt(StmtReturn(p.otherProcessVar()))
+            getchannel.addstmt(StmtReturn(ExprAddrOf(p.channelVar())))
         else:
             # delegate registration to manager
             register.addstmt(StmtReturn(ExprCall(
                 ExprSelect(p.managerVar(), '->', p.registerMethod().name),
                 [ routedvar ])))
             registerid.addstmt(StmtReturn(ExprCall(
                 ExprSelect(p.managerVar(), '->', p.registerIDMethod().name),
                 [ routedvar, idvar ])))
@@ -3385,19 +3547,19 @@ class _GenerateProtocolActorCode(ipdl.as
                 [ idvar ])))
             istracking.addstmt(StmtReturn(ExprCall(
                 ExprSelect(p.managerVar(), '->',
                            p.isTrackingSharedMemory().name),
                 [ rawvar ])))
             destroyshmem.addstmt(StmtReturn(ExprCall(
                 ExprSelect(p.managerVar(), '->', p.destroySharedMemory().name),
                 [ shmemvar ])))
-            otherprocess.addstmt(StmtReturn(ExprCall(
-                ExprSelect(p.managerVar(), '->',
-                           p.otherProcessMethod().name))))
+            otherprocess.addstmt(StmtReturn(
+                p.callOtherProcess(p.managerVar())))
+            getchannel.addstmt(StmtReturn(p.channelVar()))
 
         # all protocols share the "same" RemoveManagee() implementation
         pvar = ExprVar('aProtocolId')
         listenervar = ExprVar('aListener')
         removemanagee = MethodDefn(MethodDecl(
             p.removeManageeMethod().name,
             params=[ Decl(_protocolIdType(), pvar.name),
                      Decl(listenertype, listenervar.name) ],
@@ -3440,16 +3602,17 @@ class _GenerateProtocolActorCode(ipdl.as
                  unregister,
                  removemanagee,
                  createshmem,
                  adoptshmem,
                  lookupshmem,
                  istracking,
                  destroyshmem,
                  otherprocess,
+                 getchannel,
                  Whitespace.NL ]
 
     def makeShmemIface(self):
         p = self.protocol
         idvar = ExprVar('aId')
         sizevar = ExprVar('aSize')
         typevar = ExprVar('aType')
         memvar = ExprVar('aMem')
@@ -3630,16 +3793,78 @@ class _GenerateProtocolActorCode(ipdl.as
             StmtExpr(p.removeShmemId(idvar)),
             StmtExpr(_shmemDealloc(rawvar)),
             StmtReturn(_Result.Processed)
         ])
 
         return case
 
 
+    def makeChannelOpenedHandlers(self, actors):
+        handlers = StmtBlock()
+
+        # unpack the transport descriptor et al.
+        msgvar = self.msgvar
+        tdvar = ExprVar('td')
+        pidvar = ExprVar('pid')
+        pvar = ExprVar('p')
+        iffail = StmtIf(ExprNot(ExprCall(
+            ExprVar('mozilla::ipc::UnpackChannelOpened'),
+            args=[ _backstagePass(),
+                   msgvar,
+                   ExprAddrOf(tdvar), ExprAddrOf(pidvar), ExprAddrOf(pvar) ])))
+        iffail.addifstmt(StmtReturn(_Result.PayloadError))
+        handlers.addstmts([
+            StmtDecl(Decl(Type('TransportDescriptor'), tdvar.name)),
+            StmtDecl(Decl(Type('ProcessId'), pidvar.name)),
+            StmtDecl(Decl(Type('ProtocolId'), pvar.name)),
+            iffail,
+            Whitespace.NL
+        ])
+
+        def makeHandlerCase(actor):
+            case = StmtBlock()
+            modevar = _sideToTransportMode(actor.side)
+            tvar = ExprVar('t')
+            iffailopen = StmtIf(ExprNot(ExprAssn(
+                tvar,
+                ExprCall(ExprVar('mozilla::ipc::OpenDescriptor'),
+                         args=[ tdvar, modevar ]))))
+            iffailopen.addifstmt(StmtReturn(_Result.ValuError))
+
+            iffailalloc = StmtIf(ExprNot(ExprCall(
+                _allocMethod(actor.ptype),
+                args=[ tvar, pidvar ])))
+            iffailalloc.addifstmt(StmtReturn(_Result.ProcessingError))
+
+            case.addstmts([
+                StmtDecl(Decl(Type('Transport', ptr=1), tvar.name)),
+                iffailopen,
+                iffailalloc,
+                StmtBreak()
+            ])
+            return CaseLabel(_protocolId(actor.ptype).name), case
+
+        pswitch = StmtSwitch(pvar)
+        for actor in actors:
+            label, case = makeHandlerCase(actor)
+            pswitch.addcase(label, case)
+
+        die = Block()
+        die.addstmts([ _runtimeAbort('Invalid protocol'),
+                       StmtReturn(_Result.ValuError) ])
+        pswitch.addcase(DefaultLabel(), die)
+
+        handlers.addstmts([
+            pswitch,
+            StmtReturn(_Result.Processed)
+        ])
+        self.asyncSwitch.addcase(CaseLabel('CHANNEL_OPENED_MESSAGE_TYPE'),
+                                 handlers)
+
     ##-------------------------------------------------------------------------
     ## The next few functions are the crux of the IPDL code generator.
     ## They generate code for all the nasty work of message
     ## serialization/deserialization and dispatching handlers for
     ## received messages.
     ##
     def implementPickling(self):
         # pickling of "normal", non-IPDL types
@@ -4679,43 +4904,47 @@ class _GenerateProtocolChildCode(_Genera
     def receivesMessage(self, md):
         return md.decl.type.isInout() or md.decl.type.isOut()
 
 
 ##-----------------------------------------------------------------------------
 ## Utility passes
 ##
 
-def _splitClassDeclDefn(cls, inlinedefns=0):
+def _splitClassDeclDefn(cls):
     """Destructively split |cls| methods into declarations and
 definitions (if |not methodDecl.force_inline|).  Return classDecl,
 methodDefns."""
     defns = Block()
 
     for i, stmt in enumerate(cls.stmts):
         if isinstance(stmt, MethodDefn) and not stmt.decl.force_inline:
-            decl, defn = _splitMethodDefn(stmt, cls.name, inlinedefns)
+            decl, defn = _splitMethodDefn(stmt, cls.name)
             cls.stmts[i] = StmtDecl(decl)
             defns.addstmts([ defn, Whitespace.NL ])
 
     return cls, defns
 
-def _splitMethodDefn(md, clsname, inlinedefn):
+def _splitMethodDefn(md, clsname):
     saveddecl = deepcopy(md.decl)
     md.decl.name = (clsname +'::'+ md.decl.name)
     md.decl.virtual = 0
     md.decl.static = 0
     md.decl.warn_unused = 0
-    md.decl.inline = inlinedefn
     for param in md.decl.params:
         if isinstance(param, Param):
             param.default = None
     return saveddecl, md
 
 
+def _splitFuncDeclDefn(fun):
+    assert not fun.decl.inline
+    return StmtDecl(fun.decl), fun
+
+
 # XXX this is tantalizingly similar to _splitClassDeclDefn, but just
 # different enough that I don't see the need to define
 # _GenerateSkeleton in terms of that
 class _GenerateSkeletonImpl(Visitor):
     def __init__(self, name, namespaces):
         self.name = name
         self.cls = None
         self.namespaces = namespaces
--- a/ipc/ipdl/ipdl/parser.py
+++ b/ipc/ipdl/ipdl/parser.py
@@ -150,16 +150,17 @@ reserved = set((
         '__delete__',
         'delete',                       # reserve 'delete' to prevent its use
         'goto',
         'include',
         'manager',
         'manages',
         'namespace',
         'nullable',
+        'opens',
         'or',
         'parent',
         'protocol',
         'recv',
         'returns',
         'rpc',
         'send',
         'spawns',
@@ -338,17 +339,17 @@ def p_ProtocolDefn(p):
     protocol.sendSemantics = p[1]
     p[0] = protocol
 
 def p_ProtocolBody(p):
     """ProtocolBody : SpawnsStmtsOpt"""
     p[0] = p[1]
 
 ##--------------------
-## spawns/bridges stmts
+## spawns/bridges/opens stmts
 
 def p_SpawnsStmtsOpt(p):
     """SpawnsStmtsOpt : SpawnsStmt SpawnsStmtsOpt
                       | BridgesStmtsOpt"""
     if 2 == len(p):
         p[0] = p[1]
     else:
         p[2].spawnsStmts.insert(0, p[1])
@@ -365,27 +366,41 @@ def p_AsOpt(p):
              | """
     if 3 == len(p):
         p[0] = p[2]
     else:
         p[0] = 'child'
 
 def p_BridgesStmtsOpt(p):
     """BridgesStmtsOpt : BridgesStmt BridgesStmtsOpt
-                       | ManagersStmtOpt"""
+                       | OpensStmtsOpt"""
     if 2 == len(p):
         p[0] = p[1]
     else:
         p[2].bridgesStmts.insert(0, p[1])
         p[0] = p[2]
 
 def p_BridgesStmt(p):
     """BridgesStmt : BRIDGES ID ',' ID ';'"""
     p[0] = BridgesStmt(locFromTok(p, 1), p[2], p[4])
 
+def p_OpensStmtsOpt(p):
+    """OpensStmtsOpt : OpensStmt OpensStmtsOpt
+                     | ManagersStmtOpt"""
+    if 2 == len(p):
+        p[0] = p[1]
+    else:
+        p[2].opensStmts.insert(0, p[1])
+        p[0] = p[2]
+
+def p_OpensStmt(p):
+    """OpensStmt : PARENT OPENS ID ';'
+                 | CHILD OPENS ID ';'"""
+    p[0] = OpensStmt(locFromTok(p, 1), p[1], p[3])
+
 ##--------------------
 ## manager/manages stmts
 
 def p_ManagersStmtOpt(p):
     """ManagersStmtOpt : ManagersStmt ManagesStmtsOpt
                        | ManagesStmtsOpt"""
     if 2 == len(p):
         p[0] = p[1]
--- a/ipc/ipdl/ipdl/type.py
+++ b/ipc/ipdl/ipdl/type.py
@@ -289,17 +289,17 @@ class Bridge:
     def __hash__(self):
         return hash(self.parent) + hash(self.child)
 
 class ProtocolType(IPDLType):
     def __init__(self, qname, sendSemantics, stateless=False):
         self.qname = qname
         self.sendSemantics = sendSemantics
         self.spawns = set()             # ProtocolType
-        self.bridges = set()            # [ Bridge ]
+        self.opens = set()              # ProtocolType
         self.managers = set()           # ProtocolType
         self.manages = [ ]
         self.stateless = stateless
         self.hasDelete = False
     def isProtocol(self): return True
 
     def name(self):
         return self.qname.baseid
@@ -309,18 +309,19 @@ class ProtocolType(IPDLType):
     def addManager(self, mgrtype):
         assert mgrtype.isIPDL() and mgrtype.isProtocol()
         self.managers.add(mgrtype)
 
     def addSpawn(self, ptype):
         assert self.isToplevel() and  ptype.isToplevel()
         self.spawns.add(ptype)
 
-    def addBridge(self, parentPType, childPType):
-        self.bridges.add(Bridge(parentPType, childPType))
+    def addOpen(self, ptype):
+        assert self.isToplevel() and  ptype.isToplevel()
+        self.opens.add(ptype)
 
     def managedBy(self, mgr):
         self.managers = mgr
 
     def toplevel(self):
         if self.isToplevel():
             return self
         for mgr in self.managers:
@@ -772,16 +773,19 @@ class GatherDecls(TcheckVisitor):
         self.symtab.enterScope(p)
 
         for spawns in p.spawnsStmts:
             spawns.accept(self)
 
         for bridges in p.bridgesStmts:
             bridges.accept(self)
 
+        for opens in p.opensStmts:
+            opens.accept(self)
+
         seenmgrs = set()
         for mgr in p.managers:
             if mgr.name in seenmgrs:
                 self.error(mgr.loc, "manager `%s' appears multiple times",
                            mgr.name)
                 continue
 
             seenmgrs.add(mgr.name)
@@ -922,16 +926,24 @@ class GatherDecls(TcheckVisitor):
             decl = self.symtab.lookup(p)
             if decl is None:
                 self.error(bridges.loc,
                            "bridged protocol `%s' has not been declared", p)
             return decl
         bridges.parentSide = lookup(bridges.parentSide)
         bridges.childSide = lookup(bridges.childSide)
 
+    def visitOpensStmt(self, opens):
+        pname = opens.proto
+        opens.proto = self.symtab.lookup(pname)
+        if opens.proto is None:
+            self.error(opens.loc,
+                       "opened protocol `%s' has not been declared",
+                       pname)
+
 
     def visitManager(self, mgr):
         mgrdecl = self.symtab.lookup(mgr.name)
         pdecl = mgr.of.decl
         assert pdecl
 
         pname, mgrname = pdecl.shortname, mgr.name
         loc = mgr.loc
@@ -1229,16 +1241,21 @@ class CheckTypes(TcheckVisitor):
                        "protocol `%s' is not top-level and so cannot declare |spawns|",
                        pname)
 
         if len(p.bridgesStmts) and not ptype.isToplevel():
             self.error(p.decl.loc,
                        "protocol `%s' is not top-level and so cannot declare |bridges|",
                        pname)
 
+        if len(p.opensStmts) and not ptype.isToplevel():
+            self.error(p.decl.loc,
+                       "protocol `%s' is not top-level and so cannot declare |opens|",
+                       pname)
+
         for mgrtype in ptype.managers:
             if mgrtype is not None and ptype.needsMoreJuiceThan(mgrtype):
                 self.error(
                     p.decl.loc,
                     "protocol `%s' requires more powerful send semantics than its manager `%s' provides",
                     pname, mgrtype.name())
 
         # XXX currently we don't require a delete() message of top-level
@@ -1295,18 +1312,33 @@ class CheckTypes(TcheckVisitor):
         parentType = bridges.parentSide.type
         childType = bridges.childSide.type
         if not (parentType.isIPDL() and parentType.isProtocol()
                 and childType.isIPDL() and childType.isProtocol()
                 and parentType.isToplevel() and childType.isToplevel()):
             self.error(bridges.loc,
                        "cannot bridge non-top-level-protocol(s) `%s' and `%s'",
                        parentType.name(), childType.name())
+
+
+    def visitOpensStmt(self, opens):
+        if not self.ptype.isToplevel():
+            self.error(opens.loc,
+                       "only top-level protocols can have |opens| statements; `%s' cannot",
+                       self.ptype.name())
+            return
+
+        openedType = opens.proto.type
+        if not (openedType.isIPDL() and openedType.isProtocol()
+                and openedType.isToplevel()):
+            self.error(opens.loc,
+                       "cannot open non-top-level-protocol `%s'",
+                       openedType.name())
         else:
-            self.ptype.addBridge(parentType, childType)
+            self.ptype.addOpen(openedType)
 
 
     def visitManagesStmt(self, mgs):
         pdecl = mgs.manager.decl
         ptype, pname = pdecl.type, pdecl.shortname
 
         mgsdecl = mgs.decl
         mgstype, mgsname = mgsdecl.type, mgsdecl.shortname
@@ -1437,16 +1469,18 @@ class Process:
         return reduce(lambda a, x: str(a) + str(x) +'|', self.actors, '|')
     def __str__(self):     return repr(self)
 
 class Actor:
     def __init__(self, ptype, side):
         self.ptype = ptype
         self.side = side
 
+    def asType(self):
+        return ActorType(self.ptype)
     def other(self):
         return Actor(self.ptype, _otherside(self.side))
 
     def __cmp__(self, o):
         return cmp(self.ptype, o.ptype) or cmp(self.side, o.side)
     def __eq__(self, o):
         return self.ptype == o.ptype and self.side == o.side
     def __hash__(self):  return hash(repr(self))
@@ -1466,21 +1500,30 @@ class BridgeEdge:
         self.bridgeProto = bridgeProto # ProtocolType
         self.parent = parent           # Actor
         self.child = child             # Actor
     def __repr__(self):
         return '(%r)--%s bridge-->(%r)'% (
             self.parent, self.bridgeProto.name(), self.child)
     def __str__(self):  return repr(self)
 
+class OpensEdge:
+    def __init__(self, opener, openedProto):
+        self.opener = opener            # Actor
+        self.openedProto = openedProto  # ProtocolType
+    def __repr__(self):
+        return '(%r)--opens-->(%s)'% (self.opener, self.openedProto.name())
+    def __str__(self):  return repr(self)
+
 # "singleton" class with state that persists across type checking of
 # all protocols
 class ProcessGraph:
     processes = set()                   # set(Process)
-    bridges = { }                       # ProtocolType -> BridgeEdge
+    bridges = { }                       # ProtocolType -> [ BridgeEdge ]
+    opens = { }                         # ProtocolType -> [ OpensEdge ]
     actorToProcess = { }                # Actor -> Process
     visitedSpawns = set()               # set(ActorType)
     visitedBridges = set()              # set(ActorType)
 
     @classmethod
     def findProcess(cls, actor):
         return cls.actorToProcess.get(actor, None)
 
@@ -1489,25 +1532,86 @@ class ProcessGraph:
         if actor not in cls.actorToProcess:
             p = Process()
             p.actors.add(actor)
             cls.processes.add(p)
             cls.actorToProcess[actor] = p
         return cls.actorToProcess[actor]
 
     @classmethod
+    def bridgesOf(cls, bridgeP):
+        return cls.bridges.get(bridgeP, [])
+
+    @classmethod
+    def bridgeEndpointsOf(cls, ptype, side):
+        actor = Actor(ptype, side)
+        endpoints = []
+        for b in cls.iterbridges():
+            if b.parent == actor:
+                endpoints.append(Actor(b.bridgeProto, 'parent'))
+            elif b.child == actor:
+                endpoints.append(Actor(b.bridgeProto, 'child'))
+        return endpoints
+
+    @classmethod
+    def iterbridges(cls):
+        for edges in cls.bridges.itervalues():
+            for bridge in edges:
+                yield bridge
+
+    @classmethod
+    def opensOf(cls, openedP):
+        return cls.opens.get(openedP, [])
+
+    @classmethod
+    def opensEndpointsOf(cls, ptype, side):
+        actor = Actor(ptype, side)
+        endpoints = []
+        for o in cls.iteropens():
+            if actor == o.opener:
+                endpoints.append(Actor(o.openedProto, o.opener.side))
+            elif actor == o.opener.other():
+                endpoints.append(Actor(o.openedProto, o.opener.other().side))
+        return endpoints
+
+    @classmethod
+    def iteropens(cls):
+        for edges in cls.opens.itervalues():
+            for opens in edges:
+                yield opens
+
+    @classmethod
     def spawn(cls, spawner, remoteSpawn):
         localSpawn = remoteSpawn.other()
         spawnerProcess = ProcessGraph.getProcess(spawner)
         spawnerProcess.merge(ProcessGraph.getProcess(localSpawn))
         spawnerProcess.edge(spawner, remoteSpawn)
 
     @classmethod
     def bridge(cls, parent, child, bridgeP):
-        cls.bridges[bridgeP] = BridgeEdge(bridgeP, parent, child)
+        bridgeParent = Actor(bridgeP, 'parent')
+        parentProcess = ProcessGraph.getProcess(parent)
+        parentProcess.merge(ProcessGraph.getProcess(bridgeParent))
+        bridgeChild = Actor(bridgeP, 'child')
+        childProcess = ProcessGraph.getProcess(child)
+        childProcess.merge(ProcessGraph.getProcess(bridgeChild))
+        if bridgeP not in cls.bridges:
+            cls.bridges[bridgeP] = [ ]
+        cls.bridges[bridgeP].append(BridgeEdge(bridgeP, parent, child))
+
+    @classmethod
+    def open(cls, opener, opened, openedP):
+        remoteOpener, remoteOpened, = opener.other(), opened.other()
+        openerProcess = ProcessGraph.getProcess(opener)
+        openerProcess.merge(ProcessGraph.getProcess(opened))
+        remoteOpenerProcess = ProcessGraph.getProcess(remoteOpener)
+        remoteOpenerProcess.merge(ProcessGraph.getProcess(remoteOpened))
+        if openedP not in cls.opens:
+            cls.opens[openedP] = [ ]
+        cls.opens[openedP].append(OpensEdge(opener, openedP))
 
 
 class BuildProcessGraph(TcheckVisitor):
     class findSpawns(TcheckVisitor):
         def __init__(self, errors):
             TcheckVisitor.__init__(self, None, errors)
 
         def visitTranslationUnit(self, tu):
@@ -1605,33 +1709,54 @@ class BuildProcessGraph(TcheckVisitor):
         if parentSideActor is None:
             self.error(bridges.loc,
                        "`%s' and `%s' cannot be bridged by `%s' ",
                        parentSideProto.name(), childSideProto.name(),
                        bridgeProto.name())
 
         ProcessGraph.bridge(parentSideActor, childSideActor, bridgeProto)
 
+    def visitOpensStmt(self, opens):
+        openedP = opens.proto.type
+        opener = Actor(self.visiting, opens.side)
+        opened = Actor(openedP, opens.side)
+
+        # The picture here is:
+        #  [ opener       | opened ]   (process 1)
+        #      |               |
+        #      |               |
+        #  [ remoteOpener | remoteOpened ]  (process 2)
+        #
+        # An opens stmt tells us that the pairs |opener|/|opened|
+        # and |remoteOpener|/|remoteOpened| are each in the same
+        # process.
+        ProcessGraph.open(opener, opened, openedP)
+
 
 class CheckProcessGraph(TcheckVisitor):
     def __init__(self, errors):
         TcheckVisitor.__init__(self, None, errors)
 
     # TODO: verify spawns-per-process assumption and check that graph
     # is a dag
     def visitTranslationUnit(self, tu):
         if 0:
             print 'Processes'
             for process in ProcessGraph.processes:
                 print '  ', process
                 for edge in process.iteredges():
                     print '    ', edge
             print 'Bridges'
-            for bridge in ProcessGraph.bridges.itervalues():
-                print '  ', bridge
+            for bridgeList in ProcessGraph.bridges.itervalues():
+                for bridge in bridgeList:
+                    print '  ', bridge
+            print 'Opens'
+            for opensList in ProcessGraph.opens.itervalues():
+                for opens in opensList:
+                    print '  ', opens
 
 ##-----------------------------------------------------------------------------
 
 class CheckStateMachine(TcheckVisitor):
     def __init__(self, errors):
         # don't need the symbol table, we just want the error reporting
         TcheckVisitor.__init__(self, None, errors)
         self.p = None
--- a/ipc/ipdl/test/cxx/Makefile.in
+++ b/ipc/ipdl/test/cxx/Makefile.in
@@ -54,26 +54,28 @@ EXPORTS_mozilla/_ipdltest =  \
 
 LIBRARY_NAME = $(MODULE)_s
 LIBXUL_LIBRARY = 1
 FORCE_STATIC_LIB = 1
 EXPORT_LIBRARY = 1
 
 IPDLTESTS = \
   TestBlockChild \
+  TestBridgeMain \
   TestCrashCleanup \
   TestDataStructures \
   TestDesc \
   TestFailedCtor \
   TestHangs \
   TestJSON \
   TestLatency \
   TestManyChildAllocs  \
   TestMultiMgrs  \
   TestNestedLoops \
+  TestOpens \
   TestRPCErrorCleanup \
   TestRPCRaces \
   TestRPCShutdownRace \
   TestRaceDeferral \
   TestRacyReentry  \
   TestRacyRPCReplies  \
   TestRacyUndefer \
   TestSanity  \
@@ -84,16 +86,20 @@ IPDLTESTS = \
   TestSyncWakeup \
   TestSyncHang \
   $(NULL)
 
 ifeq ($(OS_ARCH),Linux)
 IPDLTESTS += TestSysVShmem
 endif
 
+EXTRA_PROTOCOLS = \
+  TestBridgeSub \
+  $(NULL)
+
 IPDLTESTSRCS = $(addsuffix .cpp,$(IPDLTESTS))
 IPDLTESTHDRS = $(addprefix $(srcdir)/,$(addsuffix .h,$(IPDLTESTS)))
 
 TESTER_TEMPLATE := $(srcdir)/IPDLUnitTests.template.cpp
 GENTESTER := $(srcdir)/genIPDLUnitTests.py
 
 CPPSRCS =  \
   IPDLUnitTests.cpp  \
@@ -105,17 +111,17 @@ CPPSRCS =  \
 include $(topsrcdir)/config/config.mk
 include $(topsrcdir)/ipc/chromium/chromium-config.mk
 include $(topsrcdir)/config/rules.mk
 
 
 IPDLUNITTEST_BIN = $(DEPTH)/dist/bin/ipdlunittest$(BIN_SUFFIX)
 
 IPDLUnitTests.cpp : Makefile.in $(GENTESTER) $(TESTER_TEMPLATE) $(IPDLTESTHDRS)
-	$(PYTHON) $(GENTESTER) $(TESTER_TEMPLATE) $(IPDLTESTS) > $@
+	$(PYTHON) $(GENTESTER) $(TESTER_TEMPLATE) -t $(IPDLTESTS) -e $(EXTRA_PROTOCOLS) > $@
 
 check::
 	@$(EXIT_ON_ERROR)  \
 	for test in $(IPDLTESTS); do  \
 		 $(RUN_TEST_PROGRAM) $(IPDLUNITTEST_BIN) $$test ;  \
 	done
 
 check-valgrind::
new file mode 100644
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestBridgeMain.ipdl
@@ -0,0 +1,24 @@
+include protocol PTestBridgeSub;
+
+namespace mozilla {
+namespace _ipdltest {
+
+
+protocol PTestBridgeMain {
+    child spawns PTestBridgeSub;
+
+child:
+    Start();
+
+parent:
+    __delete__();
+
+state START:
+    send Start goto DEAD;
+state DEAD:
+    recv __delete__;
+};
+
+
+} // namespace mozilla
+} // namespace _ipdltest
new file mode 100644
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestBridgeMainSub.ipdl
@@ -0,0 +1,33 @@
+include protocol PTestBridgeMain;
+include protocol PTestBridgeSub;
+
+namespace mozilla {
+namespace _ipdltest {
+
+// (Bridge protocols can have different semantics than the endpoints
+// they bridge)
+rpc protocol PTestBridgeMainSub {
+    bridges PTestBridgeMain, PTestBridgeSub;
+
+child:
+    Hi();
+    rpc HiRpc();
+
+parent:
+    Hello();
+    sync HelloSync();
+    rpc HelloRpc();
+    __delete__();
+
+state START:       recv Hello goto HI;
+state HI:          send Hi goto HELLO_SYNC;
+state HELLO_SYNC:  recv HelloSync goto HELLO_RPC;
+state HELLO_RPC:   answer HelloRpc goto HI_RPC;
+state HI_RPC:      call HiRpc goto DEAD;
+state DEAD:
+    recv __delete__;
+};
+
+
+} // namespace mozilla
+} // namespace _ipdltest
new file mode 100644
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestBridgeSub.ipdl
@@ -0,0 +1,24 @@
+
+namespace mozilla {
+namespace _ipdltest {
+
+
+protocol PTestBridgeSub {
+child:
+    Ping();
+
+parent:
+    BridgeEm();
+    __delete__();
+
+state START:
+    send Ping goto BRIDGEEM;
+state BRIDGEEM:
+    recv BridgeEm goto DEAD;
+state DEAD:
+    recv __delete__;
+};
+
+
+} // namespace mozilla
+} // namespace _ipdltest
new file mode 100644
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestOpens.ipdl
@@ -0,0 +1,25 @@
+include protocol PTestOpensOpened;
+
+namespace mozilla {
+namespace _ipdltest {
+
+
+protocol PTestOpens {
+    // This channel is opened and parked on a non-main thread
+    child opens PTestOpensOpened;
+
+child:
+    Start();
+
+parent:
+    __delete__();
+
+state START:
+    send Start goto DEAD;
+state DEAD:
+    recv __delete__;
+};
+
+
+} // namespace mozilla
+} // namespace _ipdltest
new file mode 100644
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestOpensOpened.ipdl
@@ -0,0 +1,28 @@
+namespace mozilla {
+namespace _ipdltest {
+
+// (Opens protocols can have different semantics than the endpoints
+// that opened them)
+rpc protocol PTestOpensOpened {
+child:
+    Hi();
+    rpc HiRpc();
+
+parent:
+    Hello();
+    sync HelloSync();
+    rpc HelloRpc();
+    __delete__();
+
+state START:       recv Hello goto HI;
+state HI:          send Hi goto HELLO_SYNC;
+state HELLO_SYNC:  recv HelloSync goto HELLO_RPC;
+state HELLO_RPC:   answer HelloRpc goto HI_RPC;
+state HI_RPC:      call HiRpc goto DEAD;
+state DEAD:
+    recv __delete__;
+};
+
+
+} // namespace mozilla
+} // namespace _ipdltest
new file mode 100644
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestBridgeMain.cpp
@@ -0,0 +1,252 @@
+#include "TestBridgeMain.h"
+
+#include "IPDLUnitTests.h"      // fail etc.
+#include "IPDLUnitTestSubprocess.h"
+
+using namespace std;
+
+template<>
+struct RunnableMethodTraits<mozilla::_ipdltest::TestBridgeMainSubChild>
+{
+    static void RetainCallee(mozilla::_ipdltest::TestBridgeMainSubChild* obj) { }
+    static void ReleaseCallee(mozilla::_ipdltest::TestBridgeMainSubChild* obj) { }
+};
+
+namespace mozilla {
+namespace _ipdltest {
+
+
+//-----------------------------------------------------------------------------
+// main process
+void
+TestBridgeMainParent::Main()
+{
+    if (!SendStart())
+        fail("sending Start");
+}
+
+PTestBridgeMainSubParent*
+TestBridgeMainParent::AllocPTestBridgeMainSub(Transport* transport,
+                                              ProcessId otherProcess)
+{
+    ProcessHandle h;
+    if (!base::OpenProcessHandle(otherProcess, &h)) {
+        return nsnull;
+    }
+
+    nsAutoPtr<TestBridgeMainSubParent> a(new TestBridgeMainSubParent(transport));
+    if (!a->Open(transport, h, XRE_GetIOMessageLoop(), AsyncChannel::Parent)) {
+        return nsnull;
+    }
+    return a.forget();
+}
+
+void
+TestBridgeMainParent::ActorDestroy(ActorDestroyReason why)
+{
+    if (NormalShutdown != why)
+        fail("unexpected destruction!");  
+    passed("ok");
+    QuitParent();
+}
+
+bool
+TestBridgeMainSubParent::RecvHello()
+{
+    return SendHi();
+}
+
+bool
+TestBridgeMainSubParent::RecvHelloSync()
+{
+    return true;
+}
+
+bool
+TestBridgeMainSubParent::AnswerHelloRpc()
+{
+    return CallHiRpc();
+}
+
+void
+TestBridgeMainSubParent::ActorDestroy(ActorDestroyReason why)
+{
+    if (NormalShutdown != why)
+        fail("unexpected destruction!");
+
+    // ActorDestroy() is just a callback from IPDL-generated code,
+    // which needs the top-level actor (this) to stay alive a little
+    // longer so other things can be cleaned up.
+    MessageLoop::current()->PostTask(
+        FROM_HERE,
+        new DeleteTask<TestBridgeMainSubParent>(this));
+    XRE_GetIOMessageLoop()->PostTask(
+        FROM_HERE,
+        new DeleteTask<Transport>(mTransport));
+}
+
+//-----------------------------------------------------------------------------
+// sub process --- child of main
+TestBridgeMainChild* gBridgeMainChild;
+
+TestBridgeMainChild::TestBridgeMainChild()
+    : mSubprocess(nsnull)
+{
+    gBridgeMainChild = this;
+}
+
+bool
+TestBridgeMainChild::RecvStart()
+{
+    vector<string> subsubArgs;
+    subsubArgs.push_back("TestBridgeSub");
+
+    mSubprocess = new IPDLUnitTestSubprocess();
+    if (!mSubprocess->SyncLaunch(subsubArgs))
+        fail("problem launching subprocess");
+
+    IPC::Channel* transport = mSubprocess->GetChannel();
+    if (!transport)
+        fail("no transport");
+
+    TestBridgeSubParent* bsp = new TestBridgeSubParent();
+    bsp->Open(transport, mSubprocess->GetChildProcessHandle());
+
+    bsp->Main();
+    return true;
+}
+
+void
+TestBridgeMainChild::ActorDestroy(ActorDestroyReason why)
+{
+    if (NormalShutdown != why)
+        fail("unexpected destruction!");  
+    // NB: this is kosher because QuitChild() joins with the IO thread
+    XRE_GetIOMessageLoop()->PostTask(
+        FROM_HERE,
+        new DeleteTask<IPDLUnitTestSubprocess>(mSubprocess));
+    QuitChild();
+}
+
+void
+TestBridgeSubParent::Main()
+{
+    if (!SendPing())
+        fail("sending Ping");
+}
+
+bool
+TestBridgeSubParent::RecvBridgeEm()
+{
+    if (!PTestBridgeMainSub::Bridge(gBridgeMainChild, this))
+        fail("bridging Main and Sub");
+    return true;
+}
+
+void
+TestBridgeSubParent::ActorDestroy(ActorDestroyReason why)
+{
+    if (NormalShutdown != why)
+        fail("unexpected destruction!");
+    gBridgeMainChild->Close();
+
+    // ActorDestroy() is just a callback from IPDL-generated code,
+    // which needs the top-level actor (this) to stay alive a little
+    // longer so other things can be cleaned up.
+    MessageLoop::current()->PostTask(
+        FROM_HERE,
+        new DeleteTask<TestBridgeSubParent>(this));
+}
+
+//-----------------------------------------------------------------------------
+// subsub process --- child of sub
+
+static TestBridgeSubChild* gBridgeSubChild;
+
+TestBridgeSubChild::TestBridgeSubChild()
+{
+    gBridgeSubChild = this;   
+}
+
+bool
+TestBridgeSubChild::RecvPing()
+{
+    if (!SendBridgeEm())
+        fail("sending BridgeEm");
+    return true;
+}
+
+PTestBridgeMainSubChild*
+TestBridgeSubChild::AllocPTestBridgeMainSub(Transport* transport,
+                                            ProcessId otherProcess)
+{
+    ProcessHandle h;
+    if (!base::OpenProcessHandle(otherProcess, &h)) {
+        return nsnull;
+    }
+
+    nsAutoPtr<TestBridgeMainSubChild> a(new TestBridgeMainSubChild(transport));
+    if (!a->Open(transport, h, XRE_GetIOMessageLoop(), AsyncChannel::Child)) {
+        return nsnull;
+    }
+
+    if (!a->SendHello())
+        fail("sending Hello");
+
+    return a.forget();
+}
+
+void
+TestBridgeSubChild::ActorDestroy(ActorDestroyReason why)
+{
+    if (NormalShutdown != why)
+        fail("unexpected destruction!");
+    QuitChild();
+}
+
+bool
+TestBridgeMainSubChild::RecvHi()
+{
+    if (!SendHelloSync())
+        fail("sending HelloSync");
+    if (!CallHelloRpc())
+        fail("calling HelloRpc");
+    if (!mGotHi)
+        fail("didn't answer HiRpc");
+
+    // Need to close the channel without message-processing frames on
+    // the C++ stack
+    MessageLoop::current()->PostTask(
+        FROM_HERE,
+        NewRunnableMethod(this, &TestBridgeMainSubChild::Close));
+    return true;
+}
+
+bool
+TestBridgeMainSubChild::AnswerHiRpc()
+{
+    mGotHi = true;              // d00d
+    return true;
+}
+
+void
+TestBridgeMainSubChild::ActorDestroy(ActorDestroyReason why)
+{
+    if (NormalShutdown != why)
+        fail("unexpected destruction!");  
+
+    gBridgeSubChild->Close();
+
+    // ActorDestroy() is just a callback from IPDL-generated code,
+    // which needs the top-level actor (this) to stay alive a little
+    // longer so other things can be cleaned up.
+    MessageLoop::current()->PostTask(
+        FROM_HERE,
+        new DeleteTask<TestBridgeMainSubChild>(this));
+    XRE_GetIOMessageLoop()->PostTask(
+        FROM_HERE,
+        new DeleteTask<Transport>(mTransport));
+}
+
+} // namespace mozilla
+} // namespace _ipdltest
new file mode 100644
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestBridgeMain.h
@@ -0,0 +1,152 @@
+#ifndef mozilla__ipdltest_TestBridgeMain_h
+#define mozilla__ipdltest_TestBridgeMain_h 1
+
+#include "mozilla/_ipdltest/IPDLUnitTests.h"
+
+#include "mozilla/_ipdltest/PTestBridgeMainParent.h"
+#include "mozilla/_ipdltest/PTestBridgeMainChild.h"
+
+#include "mozilla/_ipdltest/PTestBridgeSubParent.h"
+#include "mozilla/_ipdltest/PTestBridgeSubChild.h"
+
+#include "mozilla/_ipdltest/PTestBridgeMainSubParent.h"
+#include "mozilla/_ipdltest/PTestBridgeMainSubChild.h"
+
+namespace mozilla {
+namespace _ipdltest {
+
+//-----------------------------------------------------------------------------
+// "Main" process
+//
+class TestBridgeMainParent :
+    public PTestBridgeMainParent
+{
+public:
+    TestBridgeMainParent() {}
+    virtual ~TestBridgeMainParent() {}
+
+    void Main();
+
+protected:
+    NS_OVERRIDE
+    virtual PTestBridgeMainSubParent*
+    AllocPTestBridgeMainSub(Transport* transport,
+                            ProcessId otherProcess);
+
+    NS_OVERRIDE
+    virtual void ActorDestroy(ActorDestroyReason why);
+};
+
+class TestBridgeMainSubParent :
+    public PTestBridgeMainSubParent
+{
+public:
+    TestBridgeMainSubParent(Transport* aTransport)
+        : mTransport(aTransport)
+    {}
+    virtual ~TestBridgeMainSubParent() {}
+
+protected:
+    NS_OVERRIDE
+    virtual bool RecvHello();
+    NS_OVERRIDE
+    virtual bool RecvHelloSync();
+    NS_OVERRIDE
+    virtual bool AnswerHelloRpc();
+
+    NS_OVERRIDE
+    virtual void ActorDestroy(ActorDestroyReason why);
+
+    Transport* mTransport;
+};
+
+//-----------------------------------------------------------------------------
+// "Sub" process --- child of "main"
+//
+class TestBridgeSubParent;
+
+class TestBridgeMainChild :
+    public PTestBridgeMainChild
+{
+public:
+    TestBridgeMainChild();
+    virtual ~TestBridgeMainChild() {}
+
+protected:
+    NS_OVERRIDE
+    virtual bool RecvStart();
+
+    NS_OVERRIDE
+    virtual void ActorDestroy(ActorDestroyReason why);
+
+    IPDLUnitTestSubprocess* mSubprocess;
+};
+
+class TestBridgeSubParent :
+    public PTestBridgeSubParent
+{
+public:
+    TestBridgeSubParent() {}
+    virtual ~TestBridgeSubParent() {}
+
+    void Main();
+
+protected:
+    NS_OVERRIDE
+    virtual bool RecvBridgeEm();
+
+    NS_OVERRIDE
+    virtual void ActorDestroy(ActorDestroyReason why);
+};
+
+//-----------------------------------------------------------------------------
+// "Subsub" process --- child of "sub"
+//
+class TestBridgeSubChild :
+    public PTestBridgeSubChild
+{
+public:
+    TestBridgeSubChild();
+    virtual ~TestBridgeSubChild() {}
+
+protected:
+    NS_OVERRIDE
+    virtual bool RecvPing();
+
+    NS_OVERRIDE
+    virtual PTestBridgeMainSubChild*
+    AllocPTestBridgeMainSub(Transport* transport,
+                            ProcessId otherProcess);
+
+    NS_OVERRIDE
+    virtual void ActorDestroy(ActorDestroyReason why);
+};
+
+class TestBridgeMainSubChild :
+    public PTestBridgeMainSubChild
+{
+public:
+    TestBridgeMainSubChild(Transport* aTransport)
+        : mGotHi(false)
+        , mTransport(aTransport)
+    {}
+    virtual ~TestBridgeMainSubChild() {}
+
+protected:
+    NS_OVERRIDE
+    virtual bool RecvHi();
+    NS_OVERRIDE
+    virtual bool AnswerHiRpc();
+
+    NS_OVERRIDE
+    virtual void ActorDestroy(ActorDestroyReason why);
+
+    bool mGotHi;
+    Transport* mTransport;
+};
+
+} // namespace _ipdltest
+} // namespace mozilla
+
+
+#endif // ifndef mozilla__ipdltest_TestBridgeMain_h
new file mode 100644
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestOpens.cpp
@@ -0,0 +1,266 @@
+#include "base/thread.h"
+
+#include "TestOpens.h"
+
+#include "IPDLUnitTests.h"      // fail etc.
+
+template<>
+struct RunnableMethodTraits<mozilla::_ipdltest::TestOpensChild>
+{
+    static void RetainCallee(mozilla::_ipdltest::TestOpensChild* obj) { }
+    static void ReleaseCallee(mozilla::_ipdltest::TestOpensChild* obj) { }
+};
+
+template<>
+struct RunnableMethodTraits<mozilla::_ipdltest::TestOpensOpenedChild>
+{
+    static void RetainCallee(mozilla::_ipdltest::TestOpensOpenedChild* obj) { }
+    static void ReleaseCallee(mozilla::_ipdltest::TestOpensOpenedChild* obj) { }
+};
+
+using namespace base;
+using namespace mozilla::ipc;
+
+namespace mozilla {
+namespace _ipdltest {
+
+static MessageLoop* gMainThread;
+
+static void
+AssertNotMainThread()
+{
+    if (!gMainThread)
+        fail("gMainThread is not initialized");
+    if (MessageLoop::current() == gMainThread)
+        fail("unexpectedly called on the main thread");
+}
+
+//-----------------------------------------------------------------------------
+// parent
+
+// Thread on which TestOpensOpenedParent runs
+static Thread* gParentThread;
+
+void
+TestOpensParent::Main()
+{
+    if (!SendStart())
+        fail("sending Start");
+}
+
+static void
+OpenParent(TestOpensOpenedParent* aParent,
+           Transport* aTransport, ProcessHandle aOtherProcess)
+{
+    AssertNotMainThread();
+
+    // Open the actor on the off-main thread to park it there.
+    // Messages will be delivered to this thread's message loop
+    // instead of the main thread's.
+    if (!aParent->Open(aTransport, aOtherProcess,
+                       XRE_GetIOMessageLoop(), AsyncChannel::Parent))
+        fail("opening Parent");
+}
+
+PTestOpensOpenedParent*
+TestOpensParent::AllocPTestOpensOpened(Transport* transport,
+                                       ProcessId otherProcess)
+{
+    gMainThread = MessageLoop::current();
+
+    ProcessHandle h;
+    if (!base::OpenProcessHandle(otherProcess, &h)) {
+        return nsnull;
+    }
+
+    gParentThread = new Thread("ParentThread");
+    if (!gParentThread->Start())
+        fail("starting parent thread");
+
+    TestOpensOpenedParent* a = new TestOpensOpenedParent(transport);
+    gParentThread->message_loop()->PostTask(
+        FROM_HERE,
+        NewRunnableFunction(OpenParent, a, transport, h));
+
+    return a;
+}
+
+void
+TestOpensParent::ActorDestroy(ActorDestroyReason why)
+{
+    // Stops the thread and joins it
+    delete gParentThread;
+
+    if (NormalShutdown != why)
+        fail("unexpected destruction!");  
+    passed("ok");
+    QuitParent();
+}
+
+bool
+TestOpensOpenedParent::RecvHello()
+{
+    AssertNotMainThread();
+    return SendHi();
+}
+
+bool
+TestOpensOpenedParent::RecvHelloSync()
+{
+    AssertNotMainThread();
+    return true;
+}
+
+bool
+TestOpensOpenedParent::AnswerHelloRpc()
+{
+    AssertNotMainThread();
+    return CallHiRpc();
+}
+
+void
+TestOpensOpenedParent::ActorDestroy(ActorDestroyReason why)
+{
+    AssertNotMainThread();
+
+    if (NormalShutdown != why)
+        fail("unexpected destruction!");
+
+    // ActorDestroy() is just a callback from IPDL-generated code,
+    // which needs the top-level actor (this) to stay alive a little
+    // longer so other things can be cleaned up.
+    MessageLoop::current()->PostTask(
+        FROM_HERE,
+        new DeleteTask<TestOpensOpenedParent>(this));
+    XRE_GetIOMessageLoop()->PostTask(
+        FROM_HERE,
+        new DeleteTask<Transport>(mTransport));
+}
+
+//-----------------------------------------------------------------------------
+// child
+
+static TestOpensChild* gOpensChild;
+// Thread on which TestOpensOpenedChild runs
+static Thread* gChildThread;
+
+TestOpensChild::TestOpensChild()
+{
+    gOpensChild = this;
+}
+
+bool
+TestOpensChild::RecvStart()
+{
+    if (!PTestOpensOpened::Open(this))
+        fail("opening PTestOpensOpened");
+    return true;
+}
+
+static void
+OpenChild(TestOpensOpenedChild* aChild,
+           Transport* aTransport, ProcessHandle aOtherProcess)
+{
+    AssertNotMainThread();
+
+    // Open the actor on the off-main thread to park it there.
+    // Messages will be delivered to this thread's message loop
+    // instead of the main thread's.
+    if (!aChild->Open(aTransport, aOtherProcess,
+                      XRE_GetIOMessageLoop(), AsyncChannel::Child))
+        fail("opening Child");
+
+    // Kick off the unit tests
+    if (!aChild->SendHello())
+        fail("sending Hello");
+}
+
+PTestOpensOpenedChild*
+TestOpensChild::AllocPTestOpensOpened(Transport* transport,
+                                      ProcessId otherProcess)
+{
+    gMainThread = MessageLoop::current();
+
+    ProcessHandle h;
+    if (!base::OpenProcessHandle(otherProcess, &h)) {
+        return nsnull;
+    }
+
+    gChildThread = new Thread("ChildThread");
+    if (!gChildThread->Start())
+        fail("starting child thread");
+
+    TestOpensOpenedChild* a = new TestOpensOpenedChild(transport);
+    gChildThread->message_loop()->PostTask(
+        FROM_HERE,
+        NewRunnableFunction(OpenChild, a, transport, h));
+
+    return a;
+}
+
+void
+TestOpensChild::ActorDestroy(ActorDestroyReason why)
+{
+    // Stops the thread and joins it
+    delete gChildThread;
+
+    if (NormalShutdown != why)
+        fail("unexpected destruction!");
+    QuitChild();
+}
+
+bool
+TestOpensOpenedChild::RecvHi()
+{
+    AssertNotMainThread();
+
+    if (!SendHelloSync())
+        fail("sending HelloSync");
+    if (!CallHelloRpc())
+        fail("calling HelloRpc");
+    if (!mGotHi)
+        fail("didn't answer HiRpc");
+
+    // Need to close the channel without message-processing frames on
+    // the C++ stack
+    MessageLoop::current()->PostTask(
+        FROM_HERE,
+        NewRunnableMethod(this, &TestOpensOpenedChild::Close));
+    return true;
+}
+
+bool
+TestOpensOpenedChild::AnswerHiRpc()
+{
+    AssertNotMainThread();
+
+    mGotHi = true;              // d00d
+    return true;
+}
+
+void
+TestOpensOpenedChild::ActorDestroy(ActorDestroyReason why)
+{
+    AssertNotMainThread();
+
+    if (NormalShutdown != why)
+        fail("unexpected destruction!");  
+
+    // ActorDestroy() is just a callback from IPDL-generated code,
+    // which needs the top-level actor (this) to stay alive a little
+    // longer so other things can be cleaned up.
+    MessageLoop::current()->PostTask(
+        FROM_HERE,
+        new DeleteTask<TestOpensOpenedChild>(this));
+    XRE_GetIOMessageLoop()->PostTask(
+        FROM_HERE,
+        new DeleteTask<Transport>(mTransport));
+
+    // Kick off main-thread shutdown.
+    gMainThread->PostTask(
+        FROM_HERE,
+        NewRunnableMethod(gOpensChild, &TestOpensChild::Close));
+}
+
+} // namespace _ipdltest
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestOpens.h
@@ -0,0 +1,104 @@
+#ifndef mozilla__ipdltest_TestOpens_h
+#define mozilla__ipdltest_TestOpens_h 1
+
+#include "mozilla/_ipdltest/IPDLUnitTests.h"
+
+#include "mozilla/_ipdltest/PTestOpensParent.h"
+#include "mozilla/_ipdltest/PTestOpensChild.h"
+
+#include "mozilla/_ipdltest/PTestOpensOpenedParent.h"
+#include "mozilla/_ipdltest/PTestOpensOpenedChild.h"
+
+namespace mozilla {
+namespace _ipdltest {
+
+// parent process
+
+class TestOpensParent : public PTestOpensParent
+{
+public:
+    TestOpensParent() {}
+    virtual ~TestOpensParent() {}
+
+    void Main();
+
+protected:
+    NS_OVERRIDE
+    virtual PTestOpensOpenedParent*
+    AllocPTestOpensOpened(Transport* transport, ProcessId otherProcess);
+
+    NS_OVERRIDE
+    virtual void ActorDestroy(ActorDestroyReason why);
+};
+
+class TestOpensOpenedParent : public PTestOpensOpenedParent
+{
+public:
+    TestOpensOpenedParent(Transport* aTransport)
+        : mTransport(aTransport)
+    {}
+    virtual ~TestOpensOpenedParent() {}
+
+protected:
+    NS_OVERRIDE
+    virtual bool RecvHello();
+    NS_OVERRIDE
+    virtual bool RecvHelloSync();
+    NS_OVERRIDE
+    virtual bool AnswerHelloRpc();
+
+    NS_OVERRIDE
+    virtual void ActorDestroy(ActorDestroyReason why);
+
+    Transport* mTransport;
+};
+
+
+// child process
+
+class TestOpensChild : public PTestOpensChild
+{
+public:
+    TestOpensChild();
+    virtual ~TestOpensChild() {}
+
+protected:
+    NS_OVERRIDE
+    virtual bool RecvStart();
+
+    NS_OVERRIDE
+    virtual PTestOpensOpenedChild*
+    AllocPTestOpensOpened(Transport* transport, ProcessId otherProcess);
+
+    NS_OVERRIDE
+    virtual void ActorDestroy(ActorDestroyReason why);
+};
+
+class TestOpensOpenedChild : public PTestOpensOpenedChild
+{
+public:
+    TestOpensOpenedChild(Transport* aTransport)
+        : mGotHi(false)
+        , mTransport(aTransport)
+    {}
+    virtual ~TestOpensOpenedChild() {}
+
+protected:
+    NS_OVERRIDE
+    virtual bool RecvHi();
+    NS_OVERRIDE
+    virtual bool AnswerHiRpc();
+
+    NS_OVERRIDE
+    virtual void ActorDestroy(ActorDestroyReason why);
+
+    bool mGotHi;
+    Transport* mTransport;
+};
+
+
+} // namespace _ipdltest
+} // namespace mozilla
+
+
+#endif // ifndef mozilla__ipdltest_TestOpens_h
--- a/ipc/ipdl/test/cxx/genIPDLUnitTests.py
+++ b/ipc/ipdl/test/cxx/genIPDLUnitTests.py
@@ -32,71 +32,91 @@
 # 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 *****
 
 import string, sys
 
+def usage():
+    print >>sys.stderr, """
+%s template_file -t unit_tests... -e extra_protocols...
+
+  TEMPLATE_FILE is used to generate to generate the unit-tester .cpp
+  UNIT_TESTS are the top-level protocols defining unit tests
+  EXTRA_PROTOCOLS are top-level protocols for subprocesses that can be
+                  spawned in tests but are not unit tests in and of
+                  themselves
+"""% (sys.argv[0])
+    sys.exit(1)
+
 def main(argv):
     template = argv[1]
-    unittests = argv[2:]
+
+    if argv[2] != '-t': usage()
+    i = 3
+    unittests = []
+    while argv[i] != '-e':
+        unittests.append(argv[i])
+        i += 1
+
+    extras = argv[(i+1):]
 
     includes = '\n'.join([
         '#include "%s.h"'% (t) for t in unittests ])
 
 
     enum_values = '\n'.join([
-        '    %s,'% (t) for t in unittests ])
+        '    %s,'% (t) for t in unittests+extras ])
     last_enum = unittests[-1]
 
 
     string_to_enums = '\n'.join([
         '''    else if (!strcmp(aString, "%s"))
-        return %s;'''% (t, t) for t in unittests ])
+        return %s;'''% (t, t) for t in unittests+extras ])
 
     enum_to_strings = '\n'.join([
         '''    case %s:
-        return "%s";'''%(t, t) for t in unittests ])
+        return "%s";'''%(t, t) for t in unittests+extras ])
 
     parent_delete_cases = '\n'.join([
 '''    case %s: {
-           delete reinterpret_cast<mozilla::_ipdltest::%sParent*>(gParentActor);
+           delete reinterpret_cast<%sParent*>(gParentActor);
            return;
        }
 '''% (t, t) for t in unittests ])
 
     parent_main_cases = '\n'.join([
 '''    case %s: {
         %sParent** parent =
         reinterpret_cast<%sParent**>(&gParentActor);
         *parent = new %sParent();
         (*parent)->Open(transport, child);
         return (*parent)->Main();
         }
 '''% (t, t, t, t) for t in unittests ])
 
     child_delete_cases = '\n'.join([
 '''    case %s: {
-           delete reinterpret_cast<mozilla::_ipdltest::%sChild*>(gChildActor);
+           delete reinterpret_cast<%sChild*>(gChildActor);
            return;
        }
-'''% (t, t) for t in unittests ])
+'''% (t, t) for t in unittests+extras ])
 
 
     child_init_cases = '\n'.join([
 '''    case %s: {
         %sChild** child =
             reinterpret_cast<%sChild**>(&gChildActor);
         *child = new %sChild();
         (*child)->Open(transport, parent, worker);
         return;
     }
-'''% (t, t, t, t) for t in unittests ])
+'''% (t, t, t, t) for t in unittests+extras ])
 
     templatefile = open(template, 'r')
     sys.stdout.write(
         string.Template(templatefile.read()).substitute(
             INCLUDES=includes,
             ENUM_VALUES=enum_values, LAST_ENUM=last_enum,
             STRING_TO_ENUMS=string_to_enums,
             ENUM_TO_STRINGS=enum_to_strings,
--- a/ipc/ipdl/test/cxx/ipdl.mk
+++ b/ipc/ipdl/test/cxx/ipdl.mk
@@ -1,10 +1,13 @@
 IPDLSRCS =					\
   PTestBlockChild.ipdl				\
+  PTestBridgeMain.ipdl				\
+  PTestBridgeSub.ipdl				\
+  PTestBridgeMainSub.ipdl			\
   PTestCrashCleanup.ipdl			\
   PTestDataStructures.ipdl			\
   PTestDataStructuresSub.ipdl			\
   PTestDesc.ipdl				\
   PTestDescSub.ipdl				\
   PTestDescSubsub.ipdl				\
   PTestFailedCtor.ipdl				\
   PTestFailedCtorSub.ipdl			\
@@ -15,16 +18,18 @@ IPDLSRCS =					\
   PTestLatency.ipdl				\
   PTestManyChildAllocs.ipdl			\
   PTestManyChildAllocsSub.ipdl			\
   PTestMultiMgrs.ipdl				\
   PTestMultiMgrsLeft.ipdl			\
   PTestMultiMgrsRight.ipdl			\
   PTestMultiMgrsBottom.ipdl			\
   PTestNestedLoops.ipdl				\
+  PTestOpens.ipdl				\
+  PTestOpensOpened.ipdl				\
   PTestRaceDeferral.ipdl			\
   PTestRacyReentry.ipdl				\
   PTestRacyRPCReplies.ipdl			\
   PTestRacyUndefer.ipdl				\
   PTestRPCErrorCleanup.ipdl			\
   PTestRPCRaces.ipdl				\
   PTestRPCShutdownRace.ipdl			\
   PTestSanity.ipdl				\
new file mode 100644
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/opensNonexistent.ipdl
@@ -0,0 +1,6 @@
+protocol opensNonexistent {
+    parent opens Unicorn;
+
+child: __delete__();
+state DEAD: send __delete__;
+};
new file mode 100644
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/opensSubprotocol.ipdl
@@ -0,0 +1,13 @@
+include protocol subprotocolOpens;
+
+protocol opensSubprotocol {
+    child opens subprotocolOpens;
+
+    manages subprotocolOpens;
+
+child:
+    subprotocolOpens();
+    __delete__();
+
+state DEAD: send __delete__;
+};
new file mode 100644
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/subprotocolOpens.ipdl
@@ -0,0 +1,10 @@
+include protocol opensSubprotocol;
+
+protocol subprotocolOpens {
+    parent opens opensSubprotocol;
+
+    manager opensSubprotocol;
+
+child: __delete__();
+state DEAD: send __delete__;
+};
--- a/ipc/ipdl/test/ipdl/ok/content.ipdl
+++ b/ipc/ipdl/test/ipdl/ok/content.ipdl
@@ -1,15 +1,17 @@
 include protocol compositor;
 include protocol jetpack;
+include protocol media;
 include protocol plugin;
 
 sync protocol content {
     parent spawns compositor as parent;
     parent spawns jetpack;
     child spawns plugin;
+    child opens media;
 
 child:
     __delete__();
 
 state DEAD:
     send __delete__;
 };
new file mode 100644
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/ok/media.ipdl
@@ -0,0 +1,7 @@
+sync protocol media {
+child:
+    __delete__();
+
+state DEAD:
+    send __delete__;
+};
--- a/layout/reftests/svg/sizing/reftest.list
+++ b/layout/reftests/svg/sizing/reftest.list
@@ -277,24 +277,24 @@ HTTP(../..) == inline--position-relative
 # in the future.
 #
 # Since we have given the replaced element algorithm a reasonable workout in
 # the standalone tests, for the embedded by reference tests we will simply
 # check that the embedded SVG's intrinsic dimensions are used. The following
 # tests do not have any width or height on the <object> element so they should
 # be sized purely by the embedded SVG's intrinsic size.
 
-== object--auto-auto--0-0.html          pass-empty.svg # XXX add border
-== object--auto-auto--0-pct.html        object--auto-auto--0-pct--ref.html
-== object--auto-auto--0-px.html         object--auto-auto--0-px--ref.html
-== object--auto-auto--pct-0.html        object--auto-auto--pct-0--ref.html
+random-if(Android) == object--auto-auto--0-0.html          pass-empty.svg # XXX add border
+random-if(Android) == object--auto-auto--0-pct.html        object--auto-auto--0-pct--ref.html
+random-if(Android) == object--auto-auto--0-px.html         object--auto-auto--0-px--ref.html
+random-if(Android) == object--auto-auto--pct-0.html        object--auto-auto--pct-0--ref.html
 # The following four commented out tests fail post bug 428023:
 #== object--auto-auto--pct-pct.html      object--auto-auto--pct-pct--ref.html
 #== object--auto-auto--pct-px.html       object--auto-auto--pct-px--ref.html
-== object--auto-auto--px-0.html         object--auto-auto--px-0--ref.html
+random-if(Android) == object--auto-auto--px-0.html         object--auto-auto--px-0--ref.html
 #== object--auto-auto--px-pct.html       object--auto-auto--px-pct--ref.html
 random-if(Android) == object--auto-auto--px-px.html        object--auto-auto--px-px--ref.html
 #== object--pct-pct--0-0.html            pass.svg
 
 
 # Assorted tests to check that dynamic changes work correctly
 #
 # Here we have an assortment of different tests to check that updates occur
--- a/layout/xul/base/public/nsIPopupBoxObject.idl
+++ b/layout/xul/base/public/nsIPopupBoxObject.idl
@@ -37,18 +37,19 @@
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "nsIBoxObject.idl"
 
 interface nsIDOMElement;
 interface nsIDOMNode;
 interface nsIDOMEvent;
+interface nsIDOMClientRect;
 
-[scriptable, uuid(548a9e3f-af78-42b0-a260-035ece15c19f)]
+[scriptable, uuid(6AD1B199-95D3-448B-98D7-896BCE3A1DCD)]
 interface nsIPopupBoxObject : nsISupports
 {
   /**
    *  This method is deprecated. Use openPopup or openPopupAtScreen instead.
    */
   void showPopup(in nsIDOMElement srcContent, in nsIDOMElement popupContent,
                  in long xpos, in long ypos,
                  in wstring popupType, in wstring anchorAlignment, 
@@ -178,15 +179,21 @@ interface nsIPopupBoxObject : nsISupport
    */
   readonly attribute nsIDOMNode triggerNode;
 
   /**
    * Retrieve the anchor that was specified to openPopup or for menupopups in a
    * menu, the parent menu.
    */
   readonly attribute nsIDOMElement anchorNode;
+
+  /*
+   * Retrieve the screen rectangle of the popup, including the area occupied by
+   * any titlebar or borders present.
+   */
+  nsIDOMClientRect getOuterScreenRect();
 };
 
 %{C++
 nsresult
 NS_NewPopupBoxObject(nsIBoxObject** aResult);
 
 %}
--- a/layout/xul/base/src/nsPopupBoxObject.cpp
+++ b/layout/xul/base/src/nsPopupBoxObject.cpp
@@ -45,16 +45,17 @@
 #include "nsIContent.h"
 #include "nsIDocument.h"
 #include "nsIDOMDocument.h"
 #include "nsIDOMElement.h"
 #include "nsIFrame.h"
 #include "nsINameSpaceManager.h"
 #include "nsGkAtoms.h"
 #include "nsMenuPopupFrame.h"
+#include "nsClientRect.h"
 
 class nsPopupBoxObject : public nsBoxObject,
                          public nsIPopupBoxObject
 {
 public:
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_NSIPOPUPBOXOBJECT
 
@@ -285,16 +286,49 @@ nsPopupBoxObject::GetAnchorNode(nsIDOMEl
 
   nsIContent* anchor = menuPopupFrame->GetAnchor();
   if (anchor)
     CallQueryInterface(anchor, aAnchor);
 
   return NS_OK;
 }
 
+NS_IMETHODIMP
+nsPopupBoxObject::GetOuterScreenRect(nsIDOMClientRect** aRect)
+{
+  nsClientRect* rect = new nsClientRect();
+  if (!rect)
+    return NS_ERROR_OUT_OF_MEMORY;
+
+  NS_ADDREF(*aRect = rect);
+
+  nsMenuPopupFrame *menuPopupFrame = GetMenuPopupFrame();
+  if (!menuPopupFrame)
+    return NS_OK;
+
+  // Return an empty rectangle if the popup is not open.
+  nsPopupState state = menuPopupFrame->PopupState();
+  if (state != ePopupOpen && state != ePopupOpenAndVisible)
+    return NS_OK;
+
+  nsIView* view = menuPopupFrame->GetView();
+  if (view) {
+    nsIWidget* widget = view->GetWidget();
+    if (widget) {
+      nsIntRect screenRect;
+      widget->GetScreenBounds(screenRect);
+
+      PRInt32 pp = menuPopupFrame->PresContext()->AppUnitsPerDevPixel();
+      rect->SetLayoutRect(screenRect.ToAppUnits(pp));
+    }
+  }
+
+  return NS_OK;
+}
+
 // Creation Routine ///////////////////////////////////////////////////////////////////////
 
 nsresult
 NS_NewPopupBoxObject(nsIBoxObject** aResult)
 {
   *aResult = new nsPopupBoxObject;
   if (!*aResult)
     return NS_ERROR_OUT_OF_MEMORY;
--- a/mobile/chrome/content/browser-ui.js
+++ b/mobile/chrome/content/browser-ui.js
@@ -526,19 +526,21 @@ var BrowserUI = {
       Elements.tabs.addEventListener("TabOpen", BrowserUI, true);
       Elements.tabs.addEventListener("TabRemove", BrowserUI, true);
 
       // Init the tool panel views
       ExtensionsView.init();
       DownloadsView.init();
       ConsoleView.init();
 
-      // Pre-start the content process
-      Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULRuntime)
-          .ensureContentProcess();
+      if (Services.prefs.getBoolPref("browser.tabs.remote")) {
+          // Pre-start the content process
+          Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULRuntime)
+                                           .ensureContentProcess();
+      }
 
 #ifdef MOZ_SERVICES_SYNC
       // Init the sync system
       WeaveGlue.init();
 #endif
 
       Services.obs.addObserver(BrowserSearch, "browser-search-engine-modified", false);
       messageManager.addMessageListener("Browser:MozApplicationManifest", OfflineApps);
--- a/mobile/chrome/content/browser.css
+++ b/mobile/chrome/content/browser.css
@@ -18,41 +18,41 @@ browser[remote="true"] {
   -moz-binding: url("chrome://browser/content/tabs.xml#tablist");
 }
 
 documenttab {
   -moz-binding: url("chrome://browser/content/tabs.xml#documenttab");
 }
 
 settings {
-  -moz-binding: url("chrome://browser/content/bindings/setting.xml#settings");
+  -moz-binding: url("chrome://mozapps/content/extensions/setting.xml#settings");
 }
 
 setting[type="bool"] {
-  -moz-binding: url("chrome://browser/content/bindings/setting.xml#setting-bool");
+  -moz-binding: url("chrome://mozapps/content/extensions/setting.xml#setting-bool");
 }
 
 setting[type="bool"][localized="true"] {
-  -moz-binding: url("chrome://browser/content/bindings/setting.xml#setting-localized-bool");
+  -moz-binding: url("chrome://mozapps/content/extensions/setting.xml#setting-localized-bool");
 }
 
 setting[type="boolint"] {
-  -moz-binding: url("chrome://browser/content/bindings/setting.xml#setting-boolint");
+  -moz-binding: url("chrome://mozapps/content/extensions/setting.xml#setting-boolint");
 }
 
 setting[type="integer"] {
-  -moz-binding: url("chrome://browser/content/bindings/setting.xml#setting-integer");
+  -moz-binding: url("chrome://mozapps/content/extensions/setting.xml#setting-integer");
 }
 
 setting[type="control"] {
-  -moz-binding: url("chrome://browser/content/bindings/setting.xml#setting-control");
+  -moz-binding: url("chrome://mozapps/content/extensions/setting.xml#setting-control");
 }
 
 setting[type="string"] {
-  -moz-binding: url("chrome://browser/content/bindings/setting.xml#setting-string");
+  -moz-binding: url("chrome://mozapps/content/extensions/setting.xml#setting-string");
 }
 
 #browsers > notificationbox {
   -moz-binding: url("chrome://browser/content/notification.xml#stacked-notificationbox");
   overflow: -moz-hidden-unscrollable;
 }
 
 notification {
--- a/mobile/chrome/content/browser.js
+++ b/mobile/chrome/content/browser.js
@@ -2726,17 +2726,18 @@ Tab.prototype = {
     let browser = this._createBrowser(aURI, null);
 
     // Should we fully load the new browser, or wait until later
     if ("delayLoad" in aParams && aParams.delayLoad)
       return;
 
     try {
       let flags = aParams.flags || Ci.nsIWebNavigation.LOAD_FLAGS_NONE;
-      browser.loadURIWithFlags(aURI, flags, aParams.referrerURI, aParams.charset, aParams.postData);
+      let postData = aParams.postData ? aParams.postData.value : null;
+      browser.loadURIWithFlags(aURI, flags, aParams.referrerURI, aParams.charset, postData);
     } catch(e) {
       dump("Error: " + e + "\n");
     }
   },
 
   destroy: function destroy() {
     document.getElementById("tabs").removeTab(this._chromeTab);
     this._chromeTab = null;
--- a/mobile/chrome/content/browser.xul
+++ b/mobile/chrome/content/browser.xul
@@ -529,17 +529,17 @@
 #ifdef MOZ_SERVICES_SYNC
     <box id="syncsetup-container" class="perm-modal-block window-width window-height" hidden="true">
       <dialog id="syncsetup-dialog" class="content-dialog" flex="1">
         <hbox class="prompt-title">
           <description>&sync.setup.title;</description>
         </hbox>
         <separator class="prompt-line"/>
         <vbox id="syncsetup-simple" class="syncsetup-page" flex="1">
-          <scrollbox class="prompt-message" orient="vertical" flex="1">
+          <scrollbox id="sync-message" class="prompt-message" orient="vertical" flex="1">
             <description class="syncsetup-desc syncsetup-center" flex="1">&sync.setup.jpake;</description>
             <description class="syncsetup-center syncsetup-link" flex="1" onclick="WeaveGlue.openTutorial();">&sync.setup.tutorial;</description>
             <separator/>
             <vbox align="center" flex="1">
               <description id="syncsetup-code1" class="syncsetup-code">....</description>
               <description id="syncsetup-code2" class="syncsetup-code">....</description>
               <description id="syncsetup-code3" class="syncsetup-code">....</description>
             </vbox>
--- a/mobile/chrome/content/common-ui.js
+++ b/mobile/chrome/content/common-ui.js
@@ -880,18 +880,21 @@ var FormHelperUI = {
   },
 
   goToNext: function formHelperGoToNext() {
     this._currentBrowser.messageManager.sendAsyncMessage("FormAssist:Next", { });
   },
 
   doAutoComplete: function formHelperDoAutoComplete(aElement) {
     // Suggestions are only in <label>s. Ignore the rest.
-    if (aElement instanceof Ci.nsIDOMXULLabelElement)
-      this._currentBrowser.messageManager.sendAsyncMessage("FormAssist:AutoComplete", { value: aElement.getAttribute("data") });
+    if (!(aElement instanceof Ci.nsIDOMXULLabelElement))
+      return;
+
+    this._currentBrowser.messageManager.sendAsyncMessage("FormAssist:AutoComplete", { value: aElement.getAttribute("data") });
+    ContentPopupHelper.popup = null;
   },
 
   get _open() {
     return (this._container.getAttribute("type") == this.type);
   },
 
   set _open(aVal) {
     if (aVal == this._open)
--- a/mobile/chrome/content/config.js
+++ b/mobile/chrome/content/config.js
@@ -251,23 +251,23 @@ var ViewConfig = {
     let row = document.createElement("richlistitem");
 
     row.setAttribute("name", aPref.name);
     row.setAttribute("type", aPref.type);
     row.setAttribute("role", "button");
     row.setAttribute("default", aPref.default);
 
     let label = document.createElement("label");
-    label.setAttribute("class", "preftitle");
+    label.setAttribute("class", "preferences-title");
     label.setAttribute("value", aPref.name);
     label.setAttribute("crop", "end");
     row.appendChild(label);
 
     label = document.createElement("label");
-    label.setAttribute("class", "prefvalue");
+    label.setAttribute("class", "preferences-value");
     label.setAttribute("value", aPref.value);
     label.setAttribute("crop", "end");
     row.appendChild(label);
 
     return row;
   },
 
   getIncrementForValue: function getIncrementForValue(aValue) {
--- a/mobile/chrome/content/content.js
+++ b/mobile/chrome/content/content.js
@@ -631,18 +631,21 @@ let Content = {
         if ((aX > rect.left && aX < (rect.left + rect.width)) &&
             (aY > rect.top && aY < (rect.top + rect.height))) {
           isTouchClick = false;
           break;
         }
       }
 
       if (isTouchClick) {
-        let rect = rects[0];
-        let point = (new Rect(rect.left, rect.top, rect.width, rect.height)).center();
+        let rect = new Rect(rects[0]);
+        if (rect.isEmpty())
+          return;
+
+        let point = rect.center();
         aX = point.x;
         aY = point.y;
       }
     }
 
     let scrollOffset = ContentScroll.getScrollOffset(content);
     let windowUtils = Util.getWindowUtils(content);
     aButton = aButton || 0;
--- a/mobile/chrome/jar.mn
+++ b/mobile/chrome/jar.mn
@@ -43,17 +43,16 @@ chrome.jar:
   content/bindings/browser.xml         (content/bindings/browser.xml)
   content/bindings/browser.js          (content/bindings/browser.js)
   content/notification.xml             (content/notification.xml)
   content/bindings/extensions.xml      (content/bindings/extensions.xml)
   content/bindings/downloads.xml       (content/bindings/downloads.xml)
   content/bindings/console.xml         (content/bindings/console.xml)
   content/bindings/dialog.xml          (content/bindings/dialog.xml)
   content/bindings/pageaction.xml      (content/bindings/pageaction.xml)
-  content/bindings/setting.xml         (content/bindings/setting.xml)
   content/bindings/arrowbox.xml        (content/bindings/arrowbox.xml)
   content/browser.css                  (content/browser.css)
   content/cursor.css                   (content/cursor.css)
 % content branding %content/branding/
   content/sanitize.js                  (content/sanitize.js)
 * content/input.js                     (content/input.js)
 * content/Util.js                      (content/Util.js)
   content/forms.js                     (content/forms.js)
--- a/mobile/components/SessionStore.js
+++ b/mobile/components/SessionStore.js
@@ -76,36 +76,42 @@ SessionStore.prototype = {
   _interval: 10000,
   _maxTabsUndo: 5,
   _shouldRestore: false,
 
   init: function ss_init() {
     // Get file references
     this._sessionFile = Services.dirsvc.get("ProfD", Ci.nsILocalFile);
     this._sessionFileBackup = this._sessionFile.clone();
+    this._sessionCache = this._sessionFile.clone();
     this._sessionFile.append("sessionstore.js");
     this._sessionFileBackup.append("sessionstore.bak");
+    this._sessionCache.append("sessionstoreCache");
 
     this._loadState = STATE_STOPPED;
 
     try {
       if (this._sessionFileBackup.exists()) {
         this._shouldRestore = true;
         this._sessionFileBackup.remove(false);
       }
+
       if (this._sessionFile.exists()) {
         // Disable crash recovery if we have exceeded the timeout
         this._lastSessionTime = this._sessionFile.lastModifiedTime;
         let delta = Date.now() - this._lastSessionTime;
         let timeout = Services.prefs.getIntPref("browser.sessionstore.resume_from_crash_timeout");
         if (delta > (timeout * 60000))
           this._shouldRestore = false;
 
         this._sessionFile.copyTo(null, this._sessionFileBackup.leafName);
       }
+
+      if (!this._sessionCache.exists() || !this._sessionCache.isDirectory())
+        this._sessionCache.create(Ci.nsIFile.DIRECTORY_TYPE, 0700);
     } catch (ex) {
       Cu.reportError(ex); // file was write-locked?
     }
 
     this._interval = Services.prefs.getIntPref("browser.sessionstore.interval");
     this._maxTabsUndo = Services.prefs.getIntPref("browser.sessionstore.max_tabs_undo");
 
     // Disable crash recovery if it has been turned off
@@ -125,16 +131,47 @@ SessionStore.prototype = {
         this._sessionFile.remove(false);
       } catch (ex) { dump(ex + '\n'); } // couldn't remove the file - what now?
     }
     if (this._sessionFileBackup.exists()) {
       try {
         this._sessionFileBackup.remove(false);
       } catch (ex) { dump(ex + '\n'); } // couldn't remove the file - what now?
     }
+
+    this._clearCache();
+  },
+
+  _clearCache: function ss_clearCache() {
+    // First, let's get a list of files we think should be active
+    let activeFiles = [];
+    this._forEachBrowserWindow(function(aWindow) {
+      let tabs = aWindow.Browser.tabs;
+      for (let i = 0; i < tabs.length; i++) {
+        let browser = tabs[i].browser;
+        if (browser.__SS_extdata && "thumbnail" in browser.__SS_extdata)
+          activeFiles.push(browser.__SS_extdata.thumbnail);
+      }
+    });
+
+    // Now, let's find the stale files in the cache folder
+    let staleFiles = [];
+    let cacheFiles = this._sessionCache.directoryEntries;
+    while (cacheFiles.hasMoreElements()) {
+      let file = cacheFiles.getNext().QueryInterface(Ci.nsILocalFile);
+      let fileURI = Services.io.newFileURI(file);
+      if (activeFiles.indexOf(fileURI) == -1)
+        staleFiles.push(file);
+    }
+
+    // Remove the stale files in a separate step to keep the enumerator from
+    // messing up if we remove the files as we collect them.
+    staleFiles.forEach(function(aFile) {
+      aFile.remove(false);
+    })
   },
 
   observe: function ss_observe(aSubject, aTopic, aData) {
     let self = this;
     let observerService = Services.obs;
     switch (aTopic) {
       case "app-startup":
         observerService.addObserver(this, "final-ui-startup", true);
@@ -284,18 +321,20 @@ SessionStore.prototype = {
     this._windows[aWindow.__SSID] = { tabs: [], selected: 0, closedTabs: [] };
 
     // Perform additional initialization when the first window is loading
     if (this._loadState == STATE_STOPPED) {
       this._loadState = STATE_RUNNING;
       this._lastSaveTime = Date.now();
 
       // Nothing to restore, notify observers things are complete
-      if (!this._shouldRestore)
+      if (!this._shouldRestore) {
+        this._clearCache();
         Services.obs.notifyObservers(null, "sessionstore-windows-restored", "");
+      }
     }
 
     // Add tab change listeners to all already existing tabs
     let tabs = aWindow.Browser.tabs;
     for (let i = 0; i < tabs.length; i++)
       this.onTabAdd(aWindow, tabs[i].browser, true);
 
     // Notification of tab add/remove/selection
@@ -477,18 +516,18 @@ SessionStore.prototype = {
     let winData = this._windows[aWindow.__SSID];
     winData.tabs = [];
 
     let index = aWindow.Elements.browsers.selectedIndex;
     winData.selected = parseInt(index) + 1; // 1-based
 
     let tabs = aWindow.Browser.tabs;
     for (let i = 0; i < tabs.length; i++) {
-      if (tabs[i].browser.__SS_data) {
-        let browser = tabs[i].browser;
+      let browser = tabs[i].browser;
+      if (browser.__SS_data) {
         let tabData = browser.__SS_data;
         if (browser.__SS_extdata)
           tabData.extData = browser.__SS_extdata;
         winData.tabs.push(tabData);
       }
     }
   },
 
@@ -611,16 +650,33 @@ SessionStore.prototype = {
   getTabValue: function ss_getTabValue(aTab, aKey) {
     let browser = aTab.linkedBrowser;
     let data = browser.__SS_extdata || {};
     return data[aKey] || "";
   },
 
   setTabValue: function ss_setTabValue(aTab, aKey, aStringValue) {
     let browser = aTab.linkedBrowser;
+
+    // Thumbnails are actually stored in the cache, so do the save and update the URI
+    if (aKey == "thumbnail") {
+      let file = this._sessionCache.clone();
+      file.append("thumbnail-" + browser.contentWindowId);
+      file.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, 0600);
+
+      let source = Services.io.newURI(aStringValue, "UTF8", null);
+      let target = Services.io.newFileURI(file)
+
+      let persist = Cc["@mozilla.org/embedding/browser/nsWebBrowserPersist;1"].createInstance(Ci.nsIWebBrowserPersist);
+      persist.persistFlags = Ci.nsIWebBrowserPersist.PERSIST_FLAGS_REPLACE_EXISTING_FILES | Ci.nsIWebBrowserPersist.PERSIST_FLAGS_AUTODETECT_APPLY_CONVERSION;
+      persist.saveURI(source, null, null, null, null, file);
+
+      aStringValue = target.spec;
+    }
+
     if (!browser.__SS_extdata)
       browser.__SS_extdata = {};
     browser.__SS_extdata[aKey] = aStringValue;
     this.saveStateDelayed();
   },
 
   deleteTabValue: function ss_deleteTabValue(aTab, aKey) {
     let browser = aTab.linkedBrowser;
@@ -631,28 +687,27 @@ SessionStore.prototype = {
   },
 
   shouldRestore: function ss_shouldRestore() {
     return this._shouldRestore;
   },
 
   restoreLastSession: function ss_restoreLastSession(aBringToFront) {
     // The previous session data has already been renamed to the backup file
-    let dirService = Cc["@mozilla.org/file/directory_service;1"].getService(Ci.nsIProperties);
-    let session = dirService.get("ProfD", Ci.nsILocalFile);
-    session.append("sessionstore.bak");
-    if (!session.exists())
+    if (!this._sessionFileBackup.exists())
       return;
 
+    let self = this;
     function notifyObservers() {
+      self._clearCache();
       Services.obs.notifyObservers(null, "sessionstore-windows-restored", "");
     }
 
     try {
-      let channel = NetUtil.newChannel(session);
+      let channel = NetUtil.newChannel(this._sessionFileBackup);
       channel.contentType = "application/json";
       NetUtil.asyncFetch(channel, function(aStream, aResult) {
         if (!Components.isSuccessCode(aResult)) {
           Cu.reportError("SessionStore: Could not read from sessionstore.bak file");
           notifyObservers();
           return;
         }
 
--- a/mobile/themes/core/browser.css
+++ b/mobile/themes/core/browser.css
@@ -1211,67 +1211,71 @@ pageaction:not([image]) > hbox >.pageact
 }
 
 /* Preferences window   ---------------------------------------------------- */
 .setting {
   padding-left: @padding_xnormal@;
   border-bottom: @border_width_tiny@ solid #cacdd5;
 }
 
-/* XXX should be a richlistitem */
-.prefbox {
+setting {
   padding: @padding_xsmall@;
   border-bottom: @border_width_tiny@ solid rgb(207,207,207);
   min-height: @touch_row@; /* row size */
   -moz-box-align: center;
+  -moz-box-orient: horizontal;
 }
 
-.setting-group > .prefbox {
+.setting-label {
+  -moz-box-flex: 1;
+}
+
+.setting-group > setting {
   border-bottom: none;
 }
 
-.setting-subgroup > .prefbox {
+.setting-subgroup > setting {
   border-bottom: none;
   -moz-margin-start: @margin_xxxnormal@;
 }
 
-.setting-subgroup + :not(.setting-subgroup) > .prefbox {
+.setting-subgroup + :not(.setting-subgroup) > setting {
   border-top: @border_width_tiny@ solid rgb(207,207,207);
 }
 
 /* Put setting textboxes on a separate row in portrait */
 @media (@orientation@: portrait) {
-  .setting-integer,
-  .setting-string {
+  setting[type="integer"],
+  setting[type="string"] {
     -moz-box-align: start;
     -moz-box-orient: vertical;
   }
 
-  .setting-integer > .setting-input > textbox,
-  .setting-string > .setting-input > textbox {
+  setting[type="integer"] > .setting-input > textbox,
+  setting[type="string"] > .setting-input > textbox {
     width: 499px; /* textboxes seem to need a width in order to flex */
     -moz-box-flex: 1;
   }
 }
 
 .options-box {
   -moz-margin-start: 28px;  /* sized based on the 32px addon image */
 }
 
-.options-box > setting:last-child > .prefbox {
+.options-box > setting:last-child {
   border-bottom: 0;
 }
 
 /* XXX should be a richlistitem description.title */
-.preftitle {
+.preferences-title {
   font-size: @font_normal@ !important;
 }
 
 /* XXX should be a richlistitem description.normal */
-.prefdesc {
+.preferences-description {
   font-size: @font_small@ !important;
   color: grey;
 }
 
 /* alerts popup ----------------------------------------------------------- */
 #alerts-container {
   color: white;
   background-color: #5e6166;
@@ -1456,16 +1460,20 @@ pageaction:not([image]) > hbox >.pageact
 .syncsetup-label {
   color: #fff;
 }
 
 #syncsetup-customserver {
   -moz-margin-start: @margin_xnormal@;
 }
 
+#sync-message {
+  padding-bottom: 2em;
+}
+
 /* content scrollbars */
 .scroller {
   opacity: 0;
   background-color: rgba(0, 0, 0, 0.4) !important;
   -moz-border-top-colors: none !important;
   -moz-border-bottom-colors: none !important;
   -moz-border-right-colors: none !important;
   -moz-border-left-colors: none !important;
--- a/mobile/themes/core/config.css
+++ b/mobile/themes/core/config.css
@@ -39,25 +39,25 @@
     -moz-box-orient: vertical;
   }
 }
 
 richlistitem {
   -moz-box-align: center;
 }
 
-richlistitem .preftitle {
+richlistitem .preferences-title {
   pointer-events: none;
   min-width: 200px;
   -moz-box-flex: 1;
   margin-right: 8px;
 }
 
 /* XXX look  + sync */
-richlistitem[default="false"] .preftitle {
+richlistitem[default="false"] .preferences-title {
   font-weight: bold;
 }
 
 richlistitem .prefvalue {
   min-width: 200px;
   pointer-events: none;
   -moz-box-flex: 4;
   text-align: end;
@@ -93,21 +93,21 @@ richlistitem .prefvalue {
 #editor-container > hbox > label {
   pointer-events: none;
 }
 
 #editor + richlistitem {
   display: none;
 }
 
-#editor[default="false"] .preftitle {
+#editor[default="false"] .preferences-title {
   font-weight: bold;
 }
 
-#editor-setting .prefbox {
+#editor-setting setting {
   border-color: transparent !important;
 }
 
 #editor-setting[type="string"] .setting-input {
   -moz-box-flex: 4;
 }
 
 #editor-setting[type="string"] .setting-input > textbox {
--- a/mobile/themes/core/gingerbread/browser.css
+++ b/mobile/themes/core/gingerbread/browser.css
@@ -1190,67 +1190,71 @@ pageaction:not([image]) > hbox >.pageact
   border-bottom: @border_width_tiny@ solid @color_divider_border@;
 }
 
 .setting {
   padding-left: @padding_xnormal@;
   border-bottom: @border_width_tiny@ solid #cacdd5;
 }
 
-/* XXX should be a richlistitem */
-.prefbox {
+setting {
   padding: @padding_xsmall@;
   border-bottom: @border_width_tiny@ solid @color_divider_border@;
   min-height: @touch_row@; /* row size */
   -moz-box-align: center;
+  -moz-box-orient: horizontal;
 }
 
-.setting-group > .prefbox {
+.setting-label {
+  -moz-box-flex: 1;
+}
+
+.setting-group > setting {
   border-bottom: none;
 }
 
-.setting-subgroup > .prefbox {
+.setting-subgroup > setting {
   border-bottom: none;
   -moz-margin-start: @margin_xxxnormal@;
 }
 
-.setting-subgroup + :not(.setting-subgroup) > .prefbox {
+.setting-subgroup + :not(.setting-subgroup) > setting {
   border-top: @border_width_tiny@ solid rgb(207,207,207);
 }
 
 /* Put setting textboxes on a separate row in portrait */
 @media (@orientation@: portrait) {
-  .setting-integer,
-  .setting-string {
+  setting[type="integer"],
+  setting[type="string"] {
     -moz-box-align: start;
     -moz-box-orient: vertical;
   }
 
-  .setting-integer > .setting-input > textbox,
-  .setting-string > .setting-input > textbox {
+  setting[type="integer"] > .setting-input > textbox,
+  setting[type="string"] > .setting-input > textbox {
     width: 499px; /* textboxes seem to need a width in order to flex */
     -moz-box-flex: 1;
   }
 }
 
 .options-box {
   -moz-margin-start: 28px;  /* sized based on the 32px addon image */
 }
 
-.options-box > setting:last-child > .prefbox {
+.options-box > setting:last-child {
   border-bottom: 0;
 }
 
 /* XXX should be a richlistitem description.title */
-.preftitle {
+.preferences-title {
   font-size: @font_normal@ !important;
 }
 
 /* XXX should be a richlistitem description.normal */
-.prefdesc {
+.preferences-description {
   font-size: @font_small@ !important;
   color: @color_subtext_default@;
 }
 
 /* alerts popup ----------------------------------------------------------- */
 #alerts-container {
   color: @color_text_default@;
   background-color: #5e6166;
--- a/modules/libpref/src/init/all.js
+++ b/modules/libpref/src/init/all.js
@@ -1819,17 +1819,17 @@ pref("gfx.font_rendering.cleartype_param
 pref("gfx.font_rendering.cleartype_params.pixel_structure", -1);
 pref("gfx.font_rendering.cleartype_params.rendering_mode", -1);
 
 // A comma-separated list of font family names. Fonts in these families will
 // be forced to use "GDI Classic" ClearType mode, ignoring the value
 // of gfx.font_rendering.cleartype_params.rendering_mode.
 // Currently we apply this setting to the sans-serif Microsoft "core Web fonts".
 pref("gfx.font_rendering.cleartype_params.force_gdi_classic_for_families",
-     "Arial,Courier New,Tahoma,Trebuchet MS,Verdana");
+     "Arial,Courier New,Segoe UI,Tahoma,Trebuchet MS,Verdana");
 // The maximum size at which we will force GDI classic mode using
 // force_gdi_classic_for_families.
 pref("gfx.font_rendering.cleartype_params.force_gdi_classic_max_size", 15);
 
 pref("ui.key.menuAccessKeyFocuses", true);
 
 // override double-click word selection behavior.
 pref("layout.word_select.eat_space_to_next_word", true);
--- a/netwerk/protocol/websocket/nsWebSocketHandler.cpp
+++ b/netwerk/protocol/websocket/nsWebSocketHandler.cpp
@@ -98,28 +98,16 @@ static PRLogModuleInfo *webSocketLog = n
 // Use this fake ptr so the Fin message stays in sequence in the
 // main transmit queue
 #define kFinMessage (reinterpret_cast<nsCString *>(0x01))
 
 // An implementation of draft-ietf-hybi-thewebsocketprotocol-07
 #define SEC_WEBSOCKET_VERSION "7"
 
 /*
- * About using rand() without a srand() or initstate()
- *
- * rand() is commonly called throughout the codebase with the assumption
- * that it has been seeded securely. That does indeed happen in
- * the initialization of the nsUUIDGenerator sevice - getting secure
- * seed information from  PR_GetRandomNoise() and giving that to
- * initstate(). We won't repeat that here "just in case" because
- * it is easy to drain some systems of their true random sources.
- */
-
-
-/*
  * About SSL unsigned certificates
  *
  * wss will not work to a host using an unsigned certificate unless there
  * is already an exception (i.e. it cannot popup a dialog asking for
  * a security exception). This is similar to how an inlined img will
  * fail without a dialog if fails for the same reason. This should not
  * be a problem in practice as it is expected the websocket javascript
  * is served from the same host as the websocket server (or of course,
@@ -1157,17 +1145,28 @@ nsWebSocketHandler::PrimeNewOutgoingMess
         }
         payload = mOutHeader + mHdrOutToSend;
     }
     
     NS_ABORT_IF_FALSE(payload, "payload offset not found");
     
     // Perfom the sending mask. never use a zero mask
     PRUint32 mask;
-    while (!(mask = (PRUint32) rand()));
+    do {
+        PRUint8 *buffer;
+        nsresult rv = mRandomGenerator->GenerateRandomBytes(4, &buffer);
+        if (NS_FAILED(rv)) {
+            LOG(("WebSocketHandler:: PrimeNewOutgoingMessage() "
+                 "GenerateRandomBytes failure %x\n", rv));
+            StopSession(rv);
+            return;
+        }
+        mask = * reinterpret_cast<PRUint32 *>(buffer);
+        NS_Free(buffer);
+    } while (!mask);
     *(((PRUint32 *)payload) - 1) = PR_htonl(mask);
 
     LOG(("WebSocketHandler:: PrimeNewOutgoingMessage() "
          "using mask %08x\n", mask));
 
     // We don't mask the framing, but occasionally we stick a little payload
     // data in the buffer used for the framing. Close frames are the
     // current example. This data needs to be
@@ -1470,23 +1469,23 @@ nsWebSocketHandler::SetupRequest()
             NS_LITERAL_CSTRING("Sec-WebSocket-Protocol"),
             mProtocol, PR_TRUE);
 
     if (mAllowCompression)
         mHttpChannel->SetRequestHeader(
             NS_LITERAL_CSTRING("Sec-WebSocket-Extensions"),
             NS_LITERAL_CSTRING("deflate-stream"), PR_FALSE);
 
-    PRUint32      secKey[4];
+    PRUint8      *secKey;
     nsCAutoString secKeyString;
-    secKey[0] = (PRUint32) rand();
-    secKey[1] = (PRUint32) rand();
-    secKey[2] = (PRUint32) rand();
-    secKey[3] = (PRUint32) rand();
+    
+    rv = mRandomGenerator->GenerateRandomBytes(16, &secKey);
+    NS_ENSURE_SUCCESS(rv, rv);
     char* b64 = PL_Base64Encode((const char *)secKey, 16, nsnull);
+    NS_Free(secKey);
     if (!b64) return NS_ERROR_OUT_OF_MEMORY;
     secKeyString.Assign(b64);
     PR_Free(b64);
     mHttpChannel->SetRequestHeader(
         NS_LITERAL_CSTRING("Sec-WebSocket-Key"), secKeyString, PR_FALSE);
     LOG(("WebSocketHandler::AsyncOpen() client key %s\n", secKeyString.get()));
 
     // prepare the value we expect to see in
@@ -1628,51 +1627,56 @@ nsWebSocketHandler::AsyncOnChannelRedire
         return NS_OK;
     }
     
     nsCOMPtr<nsIHttpChannel> newHttpChannel =
         do_QueryInterface(newChannel, &rv);
     
     if (NS_FAILED(rv)) {
         LOG(("nsWebSocketHandler Redirect could not QI to HTTP\n"));
-        callback->OnRedirectVerifyCallback(NS_ERROR_FAILURE);
+        callback->OnRedirectVerifyCallback(rv);
         return NS_OK;
     }
 
     nsCOMPtr<nsIHttpChannelInternal> newUpgradeChannel =
         do_QueryInterface(newChannel, &rv);
     
     if (NS_FAILED(rv)) {
         LOG(("nsWebSocketHandler Redirect could not QI to HTTP Upgrade\n"));
-        callback->OnRedirectVerifyCallback(NS_ERROR_FAILURE);
+        callback->OnRedirectVerifyCallback(rv);
         return NS_OK;
     }
     
-    // The redirect is OK
+    // The redirect is likely OK
 
     newChannel->SetNotificationCallbacks(this);
     mURI = newuri;
     mHttpChannel = newHttpChannel;
     mChannel = newUpgradeChannel;
-    SetupRequest();
+    rv = SetupRequest();
+    if (NS_FAILED(rv)) {
+        LOG(("nsWebSocketHandler Redirect could not SetupRequest()\n"));
+        callback->OnRedirectVerifyCallback(rv);
+        return NS_OK;
+    }
     
     // We cannot just tell the callback OK right now due to the 1 connect at
     // a time policy. First we need to complete the old location and then
     // start the lookup chain for the new location - once that is complete
     // and we have been admitted, OnRedirectVerifyCallback(NS_OK) will be called
     // out of BeginOpen()
 
     sWebSocketAdmissions->Complete(mAddress);
     mAddress.Truncate();
     mRedirectCallback = callback;
 
     rv = ApplyForAdmission();
     if (NS_FAILED(rv)) {
         LOG(("nsWebSocketHandler Redirect failed due to DNS failure\n"));
-        callback->OnRedirectVerifyCallback(NS_ERROR_FAILURE);
+        callback->OnRedirectVerifyCallback(rv);
         mRedirectCallback = nsnull;
     }
     
     return NS_OK;
 }
 
 // nsITimerCallback
 
@@ -1846,16 +1850,23 @@ nsWebSocketHandler::AsyncOpen(nsIURI *aU
     nsresult rv;
 
     mSocketThread = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv);
     if (NS_FAILED(rv)) {
         NS_WARNING("unable to continue without socket transport service");
         return rv;
     }
 
+    mRandomGenerator = do_GetService("@mozilla.org/security/random-generator;1",
+                                     &rv);
+    if (NS_FAILED(rv)) {
+        NS_WARNING("unable to continue without random number generator");
+        return rv;
+    }
+
     nsCOMPtr<nsIPrefBranch> prefService;
     prefService = do_GetService(NS_PREFSERVICE_CONTRACTID);
 
     if (prefService) {
         PRInt32 intpref;
         PRBool boolpref;
         rv = prefService->
             GetIntPref("network.websocket.max-message-size", &intpref);
--- a/netwerk/protocol/websocket/nsWebSocketHandler.h
+++ b/netwerk/protocol/websocket/nsWebSocketHandler.h
@@ -51,16 +51,17 @@
 #include "nsILoadGroup.h"
 #include "nsITimer.h"
 #include "nsIDNSListener.h"
 #include "nsIHttpChannel.h"
 #include "nsIChannelEventSink.h"
 #include "nsIAsyncVerifyRedirectCallback.h"
 #include "nsIStringStream.h"
 #include "nsIHttpChannelInternal.h"
+#include "nsIRandomGenerator.h"
 
 #include "nsCOMPtr.h"
 #include "nsString.h"
 #include "nsDeque.h"
 
 namespace mozilla { namespace net {
 
 class nsPostMessage;
@@ -191,16 +192,17 @@ private:
   nsCOMPtr<nsISupports>                    mContext;
   nsCOMPtr<nsIInterfaceRequestor>          mCallbacks;
   nsCOMPtr<nsIEventTarget>                 mSocketThread;
   nsCOMPtr<nsIHttpChannelInternal>         mChannel;
   nsCOMPtr<nsIHttpChannel>                 mHttpChannel;
   nsCOMPtr<nsILoadGroup>                   mLoadGroup;
   nsCOMPtr<nsICancelable>                  mDNSRequest;
   nsCOMPtr<nsIAsyncVerifyRedirectCallback> mRedirectCallback;
+  nsCOMPtr<nsIRandomGenerator>             mRandomGenerator;
   
   nsCString                       mProtocol;
   nsCString                       mOrigin;
   nsCString                       mHashedSecret;
   nsCString                       mAddress;
 
   nsCOMPtr<nsISocketTransport>    mTransport;
   nsCOMPtr<nsIAsyncInputStream>   mSocketIn;
new file mode 100644
--- /dev/null
+++ b/netwerk/test/unit/test_double_content_length.js
@@ -0,0 +1,43 @@
+do_load_httpd_js();
+
+var httpserver = new nsHttpServer();
+var index = 0;
+
+function setupChannel(url)
+{
+    var ios = Components.classes["@mozilla.org/network/io-service;1"].
+                         getService(Ci.nsIIOService);
+    var chan = ios.newChannel("http://localhost:4444" + url, "", null);
+    var httpChan = chan.QueryInterface(Components.interfaces.nsIHttpChannel);
+    return httpChan;
+}
+
+function completeTest1(request, data, ctx)
+{
+    httpserver.stop(do_test_finished);
+}
+
+function run_test()
+{
+    httpserver.registerPathHandler("/2xcl", handler);
+    httpserver.start(4444);
+
+    var channel = setupChannel("/2xcl");
+    channel.asyncOpen(new ChannelListener(completeTest1,
+                                          channel, CL_EXPECT_FAILURE), null);
+    do_test_pending();
+}
+
+function handler(metadata, response)
+{
+    var body = "012345678901234567890123456789";
+    response.seizePower();
+    response.write("HTTP/1.0 200 OK\r\n");
+    response.write("Content-Type: text/plain\r\n");
+    response.write("Content-Length: 20\r\n");
+    response.write("Content-Length: 30\r\n");
+    response.write("\r\n");
+    response.write(body);
+    response.finish();
+}
+
--- a/netwerk/test/unit/xpcshell.ini
+++ b/netwerk/test/unit/xpcshell.ini
@@ -63,16 +63,17 @@ tail =
 [test_bug660066.js]
 [test_cacheflags.js]
 [test_channel_close.js]
 [test_compareURIs.js]
 [test_content_sniffer.js]
 [test_cookie_header.js]
 [test_data_protocol.js]
 [test_dns_service.js]
+[test_double_content_length.js]
 [test_event_sink.js]
 [test_extract_charset_from_content_type.js]
 [test_fallback_no-cache-entry_canceled.js]
 [test_fallback_no-cache-entry_passing.js]
 [test_fallback_redirect-to-different-origin_canceled.js]
 [test_fallback_redirect-to-different-origin_passing.js]
 [test_fallback_request-error_canceled.js]
 [test_fallback_request-error_passing.js]
--- a/toolkit/components/passwordmgr/test/Makefile.in
+++ b/toolkit/components/passwordmgr/test/Makefile.in
@@ -30,103 +30,102 @@
 # 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 *****
 
-DEPTH		= ../../../..
-topsrcdir	= @top_srcdir@
-srcdir		= @srcdir@
-VPATH		= @srcdir@
-relativesrcdir  = toolkit/components/passwordmgr/test
+DEPTH          = ../../../..
+topsrcdir      = @top_srcdir@
+srcdir         = @srcdir@
+VPATH          = @srcdir@
+relativesrcdir = toolkit/components/passwordmgr/test
 
 include $(topsrcdir)/config/config.mk
 
 ifneq (mobile,$(MOZ_BUILD_APP))
 DIRS = \
-	browser \
-	$(NULL)
+    browser \
+    $(NULL)
 endif
 
 # Module name for xpcshell tests.
-MODULE		= test_passwordmgr
+MODULE = test_passwordmgr
+XPCSHELL_TESTS = unit
 
 # Mochitest tests
 MOCHI_TESTS = \
-		test_basic_form.html \
-		test_basic_form_html5.html \
-		test_basic_form_2.html \
-		test_basic_form_0pw.html \
-		test_basic_form_1pw.html \
-		test_basic_form_1pw_2.html \
-		test_basic_form_2pw_1.html \
-		test_basic_form_2pw_2.html \
-		test_basic_form_3pw_1.html \
-		test_basic_form_autocomplete.html \
-		test_basic_form_observer_autofillForms.html \
-		test_basic_form_observer_autocomplete.html \
-		test_basic_form_observer_foundLogins.html \
-		test_basic_form_pwonly.html \
-		test_bug_227640.html \
-		test_bug_242956.html \
-		test_bug_360493_1.html \
-		test_bug_360493_2.html \
-		test_bug_391514.html \
-		test_bug_427033.html \
-		test_bug_444968.html \
-		test_master_password.html \
-		test_master_password_cleanup.html \
-		test_prompt_async.html \
-		test_xhr.html \
-		test_xml_load.html \
-		test_zzz_finish.html \
-		$(NULL)
+    test_basic_form.html \
+    test_basic_form_html5.html \
+    test_basic_form_2.html \
+    test_basic_form_0pw.html \
+    test_basic_form_1pw.html \
+    test_basic_form_1pw_2.html \
+    test_basic_form_2pw_1.html \
+    test_basic_form_2pw_2.html \
+    test_basic_form_3pw_1.html \
+    test_basic_form_autocomplete.html \
+    test_basic_form_observer_autofillForms.html \
+    test_basic_form_observer_autocomplete.html \
+    test_basic_form_observer_foundLogins.html \
+    test_basic_form_pwonly.html \
+    test_bug_227640.html \
+    test_bug_242956.html \
+    test_bug_360493_1.html \
+    test_bug_360493_2.html \
+    test_bug_391514.html \
+    test_bug_427033.html \
+    test_bug_444968.html \
+    test_master_password.html \
+    test_master_password_cleanup.html \
+    test_prompt_async.html \
+    test_xhr.html \
+    test_xml_load.html \
+    test_zzz_finish.html \
+    $(NULL)
 
 MOCHI_CONTENT = \
-		pwmgr_common.js \
-		prompt_common.js \
-		notification_common.js \
-		authenticate.sjs \
-		formsubmit.sjs \
-		subtst_privbrowsing_1.html \
-		subtst_privbrowsing_2.html \
-		subtst_privbrowsing_3.html \
-		subtst_privbrowsing_4.html \
-		subtst_master_pass.html \
-		subtst_notifications_1.html \
-		subtst_notifications_2.html \
-		subtst_notifications_3.html \
-		subtst_notifications_4.html \
-		subtst_notifications_5.html \
-		subtst_notifications_6.html \
-		subtst_notifications_7.html \
-		subtst_notifications_8.html \
-		subtst_notifications_9.html \
-		subtst_notifications_10.html \
-		subtst_prompt_async.html \
-		$(NULL)
+    pwmgr_common.js \
+    prompt_common.js \
+    notification_common.js \
+    authenticate.sjs \
+    formsubmit.sjs \
+    subtst_privbrowsing_1.html \
+    subtst_privbrowsing_2.html \
+    subtst_privbrowsing_3.html \
+    subtst_privbrowsing_4.html \
+    subtst_master_pass.html \
+    subtst_notifications_1.html \
+    subtst_notifications_2.html \
+    subtst_notifications_3.html \
+    subtst_notifications_4.html \
+    subtst_notifications_5.html \
+    subtst_notifications_6.html \
+    subtst_notifications_7.html \
+    subtst_notifications_8.html \
+    subtst_notifications_9.html \
+    subtst_notifications_10.html \
+    subtst_prompt_async.html \
+    $(NULL)
 
 # Don't run these tests in suite
 ifndef MOZ_SUITE
 MOCHI_TESTS += \
-		test_privbrowsing.html \
-		test_prompt.html \
-		test_notifications.html \
-		$(NULL)
+    test_privbrowsing.html \
+    test_prompt.html \
+    test_notifications.html \
+    $(NULL)
 else
 $(warning test_prompt.html is disabled until doorhanger notfications work. Bug 598360)
 $(warning test_notifications.html is disabled until doorhanger notfications work. Bug 598360)
 $(warning test_privbrowsing.html is disabled due to doorhangers, Bug 598360, and no privatebrowsing support)
 endif
 
-XPCSHELL_TESTS  = unit
-
 # This test doesn't pass because we can't ensure a cross-platform
 # event that occurs between DOMContentLoaded and Pageload
 # test_bug_221634.html
 
 include $(topsrcdir)/config/rules.mk
 
 libs:: $(MOCHI_TESTS) $(MOCHI_CONTENT)
 	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/tests/$(relativesrcdir)
--- a/toolkit/components/passwordmgr/test/notification_common.js
+++ b/toolkit/components/passwordmgr/test/notification_common.js
@@ -47,8 +47,33 @@ function clickPopupButton(aPopup, aButto
     }
 }
 
 const kRememberButton = 0;
 const kNeverButton = 1;
 
 const kChangeButton = 0;
 const kDontChangeButton = 1;
+
+function dumpNotifications() {
+  try {
+    // PopupNotifications
+    var container = getPopupNotifications(window.top);
+    ok(true, "is popup panel open? " + container.isPanelOpen);
+    var notes = container._currentNotifications;
+    ok(true, "Found " + notes.length + " popup notifications.");
+    for (var i = 0; i < notes.length; i++) {
+        ok(true, "#" + i + ": " + notes[i].id);
+    }
+
+    // Notification bars
+    var chromeWin = window.top.QueryInterface(Ci.nsIInterfaceRequestor)
+                           .getInterface(Ci.nsIWebNavigation)
+                           .QueryInterface(Ci.nsIDocShell)
+                           .chromeEventHandler.ownerDocument.defaultView;
+    var nb = chromeWin.getNotificationBox(window.top);
+    var notes = nb.allNotifications;
+    ok(true, "Found " + notes.length + " notification bars.");
+    for (var i = 0; i < notes.length; i++) {
+        ok(true, "#" + i + ": " + notes[i].getAttribute("value"));
+    }
+  } catch(e) { todo(false, "WOAH! " + e); }
+}
--- a/toolkit/components/passwordmgr/test/test_prompt.html
+++ b/toolkit/components/passwordmgr/test/test_prompt.html
@@ -140,17 +140,19 @@ var storageObserver = {
                         Ci.nsISupports, Ci.nsISupportsWeakReference];
 
     if (!interfaces.some( function(v) { return iid.equals(v) } ))
       throw Components.results.NS_ERROR_NO_INTERFACE;
     return this;
   },
 
   observe : function (subject, topic, data) {
+    ok(true, ".");
     ok(true, "observer for " + topic + " / " + data);
+    ok(true, "Time is " + (new Date()).toUTCString());
   netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
     try {
       switch (data) {
         case "addLogin":
             ok(subject instanceof Ci.nsILoginInfo, "subject QI 1");
             ok(subject instanceof Ci.nsILoginMetaInfo, "subject QI 2");
             dumpLogin("added: ", subject);
             break;
@@ -194,17 +196,20 @@ observerService.addObserver(storageObser
  *
  * Invoked a short period of time after calling startCallbackTimer(), and
  * allows testing the actual auth dialog while it's being displayed. Tests
  * should call startCallbackTimer() each time the auth dialog is expected (the
  * timer is a one-shot).
  */
 function handleDialog(doc, testNum) {
   netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
+  ok(true, "."); // make it easier to see next line in logs.
   ok(true, "handleDialog running for test " + testNum);
+  ok(true, "Time is " + (new Date()).toUTCString());
+dumpNotifications();
 
   var clickOK = true;
   var body = doc.getElementById("info.body");
   var userfield = doc.getElementById("loginTextbox");
   var passfield = doc.getElementById("password1Textbox");
   var username = userfield.getAttribute("value");
   var password = passfield.getAttribute("value");
   var dialog    = doc.getElementById("commonDialog");
@@ -423,17 +428,20 @@ function handleDialog(doc, testNum) {
 
 /*
  * handleLoad
  *
  * Called when a load event is fired at the subtest's iframe.
  */
 function handleLoad() {
   netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
+  ok(true, "."); // make it easier to see next line in logs.
   ok(true, "handleLoad running for test " + testNum);
+  ok(true, "Time is " + (new Date()).toUTCString());
+dumpNotifications();
 
   if (testNum != 1002)
     ok(didDialog, "handleDialog was invoked");
 
   // The server echos back the user/pass it received.
   var username = iframe.contentDocument.getElementById("user").textContent;
   var password = iframe.contentDocument.getElementById("pass").textContent;
   var authok = iframe.contentDocument.getElementById("ok").textContent;
@@ -974,62 +982,66 @@ is(authinfo.password, "user1pass", "Chec
 
 // ===== test 505 =====
 // test filling in existing login (undetermined --> user2)
 testNum++;
 authinfo.username = "";
 authinfo.password = "";
 authinfo.realm    = "http://example2.com";
 
+dumpNotifications();
 startCallbackTimer();
 isOk = prompter2.promptAuth(channel2, level, authinfo);
 
 ok(isOk, "Checking dialog return value (accept)");
 ok(didDialog, "handleDialog was invoked");
 is(authinfo.username, "user2name", "Checking returned username");
 is(authinfo.password, "user2pass", "Checking returned password");
 
 // ===== test 506 =====
 // test changing a password (undetermined --> user2 w/ newpass)
 testNum++;
 authinfo.username = "";
 authinfo.password = "";
 authinfo.realm    = "http://example2.com";
 
+dumpNotifications();
 startCallbackTimer();
 isOk = prompter2.promptAuth(channel2, level, authinfo);
 
 ok(isOk, "Checking dialog return value (accept)");
 ok(didDialog, "handleDialog was invoked");
 is(authinfo.username, "user2name", "Checking returned username");
 is(authinfo.password, "NEWuser2pass", "Checking returned password");
 
 // ===== test 507 =====
 // test changing a password (undetermined --> user2 w/ origpass)
 testNum++;
 authinfo.username = "";
 authinfo.password = "";
 authinfo.realm    = "http://example2.com";
 
+dumpNotifications();
 startCallbackTimer();
 isOk = prompter2.promptAuth(channel2, level, authinfo);
 
 ok(isOk, "Checking dialog return value (accept)");
 ok(didDialog, "handleDialog was invoked");
 is(authinfo.username, "user2name", "Checking returned username");
 is(authinfo.password, "user2pass", "Checking returned password");
 
 // ===== test 508 =====
 // test proxy login (default = no autologin), make sure it prompts.
 testNum++;
 proxyAuthinfo.username = "";
 proxyAuthinfo.password = "";
 proxyAuthinfo.realm    = "Proxy Realm";
 proxyAuthinfo.flags    = Ci.nsIAuthInformation.AUTH_PROXY;
 
+dumpNotifications();
 var time1 = pwmgr.findLogins({}, mozproxy, null, "Proxy Realm")[0].QueryInterface(Ci.nsILoginMetaInfo).timeLastUsed;
 startCallbackTimer();
 isOk = prompter2.promptAuth(proxyChannel, level, proxyAuthinfo);
 var time2 = pwmgr.findLogins({}, mozproxy, null, "Proxy Realm")[0].QueryInterface(Ci.nsILoginMetaInfo).timeLastUsed;
 
 ok(isOk, "Checking dialog return value (accept)");
 ok(didDialog, "handleDialog was invoked");
 isnot(time1, time2, "Checking that timeLastUsed was updated");
--- a/toolkit/content/WindowDraggingUtils.jsm
+++ b/toolkit/content/WindowDraggingUtils.jsm
@@ -35,20 +35,21 @@
  * ***** END LICENSE BLOCK ***** */
 
 let EXPORTED_SYMBOLS = [ "WindowDraggingElement" ];
 
 function WindowDraggingElement(elem, window) {
   this._elem = elem;
   this._window = window;
 #ifdef XP_WIN
-  this._elem.addEventListener("MozMouseHittest", this, false);
-#else
+  if (!this.isPanel())
+    this._elem.addEventListener("MozMouseHittest", this, false);
+  else
+#endif
   this._elem.addEventListener("mousedown", this, false);
-#endif
 }
 
 WindowDraggingElement.prototype = {
   mouseDownCheck: function(e) { return true; },
   dragTags: ["box", "hbox", "vbox", "spacer", "label", "statusbarpanel", "stack",
              "toolbaritem", "toolbarseparator", "toolbarspring", "toolbarspacer",
              "radiogroup", "deck", "scrollbox", "arrowscrollbox", "tabs"],
   shouldDrag: function(aEvent) {
@@ -74,45 +75,62 @@ WindowDraggingElement.prototype = {
     }
     while (target != this._elem) {
       if (this.dragTags.indexOf(target.localName) == -1)
         return false;
       target = target.parentNode;
     }
     return true;
   },
+  isPanel : function() {
+    return this._elem instanceof Components.interfaces.nsIDOMXULElement &&
+           this._elem.localName == "panel";
+  },
   handleEvent: function(aEvent) {
+    let isPanel = this.isPanel();
 #ifdef XP_WIN
-    if (this.shouldDrag(aEvent))
-      aEvent.preventDefault();
-#else
+    if (!isPanel) {
+      if (this.shouldDrag(aEvent))
+        aEvent.preventDefault();
+      return;
+    }
+#endif
+
     switch (aEvent.type) {
       case "mousedown":
         if (!this.shouldDrag(aEvent))
           return;
 
 #ifdef MOZ_WIDGET_GTK2
         // On GTK, there is a toolkit-level function which handles
         // window dragging, which must be used.
-        this._window.beginWindowMove(aEvent);
+        this._window.beginWindowMove(aEvent, isPanel ? this._elem : null);
 #else
-        this._deltaX = aEvent.screenX - this._window.screenX;
-        this._deltaY = aEvent.screenY - this._window.screenY;
+        if (isPanel) {
+          let screenRect = this._elem.getOuterScreenRect();
+          this._deltaX = aEvent.screenX - screenRect.left;
+          this._deltaY = aEvent.screenY - screenRect.top;
+        }
+        else {
+          this._deltaX = aEvent.screenX - this._window.screenX;
+          this._deltaY = aEvent.screenY - this._window.screenY;
+        }
         this._draggingWindow = true;
         this._window.addEventListener("mousemove", this, false);
         this._window.addEventListener("mouseup", this, false);
 #endif
         break;
       case "mousemove":
-        if (this._draggingWindow)
-          this._window.moveTo(aEvent.screenX - this._deltaX, aEvent.screenY - this._deltaY);
+        if (this._draggingWindow) {
+          let toDrag = this.isPanel() ? this._elem : this._window;
+          toDrag.moveTo(aEvent.screenX - this._deltaX, aEvent.screenY - this._deltaY);
+        }
         break;
       case "mouseup":
         if (this._draggingWindow) {
           this._draggingWindow = false;
           this._window.removeEventListener("mousemove", this, false);
           this._window.removeEventListener("mouseup", this, false);
         }
         break;
     }
-#endif
   }
 }
--- a/toolkit/content/tests/chrome/window_panel.xul
+++ b/toolkit/content/tests/chrome/window_panel.xul
@@ -46,16 +46,17 @@ function ok(condition, message) {
 function is(left, right, message) {
   window.opener.wrappedJSObject.SimpleTest.is(left, right, message);
 }
 
 function test_panels()
 {
   checkTreeCoords();
 
+  addEventListener("popupshowing", popupShowing, false);
   addEventListener("popupshown", popupShown, false);
   addEventListener("popuphidden", nextTest, false);
   nextTest();
 }
 
 function nextTest()
 {
   if (!tests.length) {
@@ -64,16 +65,23 @@ function nextTest()
     return;
   }
 
   currentTest = tests.shift();
   var panel = createPanel(currentTest.attrs);
   currentTest.test(panel);
 }
 
+function popupShowing(event)
+{
+  var rect = event.target.getOuterScreenRect();
+  ok(!rect.left && !rect.top && !rect.width && !rect.height,
+     currentTest.testname + " empty rectangle during popupshowing");
+}
+
 var waitSteps = 0;
 function popupShown(event)
 {
   var panel = event.target;
 
   if (waitSteps > 0 && navigator.platform.indexOf("Linux") >= 0 &&
       panel.boxObject.screenY == 210) {
     waitSteps--;
@@ -116,56 +124,93 @@ function checkTreeCoords()
   is(tree.currentIndex, 3, "tree selection after scroll");
 }
 
 var tests = [
   {
     testname: "normal panel",
     attrs: { },
     test: function(panel) {
+      var screenRect = panel.getOuterScreenRect();
+      is(screenRect.left, 0, this.testname + " screen left before open");
+      is(screenRect.top, 0, this.testname + " screen top before open");
+      is(screenRect.width, 0, this.testname + " screen width before open");
+      is(screenRect.height, 0, this.testname + " screen height before open");
+
       panel.openPopupAtScreen(200, 210);
     },
     result: function(testname, panel) {
       var panelrect = panel.getBoundingClientRect();
       is(panelrect.left, 200 - mozInnerScreenX, testname + "left");
       is(panelrect.top, 210 - mozInnerScreenY, testname + "top");
       is(panelrect.width, 120, testname + "width");
       is(panelrect.height, 40, testname + "height");
+
+      var screenRect = panel.getOuterScreenRect();
+      is(screenRect.left, 200, testname + " screen left");
+      is(screenRect.top, 210, testname + " screen top");
+      is(screenRect.width, 120, testname + " screen width");
+      is(screenRect.height, 40, testname + " screen height");
     }
   },
   {
     // only noautohide panels support titlebars, so one shouldn't be shown here
     testname: "autohide panel with titlebar",
     attrs: { titlebar: "normal" },
     test: function(panel) {
+      var screenRect = panel.getOuterScreenRect();
+      is(screenRect.left, 0, this.testname + " screen left before open");
+      is(screenRect.top, 0, this.testname + " screen top before open");
+      is(screenRect.width, 0, this.testname + " screen width before open");
+      is(screenRect.height, 0, this.testname + " screen height before open");
+
       panel.openPopupAtScreen(200, 210);
     },
     result: function(testname, panel) {
       var panelrect = panel.getBoundingClientRect();
       is(panelrect.left, 200 - mozInnerScreenX, testname + "left");
       is(panelrect.top, 210 - mozInnerScreenY, testname + "top");
       is(panelrect.width, 120, testname + "width");
       is(panelrect.height, 40, testname + "height");
+
+      var screenRect = panel.getOuterScreenRect();
+      is(screenRect.left, 200, testname + " screen left");
+      is(screenRect.top, 210, testname + " screen top");
+      is(screenRect.width, 120, testname + " screen width");
+      is(screenRect.height, 40, testname + " screen height");
     }
   },
   {
     testname: "noautohide panel with titlebar",
     attrs: { noautohide: true, titlebar: "normal" },
     test: function(panel) {
       waitSteps = 25;
+
+      var screenRect = panel.getOuterScreenRect();
+      is(screenRect.left, 0, this.testname + " screen left before open");
+      is(screenRect.top, 0, this.testname + " screen top before open");
+      is(screenRect.width, 0, this.testname + " screen width before open");
+      is(screenRect.height, 0, this.testname + " screen height before open");
+
       panel.openPopupAtScreen(200, 210);
     },
     result: function(testname, panel) {
       var panelrect = panel.getBoundingClientRect();
       ok(panelrect.left >= 200 - mozInnerScreenX, testname + "left");
       ok(panelrect.top >= 210 - mozInnerScreenY + 10, testname + "top greater");
       ok(panelrect.top <= 210 - mozInnerScreenY + 30, testname + "top less");
       is(panelrect.width, 120, testname + "width");
       is(panelrect.height, 40, testname + "height");
 
+      var screenRect = panel.getOuterScreenRect();
+      is(screenRect.left, 200, testname + " screen left");
+      is(screenRect.top, 210, testname + " screen top");
+      ok(screenRect.width >= 120 && screenRect.width <= 140, testname + " screen width");
+      ok(screenRect.height >= 40 && screenRect.height <= 80, testname + " screen height");
+
       var gotMouseEvent = false;
       function mouseMoved(event)
       {
         is(event.clientY, panelrect.top + 10,
            "popup clientY");
         is(event.screenY, panel.boxObject.screenY + 10,
            "popup screenY");
         is(event.originalTarget, panel.firstChild, "popup target");
@@ -177,16 +222,41 @@ var tests = [
       ok(gotMouseEvent, "mouse event on panel");      
       panel.removeEventListener("mousemove", mouseMoved, true);
 
       var tree = $("tree");
       tree.currentIndex = 0;
       panel.appendChild(tree);
       checkTreeCoords();
     }
+  },
+  {
+    testname: "noautohide panel with backdrag",
+    attrs: { noautohide: true, backdrag: "true" },
+    test: function(panel) {
+      var label = document.createElement("label");
+      label.id = "backdragspot";
+      label.setAttribute("value", "Hello There");
+      panel.appendChild(label);
+      panel.openPopupAtScreen(200, 230);
+    },
+    result: function(testname, panel) {
+      var oldrect = panel.getOuterScreenRect();
+
+      // Linux uses native window moving
+      if (navigator.platform.indexOf("Linux") == -1) {
+        var backdragspot = document.getElementById("backdragspot");
+        synthesizeMouse(backdragspot, 5, 5, { type: "mousedown" });
+        synthesizeMouse(backdragspot, 15, 20, { type: "mousemove" });
+        synthesizeMouse(backdragspot, 15, 20, { type: "mouseup" });
+
+        is(panel.getOuterScreenRect().left, 210, testname + "left");
+        is(panel.getOuterScreenRect().top, 245, testname + "top");
+      }
+    }
   }
 ];
 
 window.opener.wrappedJSObject.SimpleTest.waitForFocus(test_panels, window);
 
 ]]>
 </script>
 
--- a/toolkit/content/tests/widgets/test_timepicker.xul
+++ b/toolkit/content/tests/widgets/test_timepicker.xul
@@ -38,16 +38,20 @@ function testtag_timepicker()
   var tsecond = today.getSeconds();
 
   // testtag_comparetime(tp, testid + "initial", thour, tminute, tsecond);
 
   // check that setting the value property works
   tp.value = testtag_gettimestring(thour, tminute, tsecond);
   testtag_comparetime(tp, testid + "set value", thour, tminute, tsecond);
 
+  var numberOrder = /^(\D*)\s*(\d+)(\D*)(\d+)(\D*)(\d+)\s*(\D*)$/;
+  var fdt = new Date(2000,0,1,16,7,9).toLocaleFormat("%X");
+  is(tp.is24HourClock, Number(fdt.match(numberOrder)[2]) > 12, "is24HourClock");
+
   // check that setting the dateValue property works
   tp.dateValue = today;
   testtag_comparetime(tp, testid + "set dateValue", thour, tminute, tsecond);
   ok(tp.value !== today, testid + " set dateValue different time");
 
   ok(!tp.readOnly, testid + "readOnly");
   tp.readOnly = true;
   ok(tp.readOnly, testid + "set readOnly");
--- a/toolkit/content/widgets/datetimepicker.xml
+++ b/toolkit/content/widgets/datetimepicker.xml
@@ -241,22 +241,22 @@
     </handlers>
 
   </binding>
 
   <binding id="timepicker"
            extends="chrome://global/content/bindings/datetimepicker.xml#datetimepicker-base">
 
     <implementation>
-      <field name="is24HourClock" readonly="true">false</field>
-      <field name="hourLeadingZero" readonly="true">false</field>
-      <field name="minuteLeadingZero" readonly="true">true</field>
-      <field name="secondLeadingZero" readonly="true">true</field>
-      <field name="amIndicator" readonly="true">"AM"</field>
-      <field name="pmIndicator" readonly="true">"PM"</field>
+      <field name="is24HourClock">false</field>
+      <field name="hourLeadingZero">false</field>
+      <field name="minuteLeadingZero">true</field>
+      <field name="secondLeadingZero">true</field>
+      <field name="amIndicator">"AM"</field>
+      <field name="pmIndicator">"PM"</field>
 
       <field name="hourField">null</field>
       <field name="minuteField">null</field>
       <field name="secondField">null</field>
 
       <property name="value">
         <getter>
           <![CDATA[
--- a/toolkit/content/widgets/popup.xml
+++ b/toolkit/content/widgets/popup.xml
@@ -155,16 +155,24 @@
         <parameter name="aLeft"/>
         <parameter name="aTop"/>
         <body>
         <![CDATA[
           this.popupBoxObject.moveTo(aLeft, aTop);
         ]]>
         </body>
       </method>
+
+      <method name="getOuterScreenRect">
+        <body>
+        <![CDATA[
+          return this.popupBoxObject.getOuterScreenRect();
+        ]]>
+        </body>
+      </method>
     </implementation>     
 
   </binding>
 
   <binding id="popup"
            extends="chrome://global/content/bindings/popup.xml#popup-base">
     
     <content>
@@ -216,16 +224,30 @@
         <getter>
           <![CDATA[
           return (this.getAttribute("noautofocus") == "true") ?
                        Components.interfaces.nsIAccessibleProvider.XULAlert :
                        Components.interfaces.nsIAccessibleProvider.XULPane;
         ]]></getter>
       </property>
       <field name="_prevFocus">0</field>
+      <field name="_dragBindingAlive">true</field>
+      <constructor>
+      <![CDATA[
+        if (this.getAttribute("backdrag") == "true" && !this._draggableStarted) {
+          this._draggableStarted = true;
+          try {
+            let tmp = {};
+            Components.utils.import("resource://gre/modules/WindowDraggingUtils.jsm", tmp);
+            let draghandle = new tmp.WindowDraggingElement(this, window);
+            draghandle.mouseDownCheck = function () this._dragBindingAlive;
+          } catch (e) {}
+        }
+      ]]>
+      </constructor>
     </implementation>
     
     <handlers>
       <handler event="popupshowing"><![CDATA[
         // Capture the previous focus before has a chance to get set inside the panel
         try {
           this._prevFocus = document.commandDispatcher.focusedElement;
           if (!this._prevFocus)  // Content window has focus
--- a/toolkit/mozapps/extensions/AddonManager.jsm
+++ b/toolkit/mozapps/extensions/AddonManager.jsm
@@ -1207,16 +1207,22 @@ var AddonManager = {
   // Indicates that the Addon should not update automatically.
   AUTOUPDATE_DISABLE: 0,
   // Indicates that the Addon should update automatically only if
   // that's the global default.
   AUTOUPDATE_DEFAULT: 1,
   // Indicates that the Addon should update automatically.
   AUTOUPDATE_ENABLE: 2,
 
+  // Constants for how Addon options should be shown.
+  // Options will be opened in a new window
+  OPTIONS_TYPE_DIALOG: 1,
+  // Options will be displayed within the AM detail view
+  OPTIONS_TYPE_INLINE: 2,
+
   getInstallForURL: function AM_getInstallForURL(aUrl, aCallback, aMimetype,
                                                  aHash, aName, aIconURL,
                                                  aVersion, aLoadGroup) {
     AddonManagerInternal.getInstallForURL(aUrl, aCallback, aMimetype, aHash,
                                           aName, aIconURL, aVersion, aLoadGroup);
   },
 
   getInstallForFile: function AM_getInstallForFile(aFile, aCallback, aMimetype) {
--- a/toolkit/mozapps/extensions/XPIProvider.jsm
+++ b/toolkit/mozapps/extensions/XPIProvider.jsm
@@ -113,31 +113,31 @@ const PREFIX_ITEM_URI                 = 
 const RDFURI_ITEM_ROOT                = "urn:mozilla:item:root"
 const RDFURI_INSTALL_MANIFEST_ROOT    = "urn:mozilla:install-manifest";
 const PREFIX_NS_EM                    = "http://www.mozilla.org/2004/em-rdf#";
 
 const TOOLKIT_ID                      = "toolkit@mozilla.org";
 
 const BRANCH_REGEXP                   = /^([^\.]+\.[0-9]+[a-z]*).*/gi;
 
-const DB_SCHEMA                       = 4;
+const DB_SCHEMA                       = 5;
 const REQ_VERSION                     = 2;
 
 #ifdef MOZ_COMPATABILITY_NIGHTLY
 const PREF_EM_CHECK_COMPATIBILITY = PREF_EM_CHECK_COMPATIBILITY_BASE +
                                     ".nightly";
 #else
 const PREF_EM_CHECK_COMPATIBILITY = PREF_EM_CHECK_COMPATIBILITY_BASE + "." +
                                     Services.appinfo.version.replace(BRANCH_REGEXP, "$1");
 #endif
 
 // Properties that exist in the install manifest
 const PROP_METADATA      = ["id", "version", "type", "internalName", "updateURL",
-                            "updateKey", "optionsURL", "aboutURL", "iconURL",
-                            "icon64URL"];
+                            "updateKey", "optionsURL", "optionsType", "aboutURL",
+                            "iconURL", "icon64URL"];
 const PROP_LOCALE_SINGLE = ["name", "description", "creator", "homepageURL"];
 const PROP_LOCALE_MULTI  = ["developers", "translators", "contributors"];
 const PROP_TARGETAPP     = ["id", "minVersion", "maxVersion"];
 
 // Properties that only exist in the database
 const DB_METADATA        = ["installDate", "updateDate", "size", "sourceURI",
                             "releaseNotesURI", "applyBackgroundUpdates"];
 const DB_BOOL_METADATA   = ["visible", "active", "userDisabled", "appDisabled",
@@ -670,18 +670,20 @@ function loadManifestFromRDF(aUri, aStre
     addon[aProp] = getRDFProperty(ds, root, aProp);
   });
   addon.unpack = getRDFProperty(ds, root, "unpack") == "true";
 
   if (!addon.type) {
     addon.type = addon.internalName ? "theme" : "extension";
   }
   else {
+    let type = addon.type;
+    addon.type = null;
     for (let name in TYPES) {
-      if (TYPES[name] == addon.type) {
+      if (TYPES[name] == type) {
         addon.type = name;
         break;
       }
     }
   }
 
   if (!(addon.type in TYPES))
     throw new Error("Install manifest specifies unknown type: " + addon.type);
@@ -693,21 +695,27 @@ function loadManifestFromRDF(aUri, aStre
       throw new Error("Illegal add-on ID " + addon.id);
     if (!addon.version)
       throw new Error("No version in install manifest");
   }
 
   // Only read the bootstrapped property for extensions
   if (addon.type == "extension") {
     addon.bootstrap = getRDFProperty(ds, root, "bootstrap") == "true";
+    if (addon.optionsType &&
+        addon.optionsType != AddonManager.OPTIONS_TYPE_DIALOG &&
+        addon.optionsType != AddonManager.OPTIONS_TYPE_INLINE) {
+      throw new Error("Install manifest specifies unknown type: " + addon.optionsType);
+    }
   }
   else {
-    // Only extensions are allowed to provide an optionsURL or aboutURL. For
+    // Only extensions are allowed to provide an optionsURL, optionsType or aboutURL. For
     // all other types they are silently ignored
     addon.optionsURL = null;
+    addon.optionsType = null;
     addon.aboutURL = null;
 
     if (addon.type == "theme") {
       if (!addon.internalName)
         throw new Error("Themes must include an internalName property");
       addon.skinnable = getRDFProperty(ds, root, "skinnable") == "true";
     }
   }
@@ -3668,21 +3676,21 @@ var XPIProvider = {
 
     // Notify any other providers that this theme is now enabled again.
     if (aAddon.type == "theme" && aAddon.active)
       AddonManagerPrivate.notifyAddonChanged(aAddon.id, aAddon.type, false);
   }
 };
 
 const FIELDS_ADDON = "internal_id, id, location, version, type, internalName, " +
-                     "updateURL, updateKey, optionsURL, aboutURL, iconURL, " +
-                     "icon64URL, defaultLocale, visible, active, userDisabled, " +
-                     "appDisabled, pendingUninstall, descriptor, installDate, " +
-                     "updateDate, applyBackgroundUpdates, bootstrap, skinnable, " +
-                     "size, sourceURI, releaseNotesURI, softDisabled";
+                     "updateURL, updateKey, optionsURL, optionsType, aboutURL, " +
+                     "iconURL, icon64URL, defaultLocale, visible, active, " +
+                     "userDisabled, appDisabled, pendingUninstall, descriptor, " +
+                     "installDate, updateDate, applyBackgroundUpdates, bootstrap, " +
+                     "skinnable, size, sourceURI, releaseNotesURI, softDisabled";
 
 /**
  * A helper function to log an SQL error.
  *
  * @param  aError
  *         The storage error code associated with the error
  * @param  aErrorString
  *         An error message
@@ -3812,18 +3820,18 @@ var XPIDatabase = {
                             "addon_internal_id=:internal_id",
     _getTargetPlatforms: "SELECT os, abi FROM targetPlatform WHERE " +
                          "addon_internal_id=:internal_id",
     _readLocaleStrings: "SELECT locale_id, type, value FROM locale_strings " +
                         "WHERE locale_id=:id",
 
     addAddonMetadata_addon: "INSERT INTO addon VALUES (NULL, :id, :location, " +
                             ":version, :type, :internalName, :updateURL, " +
-                            ":updateKey, :optionsURL, :aboutURL, :iconURL, " +
-                            ":icon64URL, :locale, :visible, :active, " +
+                            ":updateKey, :optionsURL, :optionsType, :aboutURL, " +
+                            ":iconURL, :icon64URL, :locale, :visible, :active, " +
                             ":userDisabled, :appDisabled, :pendingUninstall, " +
                             ":descriptor, :installDate, :updateDate, " +
                             ":applyBackgroundUpdates, :bootstrap, :skinnable, " +
                             ":size, :sourceURI, :releaseNotesURI, :softDisabled)",
     addAddonMetadata_addon_locale: "INSERT INTO addon_locale VALUES " +
                                    "(:internal_id, :name, :locale)",
     addAddonMetadata_locale: "INSERT INTO locale (name, description, creator, " +
                              "homepageURL) VALUES (:name, :description, " +
@@ -4321,19 +4329,19 @@ var XPIDatabase = {
     this.beginTransaction();
 
     // Any errors in here should rollback the transaction
     try {
       this.connection.createTable("addon",
                                   "internal_id INTEGER PRIMARY KEY AUTOINCREMENT, " +
                                   "id TEXT, location TEXT, version TEXT, " +
                                   "type TEXT, internalName TEXT, updateURL TEXT, " +
-                                  "updateKey TEXT, optionsURL TEXT, aboutURL TEXT, " +
-                                  "iconURL TEXT, icon64URL TEXT, " +
-                                  "defaultLocale INTEGER, " +
+                                  "updateKey TEXT, optionsURL TEXT, " +
+                                  "optionsType TEXT, aboutURL TEXT, iconURL TEXT, " +
+                                  "icon64URL TEXT, defaultLocale INTEGER, " +
                                   "visible INTEGER, active INTEGER, " +
                                   "userDisabled INTEGER, appDisabled INTEGER, " +
                                   "pendingUninstall INTEGER, descriptor TEXT, " +
                                   "installDate INTEGER, updateDate INTEGER, " +
                                   "applyBackgroundUpdates INTEGER, " +
                                   "bootstrap INTEGER, skinnable INTEGER, " +
                                   "size INTEGER, sourceURI TEXT, " +
                                   "releaseNotesURI TEXT, softDisabled INTEGER, " +
@@ -6799,52 +6807,75 @@ function AddonWrapper(aAddon) {
     this.__defineGetter__(aProp, function() {
       if (aAddon._repositoryAddon)
         return aAddon._repositoryAddon[aProp];
 
       return null;
     });
   }, this);
 
-  ["optionsURL", "aboutURL"].forEach(function(aProp) {
-    this.__defineGetter__(aProp, function() {
-      return this.isActive ? aAddon[aProp] : null;
-    });
-  }, this);
+  this.__defineGetter__("aboutURL", function() {
+    return this.isActive ? aAddon["aboutURL"] : null;
+  });
 
   ["installDate", "updateDate"].forEach(function(aProp) {
     this.__defineGetter__(aProp, function() new Date(aAddon[aProp]));
   }, this);
 
   ["sourceURI", "releaseNotesURI"].forEach(function(aProp) {
     this.__defineGetter__(aProp, function() {
       let target = chooseValue(aAddon, aProp)[0];
       if (!target)
         return null;
       return NetUtil.newURI(target);
     });
   }, this);
 
-  // Maps iconURL and icon64URL to the properties of the same name or icon.png
-  // and icon64.png in the add-on's files.
-  ["icon", "icon64"].forEach(function(aProp) {
+  // Maps iconURL, icon64URL and optionsURL to the properties of the same name
+  // or icon.png, icon64.png and options.xul in the add-on's files.
+  ["icon", "icon64", "options"].forEach(function(aProp) {
     this.__defineGetter__(aProp + "URL", function() {
       if (this.isActive && aAddon[aProp + "URL"])
         return aAddon[aProp + "URL"];
 
-      if (this.hasResource(aProp + ".png"))
-        return this.getResourceURI(aProp + ".png").spec;
+      switch (aProp) {
+        case "icon":
+        case "icon64":
+          if (this.hasResource(aProp + ".png"))
+            return this.getResourceURI(aProp + ".png").spec;
+          break;
+        case "options":
+          if (this.isActive && this.hasResource(aProp + ".xul"))
+            return this.getResourceURI(aProp + ".xul").spec;
+          break;
+      }
 
       if (aAddon._repositoryAddon)
         return aAddon._repositoryAddon[aProp + "URL"];
 
       return null;
     }, this);
   }, this);
 
+  this.__defineGetter__("optionsType", function() {
+    if (!this.isActive)
+      return null;
+
+    if (aAddon.optionsType)
+      return aAddon.optionsType;
+
+    if (this.hasResource("options.xul"))
+      return AddonManager.OPTIONS_TYPE_INLINE;
+
+    if (this.optionsURL)
+      return AddonManager.OPTIONS_TYPE_DIALOG;
+
+    return null;
+  }, this);
+
   PROP_LOCALE_SINGLE.forEach(function(aProp) {
     this.__defineGetter__(aProp, function() {
       // Override XPI creator if repository creator is defined
       if (aProp == "creator" &&
           aAddon._repositoryAddon && aAddon._repositoryAddon.creator) {
         return aAddon._repositoryAddon.creator;
       }
 
--- a/toolkit/mozapps/extensions/content/extensions.css
+++ b/toolkit/mozapps/extensions/content/extensions.css
@@ -83,16 +83,44 @@ xhtml|link {
 .install-status {
   -moz-binding: url("chrome://mozapps/content/extensions/extensions.xml#install-status");
 }
 
 .detail-row {
   -moz-binding: url("chrome://mozapps/content/extensions/extensions.xml#detail-row");
 }
 
+settings {
+  -moz-binding: url("chrome://mozapps/content/extensions/setting.xml#settings");
+}
+
+setting[type="bool"] {
+  -moz-binding: url("chrome://mozapps/content/extensions/setting.xml#setting-bool");
+}
+
+setting[type="bool"][localized="true"] {
+  -moz-binding: url("chrome://mozapps/content/extensions/setting.xml#setting-localized-bool");
+}
+
+setting[type="boolint"] {
+  -moz-binding: url("chrome://mozapps/content/extensions/setting.xml#setting-boolint");
+}
+
+setting[type="integer"] {
+  -moz-binding: url("chrome://mozapps/content/extensions/setting.xml#setting-integer");
+}
+
+setting[type="control"] {
+  -moz-binding: url("chrome://mozapps/content/extensions/setting.xml#setting-control");
+}
+
+setting[type="string"] {
+  -moz-binding: url("chrome://mozapps/content/extensions/setting.xml#setting-string");
+}
+
 #addonitem-popup > menuitem[disabled="true"] {
   display: none;
 }
 
 #addonitem-popup[addontype="theme"] > #menuitem_enableItem,
 #addonitem-popup[addontype="theme"] > #menuitem_disableItem,
 #addonitem-popup:not([addontype="theme"]) > #menuitem_enableTheme,
 #addonitem-popup:not([addontype="theme"]) > #menuitem_disableTheme {
--- a/toolkit/mozapps/extensions/content/extensions.js
+++ b/toolkit/mozapps/extensions/content/extensions.js
@@ -919,21 +919,30 @@ var gViewController = {
         };
         gEventManager.delegateAddonEvent("onCheckingUpdate", [aAddon]);
         aAddon.findUpdates(listener, AddonManager.UPDATE_WHEN_USER_REQUESTED);
       }
     },
 
     cmd_showItemPreferences: {
       isEnabled: function(aAddon) {
-        if (!aAddon)
+        if (!aAddon || !aAddon.isActive || !aAddon.optionsURL)
           return false;
-        return aAddon.isActive && !!aAddon.optionsURL;
+        if (gViewController.currentViewObj == gDetailView &&
+            aAddon.optionsType == AddonManager.OPTIONS_TYPE_INLINE) {
+          return false;
+        }
+        return true;
       },
       doCommand: function(aAddon) {
+        if (gViewController.currentViewObj == gListView &&
+            aAddon.optionsType == AddonManager.OPTIONS_TYPE_INLINE) {
+          gViewController.commands.cmd_showItemDetails.doCommand(aAddon);
+          return;
+        }
         var optionsURL = aAddon.optionsURL;
         var windows = Services.wm.getEnumerator(null);
         while (windows.hasMoreElements()) {
           var win = windows.getNext();
           if (win.document.documentURI == optionsURL) {
             win.focus();
             return;
           }
@@ -2649,28 +2658,31 @@ var gDetailView = {
       this._autoUpdate.value = aAddon.applyBackgroundUpdates;
       let hideFindUpdates = shouldAutoUpdate(this._addon);
       document.getElementById("detail-findUpdates-btn").hidden = hideFindUpdates;
     } else {
       this._autoUpdate.hidden = true;
       document.getElementById("detail-findUpdates-btn").hidden = false;
     }
 
-    document.getElementById("detail-prefs-btn").hidden = !aIsRemote && !aAddon.optionsURL;
+    document.getElementById("detail-prefs-btn").hidden = !aIsRemote &&
+      !gViewController.commands.cmd_showItemPreferences.isEnabled(aAddon);
     
     var gridRows = document.querySelectorAll("#detail-grid rows row");
     for (var i = 0, first = true; i < gridRows.length; ++i) {
       if (first && window.getComputedStyle(gridRows[i], null).getPropertyValue("display") != "none") {
         gridRows[i].setAttribute("first-row", true);
         first = false;
       } else {
         gridRows[i].removeAttribute("first-row");
       }
     }
 
+    this.fillSettingsRows();
+
     this.updateState();
 
     gViewController.updateCommands();
     gViewController.notifyViewChanged();
   },
 
   show: function(aAddonId, aRequest) {
     var self = this;
@@ -2792,34 +2804,101 @@ var gDetailView = {
     if (this._loadingTimer) {
       clearTimeout(this._loadingTimer);
       this._loadingTimer = null;
     }
 
     this.node.removeAttribute("loading-extended");
   },
 
+  emptySettingsRows: function () {
+    var lastRow = document.getElementById("detail-downloads");
+    var rows = lastRow.parentNode;
+    while (lastRow.nextSibling)
+      rows.removeChild(rows.lastChild);
+  },
+
+  fillSettingsRows: function () {
+    this.emptySettingsRows();
+    if (this._addon.optionsType != AddonManager.OPTIONS_TYPE_INLINE)
+      return;
+
+    var rows = document.getElementById("detail-downloads").parentNode;
+
+    var xhr = new XMLHttpRequest();
+    xhr.open("GET", this._addon.optionsURL, false);
+    xhr.send();
+
+    var xml = xhr.responseXML;
+    var settings = xml.querySelectorAll(":root > setting");
+
+    // This horrible piece of code fixes two problems. 1) The menulist binding doesn't apply
+    // correctly when it's moved from one document to another (bug 659163), which is solved 
+    // by manually cloning the menulist. 2) Labels and controls aligned to the top of a row 
+    // looks really bad, so the description is put on a new row to preserve alignment.
+    for (var i = 0; i < settings.length; i++) {
+      var setting = settings[i];
+      if (i == 0)
+        setting.setAttribute("first-row", true);
+
+      // remove menulist controls for replacement later
+      var control = setting.firstElementChild;
+      if (setting.getAttribute("type") == "control" && control && control.localName == "menulist") {
+        setting.removeChild(control);
+        var consoleMessage = Cc["@mozilla.org/scripterror;1"].
+                             createInstance(Ci.nsIScriptError);
+        consoleMessage.init("Menulist is not available in the addons-manager yet, due to bug 659163",
+                            this._addon.optionsURL, null, null, 0, Ci.nsIScriptError.warningFlag, null);
+        Services.console.logMessage(consoleMessage);
+        continue;
+      }
+
+      // remove setting description, for replacement later
+      var desc = setting.textContent.trim();
+      if (desc)
+        setting.textContent = "";
+      if (setting.hasAttribute("desc")) {
+        desc = setting.getAttribute("desc");
+        setting.removeAttribute("desc");
+      }
+
+      rows.appendChild(setting);
+
+      // add a new row containing the description
+      if (desc) {
+        var row = document.createElement("row");
+        var label = document.createElement("label");
+        label.className = "preferences-description";
+        label.textContent = desc;
+        row.appendChild(label);
+        rows.appendChild(row);
+      }
+    }
+  },
+
   getSelectedAddon: function() {
     return this._addon;
   },
 
   onEnabling: function() {
     this.updateState();
   },
 
   onEnabled: function() {
     this.updateState();
+    this.fillSettingsRows();
   },
 
   onDisabling: function() {
     this.updateState();
   },
 
   onDisabled: function() {
     this.updateState();
+    this.emptySettingsRows();
   },
 
   onUninstalling: function() {
     this.updateState();
   },
 
   onUninstalled: function() {
     gViewController.popState();
rename from mobile/chrome/content/bindings/setting.xml
rename to toolkit/mozapps/extensions/content/setting.xml
--- a/mobile/chrome/content/bindings/setting.xml
+++ b/toolkit/mozapps/extensions/content/setting.xml
@@ -156,29 +156,27 @@
       <field name="input">document.getAnonymousElementByAttribute(this, "anonid", "input");</field>
 
       <field name="settings">
         this.parentNode.localName == "settings" ? this.parentNode : null;
       </field>
     </implementation>
   </binding>
 
-  <binding id="setting-bool" extends="chrome://browser/content/bindings/setting.xml#setting-base">
+  <binding id="setting-bool" extends="chrome://mozapps/content/extensions/setting.xml#setting-base">
     <content>
-      <xul:box flex="1" class="prefbox setting-bool">
-        <xul:vbox flex="1">
-          <xul:label class="preftitle" xbl:inherits="value=title" crop="end" flex="1"/>
-          <xul:label class="prefdesc" xbl:inherits="value=desc" crop="end" flex="1">
-            <children/>
-          </xul:label>
-        </xul:vbox>
-        <xul:hbox anonid="input-container" class="setting-input">
-          <xul:checkbox anonid="input" xbl:inherits="disabled,onlabel,offlabel" oncommand="inputChanged();"/>
-        </xul:hbox>
-      </xul:box>
+      <xul:vbox class="setting-label">
+        <xul:label class="preferences-title" xbl:inherits="value=title" crop="end" flex="1"/>
+        <xul:label class="preferences-description" xbl:inherits="value=desc" crop="end" flex="1">
+          <children/>
+        </xul:label>
+      </xul:vbox>
+      <xul:hbox anonid="input-container" class="setting-input">
+        <xul:checkbox anonid="input" xbl:inherits="disabled,onlabel,offlabel" oncommand="inputChanged();"/>
+      </xul:hbox>
     </content>
 
     <implementation>
       <method name="valueFromPreference">
         <body>
         <![CDATA[
           let val = Services.prefs.getBoolPref(this.pref);
           this.value = this.inverted ? !val : val;
@@ -195,29 +193,27 @@
         </body>
       </method>
 
       <property name="value" onget="return this.input.checked;" onset="return this.input.setChecked(val);"/>
       <property name="inverted" readonly="true" onget="return this.getAttribute('inverted');"/>
     </implementation>
   </binding>
 
-  <binding id="setting-boolint" extends="chrome://browser/content/bindings/setting.xml#setting-base">
+  <binding id="setting-boolint" extends="chrome://mozapps/content/extensions/setting.xml#setting-base">
     <content>
-      <xul:box flex="1" class="prefbox setting-boolint">
-        <xul:vbox flex="1">
-          <xul:label class="preftitle" xbl:inherits="value=title" crop="end" flex="1"/>
-          <xul:label class="prefdesc" xbl:inherits="value=desc" crop="end" flex="1">
-            <children/>
-          </xul:label>
-        </xul:vbox>
-        <xul:hbox anonid="input-container" class="setting-input">
-          <xul:checkbox anonid="input" xbl:inherits="disabled" oncommand="inputChanged();"/>
-        </xul:hbox>
-      </xul:box>
+      <xul:vbox class="setting-label">
+        <xul:label class="preferences-title" xbl:inherits="value=title" crop="end" flex="1"/>
+        <xul:label class="preferences-description" xbl:inherits="value=desc" crop="end" flex="1">
+          <children/>
+        </xul:label>
+      </xul:vbox>
+      <xul:hbox anonid="input-container" class="setting-input">
+        <xul:checkbox anonid="input" xbl:inherits="disabled" oncommand="inputChanged();"/>
+      </xul:hbox>
     </content>
 
     <implementation>
       <method name="valueFromPreference">
         <body>
         <![CDATA[
           let val = Services.prefs.getIntPref(this.pref);
           this.value = (val == this.getAttribute("on"));
@@ -233,17 +229,17 @@
         </body>
       </method>
 
       <property name="value" onget="return this.input.checked;" onset="return this.input.setChecked(val);"/>
       <property name="inverted" readonly="true" onget="return this.getAttribute('inverted');"/>
     </implementation>
   </binding>
 
-  <binding id="setting-localized-bool" extends="chrome://browser/content/bindings/setting.xml#setting-bool">
+  <binding id="setting-localized-bool" extends="chrome://mozapps/content/extensions/setting.xml#setting-bool">
     <implementation>
       <method name="valueFromPreference">
         <body>
         <![CDATA[
           let val = Services.prefs.getComplexValue(this.pref, Components.interfaces.nsIPrefLocalizedString).data;
           if(this.inverted) val = !val;
           this.value = (val == "true");
          ]]>
@@ -260,29 +256,27 @@
           Services.prefs.setComplexValue(this.pref, Components.interfaces.nsIPrefLocalizedString, pref);
         ]]>
         </body>
       </method>
 
     </implementation>
   </binding>
 
-  <binding id="setting-integer" extends="chrome://browser/content/bindings/setting.xml#setting-base">
+  <binding id="setting-integer" extends="chrome://mozapps/content/extensions/setting.xml#setting-base">
     <content>
-      <xul:box flex="1" class="prefbox setting-integer">
-        <xul:vbox flex="1">
-          <xul:label class="preftitle" xbl:inherits="value=title" crop="end" flex="1"/>
-          <xul:label class="prefdesc" xbl:inherits="value=desc" crop="end" flex="1">
-            <children/>
-          </xul:label>
-        </xul:vbox>
-        <xul:hbox anonid="input-container" class="setting-input">
-          <xul:textbox type="number" anonid="input" xbl:inherits="disabled,emptytext,min,max,increment,hidespinbuttons,wraparound" oninput="inputChanged();" oncommand="inputChanged();"/>
-        </xul:hbox>
-      </xul:box>
+      <xul:vbox class="setting-label">
+        <xul:label class="preferences-title" xbl:inherits="value=title" crop="end" flex="1"/>
+        <xul:label class="preferences-description" xbl:inherits="value=desc" crop="end" flex="1">
+          <children/>
+        </xul:label>
+      </xul:vbox>
+      <xul:hbox anonid="input-container" class="setting-input">
+        <xul:textbox type="number" anonid="input" xbl:inherits="disabled,emptytext,min,max,increment,hidespinbuttons,wraparound" oninput="inputChanged();" onchange="inputChanged();"/>
+      </xul:hbox>
     </content>
 
     <implementation>
       <method name="valueFromPreference">
         <body>
         <![CDATA[
           let val = Services.prefs.getIntPref(this.pref);
           this.value = val;
@@ -298,45 +292,41 @@
         </body>
       </method>
 
       <property name="type" readonly="true" onget="return this.getAttribute('type');"/>
       <property name="value" onget="return this.input.value;" onset="return this.input.value = val;"/>
     </implementation>
   </binding>
 
-  <binding id="setting-control" extends="chrome://browser/content/bindings/setting.xml#setting-base">
+  <binding id="setting-control" extends="chrome://mozapps/content/extensions/setting.xml#setting-base">
     <content>
-      <xul:box flex="1" class="prefbox setting-control">
-        <xul:vbox flex="1">
-          <xul:label class="preftitle" xbl:inherits="value=title" crop="end" flex="1"/>
-          <xul:label class="prefdesc" xbl:inherits="value=desc" crop="end" flex="1">
-            <children/>
-          </xul:label>
-        </xul:vbox>
-        <xul:hbox anonid="input-container" class="setting-input">
-          <children includes="button|menulist"/>
-        </xul:hbox>
-      </xul:box>
+      <xul:vbox class="setting-label">
+        <xul:label class="preferences-title" xbl:inherits="value=title" crop="end" flex="1"/>
+        <xul:label class="preferences-description" xbl:inherits="value=desc" crop="end" flex="1">
+          <children/>
+        </xul:label>
+      </xul:vbox>
+      <xul:hbox anonid="input-container" class="setting-input">
+        <children includes="button|menulist"/>
+      </xul:hbox>
     </content>
   </binding>
 
-  <binding id="setting-string" extends="chrome://browser/content/bindings/setting.xml#setting-base">
+  <binding id="setting-string" extends="chrome://mozapps/content/extensions/setting.xml#setting-base">
     <content>
-      <xul:box flex="1" class="prefbox setting-string">
-        <xul:vbox flex="1">
-          <xul:label class="preftitle" xbl:inherits="value=title" crop="end" flex="1"/>
-          <xul:label class="prefdesc" xbl:inherits="value=desc" crop="end" flex="1">
-            <children/>
-          </xul:label>
-        </xul:vbox>
-        <xul:hbox anonid="input-container" class="setting-input">
-          <xul:textbox xbl:inherits="disabled,emptytext,type=inputtype,min,max,increment,hidespinbuttons,decimalplaces,wraparound" anonid="input" oninput="inputChanged();"/>
-        </xul:hbox>
-      </xul:box>
+      <xul:vbox class="setting-label">
+        <xul:label class="preferences-title" xbl:inherits="value=title" crop="end" flex="1"/>
+        <xul:label class="preferences-description" xbl:inherits="value=desc" crop="end" flex="1">
+          <children/>
+        </xul:label>
+      </xul:vbox>
+      <xul:hbox anonid="input-container" class="setting-input">
+        <xul:textbox xbl:inherits="disabled,emptytext,type=inputtype,min,max,increment,hidespinbuttons,decimalplaces,wraparound" anonid="input" oninput="inputChanged();"/>
+      </xul:hbox>
     </content>
 
     <implementation>
       <method name="valueFromPreference">
         <body>
         <![CDATA[
           const nsISupportsString = Components.interfaces.nsISupportsString;
           this.value = Services.prefs.getComplexValue(this.pref, nsISupportsString).data;
--- a/toolkit/mozapps/extensions/jar.mn
+++ b/toolkit/mozapps/extensions/jar.mn
@@ -13,8 +13,9 @@ toolkit.jar:
 * content/mozapps/extensions/blocklist.xul                      (content/blocklist.xul)
 * content/mozapps/extensions/blocklist.js                       (content/blocklist.js)
 * content/mozapps/extensions/blocklist.css                      (content/blocklist.css)
 * content/mozapps/extensions/blocklist.xml                      (content/blocklist.xml)
 * content/mozapps/extensions/update.xul                         (content/update.xul)
 * content/mozapps/extensions/update.js                          (content/update.js)
 * content/mozapps/extensions/eula.xul                           (content/eula.xul)
 * content/mozapps/extensions/eula.js                            (content/eula.js)
+  content/mozapps/extensions/setting.xml                        (content/setting.xml)
--- a/toolkit/mozapps/extensions/test/browser/Makefile.in
+++ b/toolkit/mozapps/extensions/test/browser/Makefile.in
@@ -82,16 +82,17 @@ include $(DEPTH)/config/autoconf.mk
   browser_manualupdates.js \
   browser_globalwarnings.js \
   browser_globalinformations.js \
   browser_eula.js \
   browser_updateid.js \
   browser_purchase.js \
   browser_openDialog.js \
   browser_types.js \
+  browser_inlinesettings.js \
   $(NULL)
 
 _TEST_FILES = \
   head.js \
   browser_bug557956.js \
   browser_bug616841.js \
   browser_bug623950.js \
   browser_updatessl.js \
@@ -110,16 +111,17 @@ include $(DEPTH)/config/autoconf.mk
   browser_searching_empty.xml \
   browser_updatessl.rdf \
   browser_install.rdf \
   browser_install.xml \
   browser_install1_3.xpi \
   browser_eula.xml \
   browser_purchase.xml \
   discovery.html \
+  options.xul \
   redirect.sjs \
   releaseNotes.xhtml \
   $(NULL)
 
 include $(topsrcdir)/config/rules.mk
 
 libs:: $(_MAIN_TEST_FILES)
 	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir)
new file mode 100644
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/browser/addons/browser_inlinesettings1/bootstrap.js
@@ -0,0 +1,8 @@
+function install (params, aReason) {
+}
+function uninstall (params, aReason) {
+}
+function startup (params, aReason) {
+}
+function shutdown (params, aReason) {
+}
new file mode 100644
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/browser/addons/browser_inlinesettings1/install.rdf
@@ -0,0 +1,19 @@
+<?xml version="1.0" ?>
+<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:em="http://www.mozilla.org/2004/em-rdf#">
+
+  <Description about="urn:mozilla:install-manifest">
+    <em:id>inlinesettings1@tests.mozilla.org</em:id>
+    <em:name>Inline Settings (Bootstrap)</em:name>
+    <em:version>1</em:version>
+    <em:bootstrap>true</em:bootstrap>
+
+    <em:targetApplication>
+      <Description>
+        <em:id>toolkit@mozilla.org</em:id>
+        <em:minVersion>0</em:minVersion>
+        <em:maxVersion>*</em:maxVersion>
+      </Description>
+    </em:targetApplication>
+  </Description>
+
+</RDF>
new file mode 100644
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/browser/addons/browser_inlinesettings1/options.xul
@@ -0,0 +1,16 @@
+<?xml version="1.0" ?>
+<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+  <setting pref="extensions.inlinesettings1.bool" type="bool" title="Bool"/>
+  <setting pref="extensions.inlinesettings1.boolint" type="boolint" on="1" off="2" title="BoolInt"/>
+  <setting pref="extensions.inlinesettings1.integer" type="integer" title="Integer"/>
+  <setting pref="extensions.inlinesettings1.string" type="string" title="String"/>
+  <setting type="control" title="Menulist">
+    <menulist sizetopopup="always">
+      <menupopup>
+        <menuitem label="Test 1" value="1" />
+        <menuitem label="Test 2" value="2" />
+        <menuitem label="Test 3" value="3" />
+      </menupopup>
+    </menulist>
+  </setting>
+</vbox>
new file mode 100644
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/browser/browser_inlinesettings.js
@@ -0,0 +1,268 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Tests various aspects of the details view
+
+var gManagerWindow;
+var gCategoryUtilities;
+var gProvider;
+
+function installAddon(aCallback) {
+  AddonManager.getInstallForURL(TESTROOT + "addons/browser_inlinesettings1.xpi",
+                                function(aInstall) {
+    aInstall.addListener({
+      onInstallEnded: function() {
+        executeSoon(aCallback);
+      }
+    });
+    aInstall.install();
+  }, "application/x-xpinstall");
+}
+
+function test() {
+  waitForExplicitFinish();
+
+  gProvider = new MockProvider();
+
+  gProvider.createAddons([{
+    id: "inlinesettings2@tests.mozilla.org",
+    name: "Inline Settings (Regular)",
+    version: "1",
+    optionsURL: CHROMEROOT + "options.xul",
+    optionsType: AddonManager.OPTIONS_TYPE_INLINE
+  },{
+    id: "noninlinesettings@tests.mozilla.org",
+    name: "Non-Inline Settings",
+    version: "1",
+    optionsURL: CHROMEROOT + "addon_prefs.xul"
+  }]);
+
+  installAddon(function () {
+    open_manager("addons://list/extension", function(aWindow) {
+      gManagerWindow = aWindow;
+      gCategoryUtilities = new CategoryUtilities(gManagerWindow);
+
+      run_next_test();
+    });
+  });
+}
+
+function end_test() {
+  close_manager(gManagerWindow, function() {
+    AddonManager.getAddonByID("inlinesettings1@tests.mozilla.org", function(aAddon) {
+      aAddon.uninstall();
+      finish();
+    });
+  });
+}
+
+// Addon with options.xul
+add_test(function() {
+  var addon = get_addon_element(gManagerWindow, "inlinesettings1@tests.mozilla.org");
+  is(addon.mAddon.optionsType, AddonManager.OPTIONS_TYPE_INLINE, "Options should be inline type");
+  addon.parentNode.ensureElementIsVisible(addon);
+
+  var button = gManagerWindow.document.getAnonymousElementByAttribute(addon, "anonid", "preferences-btn");
+  is_element_visible(button, "Preferences button should be visible");
+
+  run_next_test();
+});
+
+// Addon with inline preferences as optionsURL
+add_test(function() {
+  var addon = get_addon_element(gManagerWindow, "inlinesettings2@tests.mozilla.org");
+  is(addon.mAddon.optionsType, AddonManager.OPTIONS_TYPE_INLINE, "Options should be inline type");
+  addon.parentNode.ensureElementIsVisible(addon);
+
+  var button = gManagerWindow.document.getAnonymousElementByAttribute(addon, "anonid", "preferences-btn");
+  is_element_visible(button, "Preferences button should be visible");
+
+  run_next_test();
+});
+
+// Addon with non-inline preferences as optionsURL
+add_test(function() {
+  var addon = get_addon_element(gManagerWindow, "noninlinesettings@tests.mozilla.org");
+  is(addon.mAddon.optionsType, AddonManager.OPTIONS_TYPE_DIALOG, "Options should be dialog type");
+  addon.parentNode.ensureElementIsVisible(addon);
+
+  var button = gManagerWindow.document.getAnonymousElementByAttribute(addon, "anonid", "preferences-btn");
+  is_element_visible(button, "Preferences button should be visible");
+
+  run_next_test();
+});
+
+// Addon with options.xul, also a test for the setting.xml bindings
+add_test(function() {
+  var addon = get_addon_element(gManagerWindow, "inlinesettings1@tests.mozilla.org");
+  addon.parentNode.ensureElementIsVisible(addon);
+
+  var button = gManagerWindow.document.getAnonymousElementByAttribute(addon, "anonid", "preferences-btn");
+  EventUtils.synthesizeMouseAtCenter(button, { clickCount: 1 }, gManagerWindow);
+
+  wait_for_view_load(gManagerWindow, function() {
+    var grid = gManagerWindow.document.getElementById("detail-grid");
+    var settings = grid.querySelectorAll("rows > setting");
+    is(settings.length, 4, "Grid should have settings children");
+
+    // Force bindings to apply
+    settings[0].clientTop;
+
+    Services.prefs.setBoolPref("extensions.inlinesettings1.bool", false);
+    var input = gManagerWindow.document.getAnonymousElementByAttribute(settings[0], "anonid", "input");
+    isnot(input.checked, true, "Checkbox should have initial value");
+    EventUtils.synthesizeMouseAtCenter(input, { clickCount: 1 }, gManagerWindow);
+    is(input.checked, true, "Checkbox should have updated value");
+    is(Services.prefs.getBoolPref("extensions.inlinesettings1.bool"), true, "Bool pref should have been updated");
+    EventUtils.synthesizeMouseAtCenter(input, { clickCount: 1 }, gManagerWindow);
+    isnot(input.checked, true, "Checkbox should have updated value");
+    is(Services.prefs.getBoolPref("extensions.inlinesettings1.bool"), false, "Bool pref should have been updated");
+
+    Services.prefs.setIntPref("extensions.inlinesettings1.boolint", 0);
+    var input = gManagerWindow.document.getAnonymousElementByAttribute(settings[1], "anonid", "input");
+    isnot(input.checked, true, "Checkbox should have initial value");
+    EventUtils.synthesizeMouseAtCenter(input, { clickCount: 1 }, gManagerWindow);
+    is(input.checked, true, "Checkbox should have updated value");
+    is(Services.prefs.getIntPref("extensions.inlinesettings1.boolint"), 1, "BoolInt pref should have been updated");
+    EventUtils.synthesizeMouseAtCenter(input, { clickCount: 1 }, gManagerWindow);
+    isnot(input.checked, true, "Checkbox should have updated value");
+    is(Services.prefs.getIntPref("extensions.inlinesettings1.boolint"), 2, "BoolInt pref should have been updated");
+
+    Services.prefs.setIntPref("extensions.inlinesettings1.integer", 0);
+    var input = gManagerWindow.document.getAnonymousElementByAttribute(settings[2], "anonid", "input");
+    is(input.value, "0", "Number box should have initial value");
+    input.select();
+    EventUtils.synthesizeKey("1", {}, gManagerWindow);
+    EventUtils.synthesizeKey("3", {}, gManagerWindow);
+    is(input.value, "13", "Number box should have updated value");
+    is(Services.prefs.getIntPref("extensions.inlinesettings1.integer"), 13, "Integer pref should have been updated");
+    EventUtils.synthesizeKey("VK_DOWN", {}, gManagerWindow);
+    is(input.value, "12", "Number box should have updated value");
+    is(Services.prefs.getIntPref("extensions.inlinesettings1.integer"), 12, "Integer pref should have been updated");
+
+    Services.prefs.setCharPref("extensions.inlinesettings1.string", "foo");
+    var input = gManagerWindow.document.getAnonymousElementByAttribute(settings[3], "anonid", "input");
+    is(input.value, "foo", "Text box should have initial value");
+    input.select();
+    EventUtils.synthesizeKey("b", {}, gManagerWindow);
+    EventUtils.synthesizeKey("a", {}, gManagerWindow);
+    EventUtils.synthesizeKey("r", {}, gManagerWindow);
+    is(input.value, "bar", "Text box should have updated value");
+    is(Services.prefs.getCharPref("extensions.inlinesettings1.string"), "bar", "String pref should have been updated");
+
+    var button = gManagerWindow.document.getElementById("detail-prefs-btn");
+    is_element_hidden(button, "Preferences button should not be visible");
+
+    gCategoryUtilities.openType("extension", run_next_test);
+  });
+});
+
+// Addon with inline preferences as optionsURL
+add_test(function() {
+  var addon = get_addon_element(gManagerWindow, "inlinesettings2@tests.mozilla.org");
+  addon.parentNode.ensureElementIsVisible(addon);
+
+  var button = gManagerWindow.document.getAnonymousElementByAttribute(addon, "anonid", "preferences-btn");
+  EventUtils.synthesizeMouseAtCenter(button, { clickCount: 1 }, gManagerWindow);
+
+  wait_for_view_load(gManagerWindow, function() {
+    var grid = gManagerWindow.document.getElementById("detail-grid");
+    var settings = grid.querySelectorAll("rows > setting");
+    is(settings.length, 2, "Grid should have settings children");
+
+    // Force bindings to apply
+    settings[0].clientTop;
+
+    var node = settings[0];
+    is(node.nodeName, "setting", "Should be a setting node");
+    var description = gManagerWindow.document.getAnonymousElementByAttribute(node, "class", "preferences-description");
+    is(description.textContent.trim(), "", "Description node should be empty");
+
+    node = node.nextSibling;
+    is(node.nodeName, "row", "Setting should be followed by a row node");
+    is(node.textContent, "Description Attribute", "Description should be in this row");
+
+    node = settings[1];
+    is(node.nodeName, "setting", "Should be a setting node");
+    description = gManagerWindow.document.getAnonymousElementByAttribute(node, "class", "preferences-description");
+    is(description.textContent.trim(), "", "Description node should be empty");
+
+    node = node.nextSibling;
+    is(node.nodeName, "row", "Setting should be followed by a row node");
+    is(node.textContent, "Description Text Node", "Description should be in this row");
+
+    var button = gManagerWindow.document.getElementById("detail-prefs-btn");
+    is_element_hidden(button, "Preferences button should not be visible");
+
+    gCategoryUtilities.openType("extension", run_next_test);
+  });
+});
+
+// Addon with non-inline preferences as optionsURL
+add_test(function() {
+  var addon = get_addon_element(gManagerWindow, "noninlinesettings@tests.mozilla.org");
+  addon.parentNode.ensureElementIsVisible(addon);
+
+  var button = gManagerWindow.document.getAnonymousElementByAttribute(addon, "anonid", "details-btn");
+  EventUtils.synthesizeMouseAtCenter(button, { clickCount: 1 }, gManagerWindow);
+
+  wait_for_view_load(gManagerWindow, function() {
+    var grid = gManagerWindow.document.getElementById("detail-grid");
+    var settings = grid.querySelectorAll("rows > setting");
+    is(settings.length, 0, "Grid should not have settings children");
+
+    var button = gManagerWindow.document.getElementById("detail-prefs-btn");
+    is_element_visible(button, "Preferences button should be visible");
+
+    gCategoryUtilities.openType("extension", run_next_test);
+  });
+});
+
+// Addon with options.xul, disabling and enabling should hide and show settings UI
+add_test(function() {
+  var addon = get_addon_element(gManagerWindow, "inlinesettings1@tests.mozilla.org");
+  addon.parentNode.ensureElementIsVisible(addon);
+
+  var button = gManagerWindow.document.getAnonymousElementByAttribute(addon, "anonid", "preferences-btn");
+  EventUtils.synthesizeMouseAtCenter(button, { clickCount: 1 }, gManagerWindow);
+
+  wait_for_view_load(gManagerWindow, function() {
+    var grid = gManagerWindow.document.getElementById("detail-grid");
+    var settings = grid.querySelectorAll("rows > setting");
+    is(settings.length, 4, "Grid should have settings children");
+
+    // disable
+    var button = gManagerWindow.document.getElementById("detail-disable-btn");
+    EventUtils.synthesizeMouseAtCenter(button, { clickCount: 1 }, gManagerWindow);
+
+    settings = grid.querySelectorAll("rows > setting");
+    is(settings.length, 0, "Grid should not have settings children");
+
+    gCategoryUtilities.openType("extension", function() {
+      var addon = get_addon_element(gManagerWindow, "inlinesettings1@tests.mozilla.org");
+      addon.parentNode.ensureElementIsVisible(addon);
+
+      var button = gManagerWindow.document.getAnonymousElementByAttribute(addon, "anonid", "preferences-btn");
+      is_element_hidden(button, "Preferences button should not be visible");
+
+      button = gManagerWindow.document.getAnonymousElementByAttribute(addon, "anonid", "details-btn");
+      EventUtils.synthesizeMouseAtCenter(button, { clickCount: 1 }, gManagerWindow);
+
+      wait_for_view_load(gManagerWindow, function() {
+        var grid = gManagerWindow.document.getElementById("detail-grid");
+        var settings = grid.querySelectorAll("rows > setting");
+        is(settings.length, 0, "Grid should not have settings children");
+
+        // enable
+        var button = gManagerWindow.document.getElementById("detail-enable-btn");
+        EventUtils.synthesizeMouseAtCenter(button, { clickCount: 1 }, gManagerWindow);
+
+        settings = grid.querySelectorAll("rows > setting");
+        is(settings.length, 4, "Grid should have settings children");
+
+        gCategoryUtilities.openType("extension", run_next_test);
+      });
+    });
+  });
+});
--- a/toolkit/mozapps/extensions/test/browser/head.js
+++ b/toolkit/mozapps/extensions/test/browser/head.js
@@ -617,16 +617,18 @@ MockProvider.prototype = {
         if (prop == "id")
           continue;
         if (prop == "applyBackgroundUpdates") {
           addon._applyBackgroundUpdates = aAddonProp[prop];
           continue;
         }
         addon[prop] = aAddonProp[prop];
       }
+      if (!addon.optionsType && !!addon.optionsURL)
+        addon.optionsType = AddonManager.OPTIONS_TYPE_DIALOG;
       this.addAddon(addon);
       newAddons.push(addon);
     }, this);
 
     return newAddons;
   },
 
   /**
new file mode 100644
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/browser/options.xul
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?>
+<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+  <setting pref="extensions.inlinesettings2.bool1" type="bool" title="Bool 1" desc="Description Attribute"/>
+  <setting pref="extensions.inlinesettings2.bool2" type="bool" title="Bool 2">Description Text Node</setting>
+</vbox>
--- a/toolkit/mozapps/extensions/test/xpcshell/head_addons.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/head_addons.js
@@ -462,17 +462,17 @@ function writeLocaleStrings(aData) {
 
 function createInstallRDF(aData) {
   var rdf = '<?xml version="1.0"?>\n';
   rdf += '<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"\n' +
          '     xmlns:em="http://www.mozilla.org/2004/em-rdf#">\n';
   rdf += '<Description about="urn:mozilla:install-manifest">\n';
 
   ["id", "version", "type", "internalName", "updateURL", "updateKey",
-   "optionsURL", "aboutURL", "iconURL", "icon64URL",
+   "optionsURL", "optionsType", "aboutURL", "iconURL", "icon64URL",
    "skinnable", "bootstrap"].forEach(function(aProp) {
     if (aProp in aData)
       rdf += "<em:" + aProp + ">" + escapeXML(aData[aProp]) + "</em:" + aProp + ">\n";
   });
 
   rdf += writeLocaleStrings(aData);
 
   if ("targetPlatforms" in aData) {
--- a/toolkit/mozapps/extensions/test/xpcshell/test_manifest.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_manifest.js
@@ -216,16 +216,65 @@ function run_test() {
     targetApplications: [{
       id: "xpcshell@tests.mozilla.org",
       minVersion: "1",
       maxVersion: "1"
     }],
     name: "Test Addon 16"
   }, profileDir);
 
+  writeInstallRDFForExtension({
+    id: "addon17@tests.mozilla.org",
+    version: "1.0",
+    optionsURL: "chrome://test/content/options.xul",
+    optionsType: "2",
+    targetApplications: [{
+      id: "xpcshell@tests.mozilla.org",
+      minVersion: "1",
+      maxVersion: "1"
+    }],
+    name: "Test Addon 17"
+  }, profileDir);
+
+  writeInstallRDFForExtension({
+    id: "addon18@tests.mozilla.org",
+    version: "1.0",
+    targetApplications: [{
+      id: "xpcshell@tests.mozilla.org",
+      minVersion: "1",
+      maxVersion: "1"
+    }],
+    name: "Test Addon 18"
+  }, profileDir, null, "options.xul");
+
+  writeInstallRDFForExtension({
+    id: "addon19@tests.mozilla.org",
+    version: "1.0",
+    optionsType: "99",
+    targetApplications: [{
+      id: "xpcshell@tests.mozilla.org",
+      minVersion: "1",
+      maxVersion: "1"
+    }],
+    name: "Test Addon 19"
+  }, profileDir);
+
+  writeInstallRDFForExtension({
+    id: "addon20@tests.mozilla.org",
+    version: "1.0",
+    optionsType: "1",
+    optionsURL: "chrome://test/content/options.xul",
+    targetApplications: [{
+      id: "xpcshell@tests.mozilla.org",
+      minVersion: "1",
+      maxVersion: "1"
+    }],
+    name: "Test Addon 20"
+  }, profileDir);
+
   do_test_pending();
   startupManager();
   AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
                                "addon2@tests.mozilla.org",
                                "addon3@tests.mozilla.org",
                                "addon4@tests.mozilla.org",
                                "addon5@tests.mozilla.org",
                                "addon6@tests.mozilla.org",
@@ -233,25 +282,30 @@ function run_test() {
                                "addon8@tests.mozilla.org",
                                "addon9@tests.mozilla.org",
                                "addon10@tests.mozilla.org",
                                "addon11@tests.mozilla.org",
                                "addon12@tests.mozilla.org",
                                "addon13@tests.mozilla.org",
                                "addon14@tests.mozilla.org",
                                "addon15@tests.mozilla.org",
-                               "addon16@tests.mozilla.org"],
+                               "addon16@tests.mozilla.org",
+                               "addon17@tests.mozilla.org",
+                               "addon18@tests.mozilla.org",
+                               "addon19@tests.mozilla.org",
+                               "addon20@tests.mozilla.org"],
                                function([a1, a2, a3, a4, a5, a6, a7, a8, a9, a10,
-                                         a11, a12, a13, a14, a15, a16]) {
+                                         a11, a12, a13, a14, a15, a16, a17, a18, a19, a20]) {
 
     do_check_neq(a1, null);
     do_check_eq(a1.id, "addon1@tests.mozilla.org");
     do_check_eq(a1.type, "extension");
     do_check_eq(a1.version, "1.0");
     do_check_eq(a1.optionsURL, "chrome://test/content/options.xul");
+    do_check_eq(a1.optionsType, AddonManager.OPTIONS_TYPE_DIALOG);
     do_check_eq(a1.aboutURL, "chrome://test/content/about.xul");
     do_check_eq(a1.iconURL, "chrome://test/skin/icon.png");
     do_check_eq(a1.icon64URL, "chrome://test/skin/icon64.png");
     do_check_eq(a1.name, "Test Addon 1");
     do_check_eq(a1.description, "Test Description");
     do_check_eq(a1.creator, "Test Creator");
     do_check_eq(a1.homepageURL, "http://www.example.com");
     do_check_eq(a1.developers[0], "Test Developer 1");
@@ -357,11 +411,43 @@ function run_test() {
 
     do_check_neq(a16, null);
     do_check_true(a16.isActive);
     do_check_false(a16.userDisabled);
     do_check_false(a16.appDisabled);
     do_check_true(a16.isCompatible);
     do_check_true(a16.providesUpdatesSecurely);
 
+    do_check_neq(a17, null);
+    do_check_true(a17.isActive);
+    do_check_false(a17.userDisabled);
+    do_check_false(a17.appDisabled);
+    do_check_true(a17.isCompatible);
+    do_check_eq(a17.optionsURL, "chrome://test/content/options.xul");
+    do_check_eq(a17.optionsType, AddonManager.OPTIONS_TYPE_INLINE);
+
+    do_check_neq(a18, null);
+    do_check_true(a18.isActive);
+    do_check_false(a18.userDisabled);
+    do_check_false(a18.appDisabled);
+    do_check_true(a18.isCompatible);
+    if (Services.prefs.getBoolPref("extensions.alwaysUnpack")) {
+      do_check_eq(a18.optionsURL, Services.io.newFileURI(profileDir).spec +
+                                  "addon18@tests.mozilla.org/options.xul");
+    } else {
+      do_check_eq(a18.optionsURL, "jar:" + Services.io.newFileURI(profileDir).spec +
+                                  "addon18@tests.mozilla.org.xpi!/options.xul");
+    }
+    do_check_eq(a18.optionsType, AddonManager.OPTIONS_TYPE_INLINE);
+
+    do_check_eq(a19, null);
+
+    do_check_neq(a20, null);
+    do_check_true(a20.isActive);
+    do_check_false(a20.userDisabled);
+    do_check_false(a20.appDisabled);
+    do_check_true(a20.isCompatible);
+    do_check_eq(a20.optionsURL, "chrome://test/content/options.xul");
+    do_check_eq(a20.optionsType, AddonManager.OPTIONS_TYPE_DIALOG);
+
     do_test_finished();
   });
 }
--- a/toolkit/mozapps/extensions/test/xpcshell/test_startup.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_startup.js
@@ -67,16 +67,42 @@ var addon5 = {
   name: "Test 5",
   targetApplications: [{
     id: "xpcshell@tests.mozilla.org",
     minVersion: "1",
     maxVersion: "1"
   }]
 };
 
+// Should be ignored because it has an invalid type
+var addon6 = {
+  id: "addon6@tests.mozilla.org",
+  version: "3.0",
+  name: "Test 6",
+  type: 5,
+  targetApplications: [{
+    id: "toolkit@mozilla.org",
+    minVersion: "1.9.2",
+    maxVersion: "1.9.2.*"
+  }]
+};
+
+// Should be ignored because it has an invalid type
+var addon7 = {
+  id: "addon7@tests.mozilla.org",
+  version: "3.0",
+  name: "Test 3",
+  type: "extension",
+  targetApplications: [{
+    id: "toolkit@mozilla.org",
+    minVersion: "1.9.2",
+    maxVersion: "1.9.2.*"
+  }]
+};
+
 createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
 
 const globalDir = gProfD.clone();
 globalDir.append("extensions2");
 globalDir.append(gAppInfo.ID);
 registerDirectory("XRESysSExtPD", globalDir.parent);
 const userDir = gProfD.clone();
 userDir.append("extensions3");
@@ -100,18 +126,20 @@ function run_test() {
   }, "startupcache-invalidate", false);
 
   startupManager();
 
   AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
                                "addon2@tests.mozilla.org",
                                "addon3@tests.mozilla.org",
                                "addon4@tests.mozilla.org",
-                               "addon5@tests.mozilla.org"],
-                               function([a1, a2, a3, a4, a5]) {
+                               "addon5@tests.mozilla.org",
+                               "addon6@tests.mozilla.org",
+                               "addon7@tests.mozilla.org"],
+                               function([a1, a2, a3, a4, a5, a6, a7]) {
 
     do_check_eq(a1, null);
     do_check_not_in_crash_annotation(addon1.id, addon1.version);
     do_check_eq(a2, null);
     do_check_not_in_crash_annotation(addon2.id, addon2.version);
     do_check_eq(a3, null);
     do_check_not_in_crash_annotation(addon3.id, addon3.version);
     do_check_eq(a4, null);
@@ -131,27 +159,31 @@ function run_test_1() {
   var dest = writeInstallRDFForExtension(addon2, profileDir);
   // Attempt to make this look like it was added some time in the past so
   // the change in run_test_2 makes the last modified time change.
   setExtensionModifiedTime(dest, dest.lastModifiedTime - 5000);
 
   writeInstallRDFForExtension(addon3, profileDir);
   writeInstallRDFForExtension(addon4, profileDir);
   writeInstallRDFForExtension(addon5, profileDir);
+  writeInstallRDFForExtension(addon6, profileDir);
+  writeInstallRDFForExtension(addon7, profileDir);
 
   gCachePurged = false;
   restartManager();
   do_check_true(gCachePurged);
 
   AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
                                "addon2@tests.mozilla.org",
                                "addon3@tests.mozilla.org",
                                "addon4@tests.mozilla.org",
-                               "addon5@tests.mozilla.org"],
-                               function([a1, a2, a3, a4, a5]) {
+                               "addon5@tests.mozilla.org",
+                               "addon6@tests.mozilla.org",
+                               "addon7@tests.mozilla.org"],
+                               function([a1, a2, a3, a4, a5, a6, a7]) {
 
     do_check_neq(a1, null);
     do_check_eq(a1.id, "addon1@tests.mozilla.org");
     do_check_eq(a1.version, "1.0");
     do_check_eq(a1.name, "Test 1");
     do_check_true(isExtensionInAddonsList(profileDir, a1.id));
     do_check_true(hasFlag(a1.permissions, AddonManager.PERM_CAN_UNINSTALL));
     do_check_true(hasFlag(a1.permissions, AddonManager.PERM_CAN_UPGRADE));
@@ -188,16 +220,28 @@ function run_test_1() {
     do_check_false(dest.exists());
 
     do_check_eq(a5, null);
     do_check_false(isExtensionInAddonsList(profileDir, "addon5@tests.mozilla.org"));
     dest = profileDir.clone();
     dest.append(do_get_expected_addon_name("addon5@tests.mozilla.org"));
     do_check_false(dest.exists());
 
+    do_check_eq(a6, null);
+    do_check_false(isExtensionInAddonsList(profileDir, "addon6@tests.mozilla.org"));
+    dest = profileDir.clone();
+    dest.append(do_get_expected_addon_name("addon6@tests.mozilla.org"));
+    do_check_false(dest.exists());
+
+    do_check_eq(a7, null);
+    do_check_false(isExtensionInAddonsList(profileDir, "addon7@tests.mozilla.org"));
+    dest = profileDir.clone();
+    dest.append(do_get_expected_addon_name("addon7@tests.mozilla.org"));
+    do_check_false(dest.exists());
+
     AddonManager.getAddonsByTypes(["extension"], function(extensionAddons) {
       do_check_eq(extensionAddons.length, 3);
 
       run_test_2();
     });
   });
 }
 
--- a/toolkit/themes/gnomestripe/mozapps/extensions/extensions.css
+++ b/toolkit/themes/gnomestripe/mozapps/extensions/extensions.css
@@ -712,40 +712,71 @@
 #detail-contrib-suggested {
   color: GrayText;
 }
 
 #detail-grid {
   margin-bottom: 2em;
 }
 
+#detail-grid > columns > column:first-child {
+  max-width: 25em;
+}
+
 .detail-row[first-row="true"],
-.detail-row-complex[first-row="true"] {
+.detail-row-complex[first-row="true"],
+setting[first-row="true"] {
   border-top: none;
 }
 
 .detail-row,
-.detail-row-complex {
+.detail-row-complex,
+setting {
   border-top: 1px solid ThreeDShadow;
   -moz-box-align: center;
+  min-height: 33px;
 }
 
 .detail-row-value {
   -moz-margin-start: 0;
 }
 
 #detail-controls {
   margin-bottom: 1em;
 }
 
 #detail-view[active="false"]:not([pending]):not([notification]) {
   background-image: -moz-linear-gradient(rgba(135, 135, 135, 0.1),
                                          rgba(135, 135, 135, 0));
 }
 
+setting[first-row="true"] {
+  margin-top: 2em;
+}
+
+setting {
+  display: -moz-grid-line;
+}
+
+.preferences-description {
+  font-size: 90.9%;
+  color: graytext;
+  margin-top: -2px;
+  -moz-margin-start: 2em;
+}
+
+setting[type="string"] > .setting-input > textbox {
+  -moz-box-flex: 1;
+}
+
+menulist { /* Fixes some styling inconsistencies */
+  font-size: 100%;
+  margin: 1px 5px 2px 5px;
+}
+
 
 /*** creator ***/
 
 .creator > label {
   -moz-margin-start: 0;
   -moz-margin-end: 0;
 }
 
--- a/toolkit/themes/pinstripe/mozapps/extensions/extensions.css
+++ b/toolkit/themes/pinstripe/mozapps/extensions/extensions.css
@@ -889,41 +889,67 @@
   box-shadow: 0 0 6.5px rgba(0, 0, 0, 0.4) inset,
               0 0 2px rgba(0, 0, 0, 0.4) inset;
 }
 
 #detail-grid {
   margin-bottom: 2em;
 }
 
+#detail-grid > columns > column:first-child {
+  max-width: 25em;
+}
+
 .detail-row[first-row="true"],
-.detail-row-complex[first-row="true"] {
+.detail-row-complex[first-row="true"],
+setting[first-row="true"] {
   border-top: none;
 }
 
 .detail-row,
-.detail-row-complex {
+.detail-row-complex,
+setting {
   border-top: 2px solid;
   -moz-border-top-colors: rgba(28, 31, 37, 0.2) rgba(255, 255, 255, 0.2);
   -moz-box-align: center;
+  min-height: 30px;
 }
 
 .detail-row-value {
   -moz-margin-start: 0;
 }
 
 #detail-controls {
   margin-bottom: 1em;
 }
 
 #detail-view[active="false"]:not([pending]):not([notification]) {
   background-image: -moz-linear-gradient(rgba(135, 135, 135, 0.1),
                                          rgba(135, 135, 135, 0));
 }
 
+setting[first-row="true"] {
+  margin-top: 2em;
+}
+
+setting {
+  display: -moz-grid-line;
+}
+
+.preferences-description {
+  font-size: 90.9%;
+  color: graytext;
+  margin-top: -2px;
+  -moz-margin-start: 2em;
+}
+
+setting[type="string"] > .setting-input > textbox {
+  -moz-box-flex: 1;
+}
+
 
 /*** creator ***/
 
 .creator > label {
   -moz-margin-start: 0;
   -moz-margin-end: 0;
 }
 
@@ -1077,34 +1103,38 @@
 
 #update-selected {
   margin: 12px;
 }
 
 
 /*** buttons ***/
 
-.addon-control {
+.addon-control,
+setting[type="control"] button,
+setting[type="control"] menulist {
   -moz-appearance: none;
   padding: 1px 4px;
   min-width: 60px;
   border-radius: 3px;
   border: 1px solid rgba(60,73,97,0.5);
   box-shadow: inset 0 1px rgba(255,255,255,0.25), 0 1px rgba(255,255,255,0.25);
   background-image: -moz-linear-gradient(rgba(255,255,255,0.45), rgba(255,255,255,0.2));
   background-clip: padding-box;
   color: #252F3B;
   text-shadow: @loweredShadow@;
 }
 
 .addon-control[disabled="true"] {
   display: none;
 }
 
-.addon-control:active:hover {
+.addon-control:active:hover,
+setting[type="control"] button:active:hover,
+setting[type="control"] menulist:hover {
   box-shadow: inset 0 1px 3px rgba(0,0,0,.2), 0 1px rgba(255,255,255,0.25);
   background-image: -moz-linear-gradient(rgba(45,54,71,0.3), rgba(45,54,71,0.1));
   border-color: rgba(60,73,97,0.7);
 }
 
 .button-link {
   -moz-appearance: none;
   background: transparent;
--- a/toolkit/themes/winstripe/mozapps/extensions/extensions.css
+++ b/toolkit/themes/winstripe/mozapps/extensions/extensions.css
@@ -914,41 +914,71 @@
               0 0 2px rgba(0, 0, 0, 0.4) inset,
               0 1px 0 rgba(255, 255, 255, 0.4);
 }
 
 #detail-grid {
   margin-bottom: 2em;
 }
 
+#detail-grid > columns > column:first-child {
+  max-width: 25em;
+}
+
 .detail-row[first-row="true"],
-.detail-row-complex[first-row="true"] {
+.detail-row-complex[first-row="true"],
+setting[first-row="true"] {
   border-top: none;
 }
 
 .detail-row,
-.detail-row-complex {
+.detail-row-complex,
+setting {
   border-top: 2px solid;
   -moz-border-top-colors: rgba(28, 31, 37, 0.1) rgba(255, 255, 255, 0.1);
   -moz-box-align: center;
+  min-height: 30px;
 }
 
 .detail-row-value {
   -moz-margin-start: 0;
 }
 
 #detail-controls {
   margin-bottom: 1em;
 }
 
 #detail-view[active="false"]:not([pending]):not([notification]) {
   background-image: -moz-linear-gradient(rgba(135, 135, 135, 0.1),
                                          rgba(135, 135, 135, 0));
 }
 
+setting[first-row="true"] {
+  margin-top: 2em;
+}
+
+setting {
+  display: -moz-grid-line;
+}
+
+.preferences-description {
+  font-size: 90.9%;
+  color: graytext;
+  margin-top: -2px;
+  -moz-margin-start: 2em;
+}
+
+setting[type="string"] > .setting-input > textbox {
+  -moz-box-flex: 1;
+}
+
+menulist { /* Fixes some styling inconsistencies */
+  margin: 1px 5px 2px 5px;
+}
+
 /*** creator ***/
 
 .creator > label {
   -moz-margin-start: 0;
   -moz-margin-end: 0;
 }
 
 .creator > .text-link {
@@ -1120,37 +1150,42 @@
 
 #update-selected {
   margin: 12px;
 }
 
 
 /*** buttons ***/
 
-.addon-control {
+.addon-control,
+setting[type="control"] button,
+setting[type="control"] menulist {
   -moz-appearance: none;
   color: black;
   padding: 0 5px;
   background: -moz-linear-gradient(rgba(251, 252, 253, 0.95), rgba(246, 247, 248, 0) 49%, 
                                    rgba(211, 212, 213, 0.45) 51%, rgba(225, 226, 229, 0.3));
   background-clip: padding-box;
   border-radius: 3px;
   border: 1px solid rgba(31, 64, 100, 0.4);
   border-top-color: rgba(31, 64, 100, 0.3);
   box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.25) inset,
               0 0 2px 1px rgba(255, 255, 255, 0.25) inset;
 }
 
-.addon-control:active:hover {
+.addon-control:active:hover,
+setting[type="control"] button:active:hover,
+setting[type="control"] menulist:hover {
   background-color: rgba(61, 76, 92, 0.2);
   border-color: rgba(39, 53, 68, 0.5);
   box-shadow: 0 0 3px 1px rgba(39, 53, 68, 0.2) inset;
 }
 
-.addon-control > .button-box {
+.addon-control > .button-box,
+setting[type="control"] button > .button-box {
   padding: 1px;
 }
 
 .addon-control[disabled="true"] {
   display: none;
 }
 
 .button-link {
--- a/toolkit/xre/nsEmbedFunctions.cpp
+++ b/toolkit/xre/nsEmbedFunctions.cpp
@@ -563,24 +563,24 @@ XRE_InitParentProcess(int aArgc,
                       char* aArgv[],
                       MainFunction aMainFunction,
                       void* aMainFunctionData)
 {
   NS_ENSURE_ARG_MIN(aArgc, 1);
   NS_ENSURE_ARG_POINTER(aArgv);
   NS_ENSURE_ARG_POINTER(aArgv[0]);
 
+  ScopedXREEmbed embed;
+
   gArgc = aArgc;
   gArgv = aArgv;
   int rv = XRE_InitCommandLine(gArgc, gArgv);
   if (NS_FAILED(rv))
       return NS_ERROR_FAILURE;
 
-  ScopedXREEmbed embed;
-
   {
     embed.Start();
 
     nsCOMPtr<nsIAppShell> appShell(do_GetService(kAppShellCID));
     NS_ENSURE_TRUE(appShell, NS_ERROR_FAILURE);
 
     if (aMainFunction) {
       nsCOMPtr<nsIRunnable> runnable =
--- a/xulrunner/installer/Makefile.in
+++ b/xulrunner/installer/Makefile.in
@@ -51,16 +51,17 @@ NO_PKG_FILES = \
 	$(NULL)
 
 # If we're on mac, we want to make the .pkg first, in the mac/
 # directory. Then packager.mk can put it into a DMG
 
 ifeq (cocoa,$(MOZ_WIDGET_TOOLKIT))
 DIRS += mac
 _APPNAME = $(PKG_BASENAME).pkg
+_BINPATH = /XUL.framework/Versions/Current
 PKG_SKIP_STRIP = 1
 MOZ_PKG_SPECIAL = pkg
 PKG_DMG_SOURCE = $(STAGEPATH)xulrunner-pkg
 endif
 
 include $(topsrcdir)/config/rules.mk
 
 INSTALL_SDK = 1