Merge m-c to b2g-inbound a=merge
authorWes Kocher <wkocher@mozilla.com>
Thu, 09 Oct 2014 21:41:54 -0700
changeset 232935 6d25927a7532e0f830183ce1ba13234822e300e2
parent 232934 b2aa39ce5d2ad2ad77b800a29e8f652423ea6edc (current diff)
parent 232932 50b689feab5f519b102141d629137cfd40bcd60b (diff)
child 232936 afe911752efe70a11fac7d1f0a2ac22b57d01891
push id4187
push userbhearsum@mozilla.com
push dateFri, 28 Nov 2014 15:29:12 +0000
treeherdermozilla-beta@f23cc6a30c11 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone35.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge m-c to b2g-inbound a=merge
browser/themes/linux/aboutSocialError.css
browser/themes/osx/aboutSocialError.css
browser/themes/windows/aboutSocialError.css
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -1287,16 +1287,19 @@ pref("services.sync.prefs.sync.security.
 pref("services.sync.prefs.sync.security.default_personal_cert", true);
 pref("services.sync.prefs.sync.security.tls.version.min", true);
 pref("services.sync.prefs.sync.security.tls.version.max", true);
 pref("services.sync.prefs.sync.signon.rememberSignons", true);
 pref("services.sync.prefs.sync.spellchecker.dictionary", true);
 pref("services.sync.prefs.sync.xpinstall.whitelist.required", true);
 #endif
 
+// Developer edition preferences
+pref("browser.devedition.theme.enabled", false);
+
 // Disable the error console
 pref("devtools.errorconsole.enabled", false);
 
 // Developer toolbar and GCLI preferences
 pref("devtools.toolbar.enabled", true);
 pref("devtools.toolbar.visible", false);
 pref("devtools.commands.dir", "");
 
@@ -1599,16 +1602,17 @@ pref("loop.throttled", false);
 pref("loop.enabled", true);
 pref("loop.throttled", true);
 pref("loop.soft_start_ticket_number", -1);
 pref("loop.soft_start_hostname", "soft-start.loop.services.mozilla.com");
 #endif
 
 pref("loop.server", "https://loop.services.mozilla.com");
 pref("loop.seenToS", "unseen");
+pref("loop.learnMoreUrl", "https://www.firefox.com/hello/");
 pref("loop.legal.ToS_url", "https://call.mozilla.com/legal/terms/");
 pref("loop.legal.privacy_url", "https://www.mozilla.org/privacy/");
 pref("loop.do_not_disturb", false);
 pref("loop.ringtone", "chrome://browser/content/loop/shared/sounds/Firefox-Long.ogg");
 pref("loop.retry_delay.start", 60000);
 pref("loop.retry_delay.limit", 300000);
 pref("loop.feedback.baseUrl", "https://input.mozilla.org/api/v1/feedback");
 pref("loop.feedback.product", "Loop");
@@ -1627,16 +1631,25 @@ pref("loop.rooms.enabled", false);
 pref("loop.fxa_oauth.tokendata", "");
 pref("loop.fxa_oauth.profile", "");
 
 // serverURL to be assigned by services team
 pref("services.push.serverURL", "wss://push.services.mozilla.com/");
 
 pref("social.sidebar.unload_timeout_ms", 10000);
 
+// activation from inside of share panel is possible if activationPanelEnabled
+// is true. Pref'd off for release while usage testing is done through beta.
+#ifdef EARLY_BETA_OR_EARLIER
+pref("social.share.activationPanelEnabled", true);
+#else
+pref("social.share.activationPanelEnabled", false);
+#endif
+pref("social.shareDirectory", "https://activations.cdn.mozilla.net/sharePanel.html");
+
 pref("dom.identity.enabled", false);
 
 // Block insecure active content on https pages
 pref("security.mixed_content.block_active_content", true);
 
 // 1 = allow MITM for certificate pinning checks.
 pref("security.cert_pinning.enforcement_level", 1);
 
new file mode 100644
--- /dev/null
+++ b/browser/base/content/aboutProviderDirectory.xhtml
@@ -0,0 +1,60 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+   - License, v. 2.0. If a copy of the MPL was not distributed with this
+   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+<!DOCTYPE html [
+  <!ENTITY % htmlDTD
+    PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+    "DTD/xhtml1-strict.dtd">
+  %htmlDTD;
+  <!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd" >
+  %brandDTD;
+  <!ENTITY % browserDTD SYSTEM "chrome://browser/locale/browser.dtd">
+  %browserDTD;
+]>
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+  <head>
+    <title>&social.directory.label;</title>
+    <link rel="stylesheet" type="text/css" media="all"
+          href="chrome://browser/skin/aboutProviderDirectory.css"/>
+  </head>
+
+  <body>
+    <div id="activation-link" hidden="true">
+      <div id="message-box">
+        <p>&social.directory.text;</p>
+      </div>
+      <div id="button-box">
+        <button onclick="openDirectory()">&social.directory.button;</button>
+      </div>
+    </div>
+    <div id="activation" hidden="true">
+      <p>&social.directory.introText;</p>
+      <div><iframe id="activation-frame"/></div>
+      <p><a class="link" onclick="openDirectory()">&social.directory.viewmore.text;</a></p>
+    </div>
+  </body>
+
+  <script type="text/javascript;version=1.8"><![CDATA[
+    const Cu = Components.utils;
+
+    Cu.import("resource://gre/modules/Services.jsm");
+
+    function openDirectory() {
+      let url = Services.prefs.getCharPref("social.directories").split(',')[0];
+      window.open(url);
+      window.close();
+    }
+    
+    if (Services.prefs.getBoolPref("social.share.activationPanelEnabled")) {
+      let url = Services.prefs.getCharPref("social.shareDirectory");
+      document.getElementById("activation-frame").setAttribute("src", url);
+      document.getElementById("activation").removeAttribute("hidden");
+    } else {
+      document.getElementById("activation-link").removeAttribute("hidden");
+    }
+  ]]></script>
+</html>
--- a/browser/base/content/aboutSocialError.xhtml
+++ b/browser/base/content/aboutSocialError.xhtml
@@ -37,37 +37,36 @@
     Cu.import("resource://gre/modules/Services.jsm");
     Cu.import("resource:///modules/Social.jsm");
 
     let config = {
       tryAgainCallback: reloadProvider
     }
 
     function parseQueryString() {
-      let url = document.documentURI;
-      let queryString = url.replace(/^about:socialerror\??/, "");
-
-      let modeMatch = queryString.match(/mode=([^&]+)/);
-      let mode = modeMatch && modeMatch[1] ? modeMatch[1] : "";
-      let originMatch = queryString.match(/origin=([^&]+)/);
-      config.origin = originMatch && originMatch[1] ? decodeURIComponent(originMatch[1]) : "";
+      let searchParams = new URLSearchParams(location.href.split("?")[1]);
+      let mode = searchParams.get("mode");
+      config.directory = searchParams.get("directory");
+      config.origin = searchParams.get("origin");
+      let encodedURL = searchParams.get("url");
+      let url = decodeURIComponent(encodedURL);
+      if (config.directory) {
+        let URI = Services.io.newURI(url, null, null);
+        config.origin = Services.scriptSecurityManager.getNoAppCodebasePrincipal(URI).origin;
+      }
 
       switch (mode) {
         case "compactInfo":
           document.getElementById("btnTryAgain").style.display = 'none';
           document.getElementById("btnCloseSidebar").style.display = 'none';
           break;
         case "tryAgainOnly":
           document.getElementById("btnCloseSidebar").style.display = 'none';
           //intentional fall-through
         case "tryAgain":
-          let urlMatch = queryString.match(/url=([^&]+)/);
-          let encodedURL = urlMatch && urlMatch[1] ? urlMatch[1] : "";
-          url = decodeURIComponent(encodedURL);
-
           config.tryAgainCallback = loadQueryURL;
           config.queryURL = url;
           break;
         case "workerFailure":
           config.tryAgainCallback = reloadProvider;
           break;
         default:
           break;
@@ -75,17 +74,17 @@
     }
 
     function setUpStrings() {
       let brandBundle = Services.strings.createBundle("chrome://branding/locale/brand.properties");
       let browserBundle = Services.strings.createBundle("chrome://browser/locale/browser.properties");
 
       let productName = brandBundle.GetStringFromName("brandShortName");
       let provider = Social._getProviderFromOrigin(config.origin);
-      let providerName = provider && provider.name;
+      let providerName = provider ? provider.name : config.origin;
 
       // Sets up the error message
       let msg = browserBundle.formatStringFromName("social.error.message", [productName, providerName], 2);
       document.getElementById("main-error-msg").textContent = msg;
 
       // Sets up the buttons' labels and accesskeys
       let btnTryAgain = document.getElementById("btnTryAgain");
       btnTryAgain.textContent = browserBundle.GetStringFromName("social.error.tryAgain.label");
--- a/browser/base/content/browser-context.inc
+++ b/browser/base/content/browser-context.inc
@@ -67,18 +67,18 @@
                 accesskey="&openLinkInPrivateWindowCmd.accesskey;"
                 oncommand="gContextMenu.openLinkInPrivateWindow();"/>
       <menuseparator id="context-sep-open"/>
       <menuitem id="context-bookmarklink"
                 label="&bookmarkThisLinkCmd.label;"
                 accesskey="&bookmarkThisLinkCmd.accesskey;"
                 oncommand="gContextMenu.bookmarkLink();"/>
       <menuitem id="context-sharelink"
-                label="&shareLinkCmd.label;"
-                accesskey="&shareLinkCmd.accesskey;"
+                label="&shareLink.label;"
+                accesskey="&shareLink.accesskey;"
                 oncommand="gContextMenu.shareLink();"/>
       <menuitem id="context-savelink"
                 label="&saveLinkCmd.label;"
                 accesskey="&saveLinkCmd.accesskey;"
                 oncommand="gContextMenu.saveLink();"/>
       <menu id="context-marklinkMenu" label="&social.marklinkMenu.label;"
             accesskey="&social.marklinkMenu.accesskey;">
         <menupopup/>
@@ -195,18 +195,18 @@
                 accesskey="&copyAudioURLCmd.accesskey;"
                 oncommand="gContextMenu.copyMediaLocation();"/>
       <menuseparator id="context-sep-copyimage"/>
       <menuitem id="context-saveimage"
                 label="&saveImageCmd.label;"
                 accesskey="&saveImageCmd.accesskey;"
                 oncommand="gContextMenu.saveMedia();"/>
       <menuitem id="context-shareimage"
-                label="&shareImageCmd.label;"
-                accesskey="&shareImageCmd.accesskey;"
+                label="&shareImage.label;"
+                accesskey="&shareImage.accesskey;"
                 oncommand="gContextMenu.shareImage();"/>
       <menuitem id="context-sendimage"
                 label="&emailImageCmd.label;"
                 accesskey="&emailImageCmd.accesskey;"
                 oncommand="gContextMenu.sendMedia();"/>
       <menuitem id="context-setDesktopBackground"
                 label="&setDesktopBackgroundCmd.label;"
                 accesskey="&setDesktopBackgroundCmd.accesskey;"
@@ -220,18 +220,18 @@
                 accesskey="&viewImageDescCmd.accesskey;"
                 oncommand="gContextMenu.viewImageDesc(event);"
                 onclick="checkForMiddleClick(this, event);"/>
       <menuitem id="context-savevideo"
                 label="&saveVideoCmd.label;"
                 accesskey="&saveVideoCmd.accesskey;"
                 oncommand="gContextMenu.saveMedia();"/>
       <menuitem id="context-sharevideo"
-                label="&shareVideoCmd.label;"
-                accesskey="&shareVideoCmd.accesskey;"
+                label="&shareVideo.label;"
+                accesskey="&shareVideo.accesskey;"
                 oncommand="gContextMenu.shareVideo();"/>
       <menuitem id="context-saveaudio"
                 label="&saveAudioCmd.label;"
                 accesskey="&saveAudioCmd.accesskey;"
                 oncommand="gContextMenu.saveMedia();"/>
       <menuitem id="context-video-saveimage"
                 accesskey="&videoSaveImage.accesskey;"
                 label="&videoSaveImage.label;"
@@ -300,18 +300,18 @@
       <menuseparator id="context-sep-selectall"/>
       <menuitem id="context-keywordfield"
                 label="&keywordfield.label;"
                 accesskey="&keywordfield.accesskey;"
                 oncommand="AddKeywordForSearchField();"/>
       <menuitem id="context-searchselect"
                 oncommand="BrowserSearch.loadSearchFromContext(this.searchTerms);"/>
       <menuitem id="context-shareselect"
-                label="&shareSelectCmd.label;"
-                accesskey="&shareSelectCmd.accesskey;"
+                label="&shareSelect.label;"
+                accesskey="&shareSelect.accesskey;"
                 oncommand="gContextMenu.shareSelect(getBrowserSelection());"/>
       <menuseparator id="frame-sep"/>
       <menu id="frame" label="&thisFrameMenu.label;" accesskey="&thisFrameMenu.accesskey;">
         <menupopup>
           <menuitem id="context-showonlythisframe"
                     label="&showOnlyThisFrameCmd.label;"
                     accesskey="&showOnlyThisFrameCmd.accesskey;"
                     oncommand="gContextMenu.showOnlyThisFrame();"/>
new file mode 100644
--- /dev/null
+++ b/browser/base/content/browser-devedition.js
@@ -0,0 +1,85 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+/**
+ * Listeners for the DevEdition theme.  This adds an extra stylesheet
+ * to browser.xul if a pref is set and no other themes are applied.
+ */
+let DevEdition = {
+  _prefName: "browser.devedition.theme.enabled",
+  _themePrefName: "general.skins.selectedSkin",
+  _lwThemePrefName: "lightweightThemes.isThemeSelected",
+  _devtoolsThemePrefName: "devtools.theme",
+
+  styleSheetLocation: "chrome://browser/skin/devedition.css",
+  styleSheet: null,
+
+  init: function () {
+    this._updateDevtoolsThemeAttribute();
+    this._updateStyleSheet();
+
+    // Listen for changes to all prefs except for complete themes.
+    // No need for this since changing a complete theme requires a
+    // restart.
+    Services.prefs.addObserver(this._lwThemePrefName, this, false);
+    Services.prefs.addObserver(this._prefName, this, false);
+    Services.prefs.addObserver(this._devtoolsThemePrefName, this, false);
+  },
+
+  observe: function (subject, topic, data) {
+    if (topic == "nsPref:changed") {
+      if (data == this._devtoolsThemePrefName) {
+        this._updateDevtoolsThemeAttribute();
+      } else {
+        this._updateStyleSheet();
+      }
+    }
+  },
+
+  _updateDevtoolsThemeAttribute: function() {
+    // Set an attribute on root element to make it possible
+    // to change colors based on the selected devtools theme.
+    document.documentElement.setAttribute("devtoolstheme",
+      Services.prefs.getCharPref(this._devtoolsThemePrefName));
+  },
+
+  _updateStyleSheet: function() {
+    // Only try to apply the dev edition theme if it is preffered
+    // on and there are no other themes applied.
+    let lightweightThemeSelected = false;
+    try {
+      lightweightThemeSelected = Services.prefs.getBoolPref(this._lwThemePrefName);
+    } catch(e) {}
+
+    let defaultThemeSelected = false;
+    try {
+       defaultThemeSelected = Services.prefs.getCharPref(this._themePrefName) == "classic/1.0";
+    } catch(e) {}
+
+    let deveditionThemeEnabled = Services.prefs.getBoolPref(this._prefName) &&
+      !lightweightThemeSelected && defaultThemeSelected;
+
+    if (deveditionThemeEnabled && !this.styleSheet) {
+      let styleSheetAttr = `href="${this.styleSheetLocation}" type="text/css"`;
+      let styleSheet = this.styleSheet = document.createProcessingInstruction(
+        'xml-stylesheet', styleSheetAttr);
+      this.styleSheet.addEventListener("load", function onLoad() {
+        styleSheet.removeEventListener("load", onLoad);
+        ToolbarIconColor.inferFromText();
+      });
+      document.insertBefore(this.styleSheet, document.documentElement);
+    } else if (!deveditionThemeEnabled && this.styleSheet) {
+      this.styleSheet.remove();
+      this.styleSheet = null;
+      ToolbarIconColor.inferFromText();
+    }
+  },
+
+  uninit: function () {
+    Services.prefs.removeObserver(this._lwThemePrefName, this);
+    Services.prefs.removeObserver(this._prefName, this);
+    Services.prefs.removeObserver(this._devtoolsThemePrefName, this);
+    this.styleSheet = null;
+  }
+};
--- a/browser/base/content/browser-sets.inc
+++ b/browser/base/content/browser-sets.inc
@@ -114,31 +114,32 @@
 #ifdef E10S_TESTING_ONLY
     <command id="Tools:RemoteWindow"
       oncommand="OpenBrowserWindow({remote: true});"/>
     <command id="Tools:NonRemoteWindow"
       oncommand="OpenBrowserWindow({remote: false});"/>
 #endif
     <command id="History:UndoCloseTab" oncommand="undoCloseTab();"/>
     <command id="History:UndoCloseWindow" oncommand="undoCloseWindow();"/>
-    <command id="Social:SharePage" oncommand="SocialShare.sharePage();" disabled="true"/>
+    <command id="Social:SharePage" oncommand="SocialShare.sharePage();"/>
     <command id="Social:ToggleSidebar" oncommand="SocialSidebar.toggleSidebar();" hidden="true"/>
     <command id="Social:ToggleNotifications" oncommand="Social.toggleNotifications();" hidden="true"/>
     <command id="Social:Addons" oncommand="BrowserOpenAddonsMgr('addons://list/service');"/>
     <command id="Chat:Focus" oncommand="Cu.import('resource:///modules/Chat.jsm', {}).Chat.focus(window);"/>
   </commandset>
 
   <commandset id="placesCommands">
     <command id="Browser:ShowAllBookmarks"
              oncommand="PlacesCommandHook.showPlacesOrganizer('AllBookmarks');"/>
     <command id="Browser:ShowAllHistory"
              oncommand="PlacesCommandHook.showPlacesOrganizer('History');"/>
   </commandset>
 
   <broadcasterset id="mainBroadcasterSet">
+    <broadcaster id="Social:PageShareOrMark" disabled="true"/>
     <broadcaster id="viewBookmarksSidebar" autoCheck="false" label="&bookmarksButton.label;"
                  type="checkbox" group="sidebar" sidebarurl="chrome://browser/content/bookmarks/bookmarksPanel.xul"
                  oncommand="toggleSidebar('viewBookmarksSidebar');"/>
 
     <!-- for both places and non-places, the sidebar lives at
          chrome://browser/content/history/history-panel.xul so there are no
          problems when switching between versions -->
     <broadcaster id="viewHistorySidebar" autoCheck="false" sidebartitle="&historyButton.label;"
--- a/browser/base/content/browser-social.js
+++ b/browser/base/content/browser-social.js
@@ -43,16 +43,22 @@ XPCOMUtils.defineLazyGetter(this, "Creat
 });
 
 XPCOMUtils.defineLazyGetter(this, "CreateSocialMarkWidget", function() {
   let tmp = {};
   Cu.import("resource:///modules/Social.jsm", tmp);
   return tmp.CreateSocialMarkWidget;
 });
 
+XPCOMUtils.defineLazyGetter(this, "hookWindowCloseForPanelClose", function() {
+  let tmp = {};
+  Cu.import("resource://gre/modules/MozSocialAPI.jsm", tmp);
+  return tmp.hookWindowCloseForPanelClose;
+});
+
 SocialUI = {
   _initialized: false,
 
   // Called on delayed startup to initialize the UI
   init: function SocialUI_init() {
     if (this._initialized) {
       return;
     }
@@ -63,17 +69,17 @@ SocialUI = {
     Services.obs.addObserver(this, "social:providers-changed", false);
     Services.obs.addObserver(this, "social:provider-reload", false);
     Services.obs.addObserver(this, "social:provider-enabled", false);
     Services.obs.addObserver(this, "social:provider-disabled", false);
 
     Services.prefs.addObserver("social.toast-notifications.enabled", this, false);
 
     gBrowser.addEventListener("ActivateSocialFeature", this._activationEventHandler.bind(this), true, true);
-    PanelUI.panel.addEventListener("popupshown", SocialUI.updatePanelState, true);
+    CustomizableUI.addListener(this);
 
     // menupopups that list social providers. we only populate them when shown,
     // and if it has not been done already.
     document.getElementById("viewSidebarMenu").addEventListener("popupshowing", SocialSidebar.populateSidebarMenu, true);
     document.getElementById("social-statusarea-popup").addEventListener("popupshowing", SocialSidebar.populateSidebarMenu, true);
 
     Social.init().then((update) => {
       if (update)
@@ -96,18 +102,18 @@ SocialUI = {
     Services.obs.removeObserver(this, "social:profile-changed");
     Services.obs.removeObserver(this, "social:frameworker-error");
     Services.obs.removeObserver(this, "social:providers-changed");
     Services.obs.removeObserver(this, "social:provider-reload");
     Services.obs.removeObserver(this, "social:provider-enabled");
     Services.obs.removeObserver(this, "social:provider-disabled");
 
     Services.prefs.removeObserver("social.toast-notifications.enabled", this);
+    CustomizableUI.removeListener(this);
 
-    PanelUI.panel.removeEventListener("popupshown", SocialUI.updatePanelState, true);
     document.getElementById("viewSidebarMenu").removeEventListener("popupshowing", SocialSidebar.populateSidebarMenu, true);
     document.getElementById("social-statusarea-popup").removeEventListener("popupshowing", SocialSidebar.populateSidebarMenu, true);
 
     this._initialized = false;
   },
 
   observe: function SocialUI_observe(subject, topic, data) {
     switch (topic) {
@@ -161,39 +167,40 @@ SocialUI = {
   },
 
   _providersChanged: function() {
     SocialSidebar.clearProviderMenus();
     SocialSidebar.update();
     SocialShare.populateProviderMenu();
     SocialStatus.populateToolbarPalette();
     SocialMarks.populateToolbarPalette();
-    SocialShare.update();
   },
 
   // This handles "ActivateSocialFeature" events fired against content documents
   // in this window.  If this activation happens from within Firefox, such as
   // about:home or the share panel, we bypass the enable prompt. Any website
   // activation, such as from the activations directory or a providers website
   // will still get the prompt.
-  _activationEventHandler: function SocialUI_activationHandler(e, aBypassUserEnable=false) {
+  _activationEventHandler: function SocialUI_activationHandler(e, options={}) {
     let targetDoc;
     let node;
     if (e.target instanceof HTMLDocument) {
       // version 0 support
       targetDoc = e.target;
       node = targetDoc.documentElement
     } else {
       targetDoc = e.target.ownerDocument;
       node = e.target;
     }
     if (!(targetDoc instanceof HTMLDocument))
       return;
 
-    if (!aBypassUserEnable && targetDoc.defaultView != content)
+    // The share panel iframe will not match "content" so it passes a bypass
+    // flag
+    if (!options.bypassContentCheck && targetDoc.defaultView != content)
       return;
 
     // If we are in PB mode, we silently do nothing (bug 829404 exists to
     // do something sensible here...)
     if (PrivateBrowsingUtils.isWindowPrivate(window))
       return;
 
     // If the last event was received < 1s ago, ignore this one
@@ -219,21 +226,46 @@ SocialUI = {
         return;
       }
     }
     Social.installProvider(targetDoc, data, function(manifest) {
       Social.activateFromOrigin(manifest.origin, function(provider) {
         if (provider.sidebarURL) {
           SocialSidebar.show(provider.origin);
         }
+        if (provider.shareURL) {
+          // Ensure that the share button is somewhere usable.
+          // SocialShare.shareButton may return null if it is in the menu-panel
+          // and has never been visible, so we check the widget directly. If
+          // there is no area for the widget we move it into the toolbar.
+          let widget = CustomizableUI.getWidget("social-share-button");
+          if (!widget.areaType) {
+            CustomizableUI.addWidgetToArea("social-share-button", CustomizableUI.AREA_NAVBAR);
+            // ensure correct state
+            SocialUI.onCustomizeEnd(window);
+          }
+
+          // make this new provider the selected provider. If the panel hasn't
+          // been opened, we need to make the frame first.
+          SocialShare._createFrame();
+          SocialShare.iframe.setAttribute('src', 'data:text/plain;charset=utf8,');
+          SocialShare.iframe.setAttribute('origin', provider.origin);
+          // get the right button selected
+          SocialShare.populateProviderMenu();
+          if (SocialShare.panel.state == "open") {
+            SocialShare.sharePage(provider.origin);
+          }
+        }
         if (provider.postActivationURL) {
-          openUILinkIn(provider.postActivationURL, "tab");
+          // if activated from an open share panel, we load the landing page in
+          // a background tab
+          gBrowser.loadOneTab(provider.postActivationURL, {inBackground: SocialShare.panel.state == "open"});
         }
       });
-    }, aBypassUserEnable);
+    }, options);
   },
 
   showLearnMore: function() {
     let url = Services.urlFormatter.formatURLPref("app.support.baseURL") + "social-api";
     openUILinkIn(url, "tab");
   },
 
   closeSocialPanelForLinkTraversal: function (target, linkNode) {
@@ -273,31 +305,59 @@ SocialUI = {
 
   get enabled() {
     // Returns whether social is enabled *for this window*.
     if (this._chromeless || PrivateBrowsingUtils.isWindowPrivate(window))
       return false;
     return Social.providers.length > 0;
   },
 
-  updatePanelState :function(event) {
-    // we only want to update when the panel is initially opened, not during
-    // multiview changes
-    if (event.target != PanelUI.panel)
+  canShareOrMarkPage: function(aURI) {
+    // Bug 898706 we do not enable social in private sessions since frameworker
+    // would be shared between private and non-private windows
+    if (PrivateBrowsingUtils.isWindowPrivate(window))
+      return false;
+
+    return (aURI && (aURI.schemeIs('http') || aURI.schemeIs('https')));
+  },
+
+  onCustomizeEnd: function(aWindow) {
+    if (aWindow != window)
       return;
-    SocialUI.updateState();
+    // customization mode gets buttons out of sync with command updating, fix
+    // the disabled state
+    let canShare = this.canShareOrMarkPage(gBrowser.currentURI);
+    let shareButton = SocialShare.shareButton;
+    if (shareButton) {
+      if (canShare) {
+        shareButton.removeAttribute("disabled")
+      } else {
+        shareButton.setAttribute("disabled", "true")
+      }
+    }
+    // update the disabled state of the button based on the command
+    for (let node of SocialMarks.nodes) {
+      if (canShare) {
+        node.removeAttribute("disabled")
+      } else {
+        node.setAttribute("disabled", "true")
+      }
+    }
   },
 
   // called on tab/urlbar/location changes and after customization. Update
   // anything that is tab specific.
   updateState: function() {
+    if (location == "about:customizing")
+      return;
+    goSetCommandEnabled("Social:PageShareOrMark", this.canShareOrMarkPage(gBrowser.currentURI));
     if (!SocialUI.enabled)
       return;
+    // larger update that may change button icons
     SocialMarks.update();
-    SocialShare.update();
   }
 }
 
 SocialFlyout = {
   get panel() {
     return document.getElementById("social-flyout-panel");
   },
 
@@ -428,16 +488,22 @@ SocialFlyout = {
           Cu.reportError(e);
         }
       }
     });
   }
 }
 
 SocialShare = {
+  get _dynamicResizer() {
+    delete this._dynamicResizer;
+    this._dynamicResizer = new DynamicResizeWatcher();
+    return this._dynamicResizer;
+  },
+
   // Share panel may be attached to the overflow or menu button depending on
   // customization, we need to manage open state of the anchor.
   get anchor() {
     let widget = CustomizableUI.getWidget("social-share-button");
     return widget.forWindow(window).anchor;
   },
   get panel() {
     return document.getElementById("social-share-panel");
@@ -446,143 +512,107 @@ SocialShare = {
   get iframe() {
     // first element is our menu vbox.
     if (this.panel.childElementCount == 1)
       return null;
     else
       return this.panel.lastChild;
   },
 
+  _activationHandler: function(event) {
+    if (!Services.prefs.getBoolPref("social.share.activationPanelEnabled"))
+      return;
+    SocialUI._activationEventHandler(event, { bypassContentCheck: true, bypassInstallPanel: true });
+  },
+
   uninit: function () {
     if (this.iframe) {
+      this.iframe.removeEventListener("ActivateSocialFeature", this._activationHandler, true, true);
       this.iframe.remove();
     }
   },
 
   _createFrame: function() {
     let panel = this.panel;
-    if (!SocialUI.enabled || this.iframe)
+    if (this.iframe)
       return;
     this.panel.hidden = false;
     // create and initialize the panel for this window
     let iframe = document.createElement("browser");
     iframe.setAttribute("type", "content");
     iframe.setAttribute("class", "social-share-frame");
     iframe.setAttribute("context", "contentAreaContextMenu");
     iframe.setAttribute("tooltip", "aHTMLTooltip");
     iframe.setAttribute("disableglobalhistory", "true");
     iframe.setAttribute("flex", "1");
     panel.appendChild(iframe);
+    this.iframe.addEventListener("ActivateSocialFeature", this._activationHandler, true, true);
     this.populateProviderMenu();
   },
 
   getSelectedProvider: function() {
     let provider;
     let lastProviderOrigin = this.iframe && this.iframe.getAttribute("origin");
     if (lastProviderOrigin) {
       provider = Social._getProviderFromOrigin(lastProviderOrigin);
     }
-    // if they have a provider selected in the sidebar use that for the initial
-    // default in share
-    if (!provider)
-      provider = SocialSidebar.provider;
-    // if our provider has no shareURL, select the first one that does
-    if (!provider || !provider.shareURL) {
-      let providers = [p for (p of Social.providers) if (p.shareURL)];
-      provider = providers.length > 0  && providers[0];
-    }
     return provider;
   },
 
+  createTooltip: function(event) {
+    let tt = event.target;
+    let provider = Social._getProviderFromOrigin(tt.triggerNode.getAttribute("origin"));
+    tt.firstChild.setAttribute("value", provider.name);
+    tt.lastChild.setAttribute("value", provider.origin);
+  },
+
   populateProviderMenu: function() {
     if (!this.iframe)
       return;
     let providers = [p for (p of Social.providers) if (p.shareURL)];
     let hbox = document.getElementById("social-share-provider-buttons");
-    // selectable providers are inserted before the provider-menu seperator,
-    // remove any menuitems in that area
-    while (hbox.firstChild) {
+    // remove everything before the add-share-provider button (which should also
+    // be lastChild if any share providers were added)
+    let addButton = document.getElementById("add-share-provider");
+    while (hbox.firstChild != addButton) {
       hbox.removeChild(hbox.firstChild);
     }
-    // reset our share toolbar
-    // only show a selection if there is more than one
-    if (!SocialUI.enabled || providers.length < 2) {
-      this.panel.firstChild.hidden = true;
-      return;
-    }
     let selectedProvider = this.getSelectedProvider();
     for (let provider of providers) {
       let button = document.createElement("toolbarbutton");
       button.setAttribute("class", "toolbarbutton share-provider-button");
       button.setAttribute("type", "radio");
       button.setAttribute("group", "share-providers");
       button.setAttribute("image", provider.iconURL);
-      button.setAttribute("tooltiptext", provider.name);
+      button.setAttribute("tooltip", "share-button-tooltip");
       button.setAttribute("origin", provider.origin);
-      button.setAttribute("oncommand", "SocialShare.sharePage(this.getAttribute('origin')); this.checked=true;");
+      button.setAttribute("oncommand", "SocialShare.sharePage(this.getAttribute('origin'));");
       if (provider == selectedProvider) {
         this.defaultButton = button;
       }
-      hbox.appendChild(button);
+      hbox.insertBefore(button, addButton);
     }
     if (!this.defaultButton) {
-      this.defaultButton = hbox.firstChild
+      this.defaultButton = addButton;
     }
     this.defaultButton.setAttribute("checked", "true");
-    this.panel.firstChild.hidden = false;
   },
 
   get shareButton() {
     // web-panels (bookmark/sidebar) don't include customizableui, so
     // nsContextMenu fails when accessing shareButton, breaking
     // browser_bug409481.js.
     if (!window.CustomizableUI)
       return null;
     let widget = CustomizableUI.getWidget("social-share-button");
     if (!widget || !widget.areaType)
       return null;
     return widget.forWindow(window).node;
   },
 
-  canSharePage: function(aURI) {
-    // we do not enable sharing from private sessions
-    if (PrivateBrowsingUtils.isWindowPrivate(window))
-      return false;
-
-    if (!aURI || !(aURI.schemeIs('http') || aURI.schemeIs('https')))
-      return false;
-    return true;
-  },
-
-  update: function() {
-    let widget = CustomizableUI.getWidget("social-share-button");
-    if (!widget)
-      return;
-    let shareButton = widget.forWindow(window).node;
-    // hidden state is based on available share providers and location of
-    // button. It's always visible and disabled in the customization palette.
-    shareButton.hidden = !SocialUI.enabled || (widget.areaType &&
-                         [p for (p of Social.providers) if (p.shareURL)].length == 0);
-    let disabled = !widget.areaType || shareButton.hidden || !this.canSharePage(gBrowser.currentURI);
-
-    // 1. update the relevent command's disabled state so the keyboard
-    // shortcut only works when available.
-    // 2. If the button has been relocated to a place that is not visible by
-    // default (e.g. menu panel) then the disabled attribute will not update
-    // correctly based on the command, so we update the attribute directly as.
-    let cmd = document.getElementById("Social:SharePage");
-    if (disabled) {
-      cmd.setAttribute("disabled", "true");
-      shareButton.setAttribute("disabled", "true");
-    } else {
-      cmd.removeAttribute("disabled");
-      shareButton.removeAttribute("disabled");
-    }
-  },
-
   _onclick: function() {
     Services.telemetry.getHistogramById("SOCIAL_PANEL_CLICKS").add(0);
   },
   
   onShowing: function() {
     this.anchor.setAttribute("open", "true");
     this.iframe.addEventListener("click", this._onclick, true);
   },
@@ -602,46 +632,44 @@ SocialShare = {
     }
   },
 
   setErrorMessage: function() {
     let iframe = this.iframe;
     if (!iframe)
       return;
 
-    iframe.removeAttribute("src");
-    iframe.webNavigation.loadURI("about:socialerror?mode=compactInfo&origin=" +
-                                 encodeURIComponent(iframe.getAttribute("origin")),
-                                 null, null, null, null);
+    let url;
+    let origin = iframe.getAttribute("origin");
+    if (!origin) {
+      // directory site is down
+      url = "about:socialerror?mode=tryAgainOnly&directory=1&url=" + encodeURIComponent(iframe.getAttribute("src"));
+    } else {
+      url = "about:socialerror?mode=compactInfo&origin=" + encodeURIComponent(origin);
+    }
+    iframe.webNavigation.loadURI(url, null, null, null, null);
     sizeSocialPanelToContent(this.panel, iframe);
   },
 
   sharePage: function(providerOrigin, graphData, target) {
     // if providerOrigin is undefined, we use the last-used provider, or the
     // current/default provider.  The provider selection in the share panel
     // will call sharePage with an origin for us to switch to.
     this._createFrame();
     let iframe = this.iframe;
-    let provider;
-    if (providerOrigin)
-      provider = Social._getProviderFromOrigin(providerOrigin);
-    else
-      provider = this.getSelectedProvider();
-    if (!provider || !provider.shareURL)
-      return;
 
     // graphData is an optional param that either defines the full set of data
     // to be shared, or partial data about the current page. It is set by a call
     // in mozSocial API, or via nsContentMenu calls. If it is present, it MUST
     // define at least url. If it is undefined, we're sharing the current url in
     // the browser tab.
     let pageData = graphData ? graphData : this.currentShare;
     let sharedURI = pageData ? Services.io.newURI(pageData.url, null, null) :
                                 gBrowser.currentURI;
-    if (!this.canSharePage(sharedURI))
+    if (!SocialUI.canShareOrMarkPage(sharedURI))
       return;
 
     // the point of this action type is that we can use existing share
     // endpoints (e.g. oexchange) that do not support additional
     // socialapi functionality.  One tweak is that we shoot an event
     // containing the open graph data.
     if (!pageData || sharedURI == gBrowser.currentURI) {
       pageData = OpenGraphBuilder.getData(gBrowser);
@@ -653,42 +681,48 @@ SocialShare = {
       }
     }
     // if this is a share of a selected item, get any microdata
     if (!pageData.microdata && target) {
       pageData.microdata = OpenGraphBuilder.getMicrodata(gBrowser, target);
     }
     this.currentShare = pageData;
 
+    let provider;
+    if (providerOrigin)
+      provider = Social._getProviderFromOrigin(providerOrigin);
+    else
+      provider = this.getSelectedProvider();
+    if (!provider || !provider.shareURL) {
+      this.showDirectory();
+      return;
+    }
+    // check the menu button
+    let hbox = document.getElementById("social-share-provider-buttons");
+    let btn = hbox.querySelector("[origin='" + provider.origin + "']");
+    if (btn)
+      btn.checked = true;
+
     let shareEndpoint = OpenGraphBuilder.generateEndpointURL(provider.shareURL, pageData);
 
     let size = provider.getPageSize("share");
     if (size) {
-      if (this._dynamicResizer) {
-        this._dynamicResizer.stop();
-        this._dynamicResizer = null;
-      }
-      let {width, height} = size;
-      width += this.panel.boxObject.width - iframe.boxObject.width;
-      height += this.panel.boxObject.height - iframe.boxObject.height;
-      this.panel.sizeTo(width, height);
-    } else {
-      this._dynamicResizer = new DynamicResizeWatcher();
+      this._dynamicResizer.stop();
     }
 
     // if we've already loaded this provider/page share endpoint, we don't want
     // to add another load event listener.
     let reload = true;
     let endpointMatch = shareEndpoint == iframe.getAttribute("src");
     let docLoaded = iframe.contentDocument && iframe.contentDocument.readyState == "complete";
     if (endpointMatch && docLoaded) {
       reload = shareEndpoint != iframe.contentDocument.location.spec;
     }
     if (!reload) {
-      if (this._dynamicResizer)
+      if (!size)
         this._dynamicResizer.start(this.panel, iframe);
       iframe.docShell.isActive = true;
       iframe.docShell.isAppTab = true;
       let evt = iframe.contentDocument.createEvent("CustomEvent");
       evt.initCustomEvent("OpenGraphData", true, true, JSON.stringify(pageData));
       iframe.contentDocument.documentElement.dispatchEvent(evt);
     } else {
       // first time load, wait for load and dispatch after load
@@ -696,17 +730,23 @@ SocialShare = {
         iframe.removeEventListener("load", panelBrowserOnload, true);
         iframe.docShell.isActive = true;
         iframe.docShell.isAppTab = true;
         // to support standard share endpoints mimick window.open by setting
         // window.opener, some share endpoints rely on w.opener to know they
         // should close the window when done.
         iframe.contentWindow.opener = iframe.contentWindow;
         setTimeout(function() {
-          if (SocialShare._dynamicResizer) { // may go null if hidden quickly
+          if (size) {
+            let panel = SocialShare.panel;
+            let {width, height} = size;
+            width += panel.boxObject.width - iframe.boxObject.width;
+            height += panel.boxObject.height - iframe.boxObject.height;
+            panel.sizeTo(width, height);
+          } else {
             SocialShare._dynamicResizer.start(iframe.parentNode, iframe);
           }
         }, 0);
         let evt = iframe.contentDocument.createEvent("CustomEvent");
         evt.initCustomEvent("OpenGraphData", true, true, JSON.stringify(pageData));
         iframe.contentDocument.documentElement.dispatchEvent(evt);
       }, true);
     }
@@ -717,20 +757,41 @@ SocialShare = {
       if (purge > 0)
         iframe.sessionHistory.PurgeHistory(purge);
     }
 
     // always ensure that origin belongs to the endpoint
     let uri = Services.io.newURI(shareEndpoint, null, null);
     iframe.setAttribute("origin", provider.origin);
     iframe.setAttribute("src", shareEndpoint);
+    this._openPanel();
+  },
 
+  showDirectory: function() {
+    this._createFrame();
+    let iframe = this.iframe;
+    iframe.removeAttribute("origin");
+    iframe.addEventListener("load", function panelBrowserOnload(e) {
+      iframe.removeEventListener("load", panelBrowserOnload, true);
+      hookWindowCloseForPanelClose(iframe.contentWindow);
+      SocialShare._dynamicResizer.start(iframe.parentNode, iframe);
+
+      iframe.addEventListener("unload", function panelBrowserOnload(e) {
+        iframe.removeEventListener("unload", panelBrowserOnload, true);
+        SocialShare._dynamicResizer.stop();
+      }, true);
+    }, true);
+    iframe.setAttribute("src", "about:providerdirectory");
+    this._openPanel();
+  },
+
+  _openPanel: function() {
     let anchor = document.getAnonymousElementByAttribute(this.anchor, "class", "toolbarbutton-icon");
     this.panel.openPopup(anchor, "bottomcenter topright", 0, 0, false, false);
-    Social.setErrorListener(iframe, this.setErrorMessage.bind(this));
+    Social.setErrorListener(this.iframe, this.setErrorMessage.bind(this));
     Services.telemetry.getHistogramById("SOCIAL_TOOLBAR_BUTTONS").add(0);
   }
 };
 
 SocialSidebar = {
   _openStartTime: 0,
 
   // Whether the sidebar can be shown for this window.
@@ -1301,30 +1362,37 @@ SocialStatus = {
 
 
 /**
  * SocialMarks
  *
  * Handles updates to toolbox and signals all buttons to update when necessary.
  */
 SocialMarks = {
-  update: function() {
-    // querySelectorAll does not work on the menu panel the panel, so we have to
-    // do this the hard way.
-    let providers = SocialMarks.getProviders();
+  get nodes() {
+    let providers = [p for (p of Social.providers) if (p.markURL)];
     for (let p of providers) {
       let widgetId = SocialMarks._toolbarHelper.idFromOrigin(p.origin);
       let widget = CustomizableUI.getWidget(widgetId);
       if (!widget)
         continue;
       let node = widget.forWindow(window).node;
+      if (node)
+        yield node;
+    }
+  },
+  update: function() {
+    // querySelectorAll does not work on the menu panel, so we have to do this
+    // the hard way.
+    for (let node of this.nodes) {
       // xbl binding is not complete on startup when buttons are not in toolbar,
       // verify update is available
-      if (node && node.update)
+      if (node.update) {
         node.update();
+      }
     }
   },
 
   getProviders: function() {
     // only rely on providers that the user has placed in the UI somewhere. This
     // also means that populateToolbarPalette must be called prior to using this
     // method, otherwise you get a big fat zero. For our use case with context
     // menu's, this is ok.
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -194,16 +194,17 @@ let gInitialPages = [
   "about:home",
   "about:privatebrowsing",
   "about:welcomeback",
   "about:sessionrestore"
 ];
 
 #include browser-addons.js
 #include browser-customization.js
+#include browser-devedition.js
 #include browser-feeds.js
 #include browser-fullScreen.js
 #include browser-fullZoom.js
 #include browser-loop.js
 #include browser-places.js
 #include browser-plugins.js
 #include browser-safebrowsing.js
 #include browser-social.js
@@ -782,17 +783,17 @@ function gKeywordURIFixup({ target: brow
   };
 
   gDNSService.asyncResolve(hostName, 0, onLookupComplete, Services.tm.mainThread);
 }
 
 // Called when a docshell has attempted to load a page in an incorrect process.
 // This function is responsible for loading the page in the correct process.
 function RedirectLoad({ target: browser, data }) {
-  let tab = gBrowser._getTabForBrowser(browser);
+  let tab = gBrowser.getTabForBrowser(browser);
   // Flush the tab state before getting it
   TabState.flush(browser);
   let tabState = JSON.parse(SessionStore.getTabState(tab));
 
   if (data.historyIndex < 0) {
     // Add a pseudo-history state for the new url to load
     let newEntry = {
       url: data.uri,
@@ -829,16 +830,17 @@ var gBrowserInit = {
     // These routines add message listeners. They must run before
     // loading the frame script to ensure that we don't miss any
     // message sent between when the frame script is loaded and when
     // the listener is registered.
     DOMLinkHandler.init();
     gPageStyleMenu.init();
     LanguageDetectionListener.init();
     BrowserOnClick.init();
+    DevEdition.init();
 
     let mm = window.getGroupMessageManager("browsers");
     mm.loadFrameScript("chrome://browser/content/content.js", true);
 
     // initialize observers and listeners
     // and give C++ access to gBrowser
     XULBrowserWindow.init();
     window.QueryInterface(Ci.nsIInterfaceRequestor)
@@ -1386,16 +1388,18 @@ var gBrowserInit = {
     BookmarkingUI.uninit();
 
     TabsInTitlebar.uninit();
 
     ToolbarIconColor.uninit();
 
     BrowserOnClick.uninit();
 
+    DevEdition.uninit();
+
     var enumerator = Services.wm.getEnumerator(null);
     enumerator.getNext();
     if (!enumerator.hasMoreElements()) {
       document.persist("sidebar-box", "sidebarcommand");
       document.persist("sidebar-box", "width");
       document.persist("sidebar-box", "src");
       document.persist("sidebar-title", "value");
     }
@@ -3765,17 +3769,17 @@ var XULBrowserWindow = {
       } else {
         this.reloadCommand.removeAttribute("disabled");
       }
 
       if (gURLBar) {
         URLBarSetURI(aLocationURI);
 
         BookmarkingUI.onLocationChange();
-        SocialUI.updateState();
+        SocialUI.updateState(location);
       }
 
       // Utility functions for disabling find
       var shouldDisableFind = function shouldDisableFind(aDocument) {
         let docElt = aDocument.documentElement;
         return docElt && docElt.getAttribute("disablefastfind") == "true";
       }
 
--- a/browser/base/content/browser.xul
+++ b/browser/base/content/browser.xul
@@ -241,17 +241,21 @@
     <panel id="social-share-panel"
            class="social-panel"
            type="arrow"
            orient="horizontal"
            onpopupshowing="SocialShare.onShowing()"
            onpopuphidden="SocialShare.onHidden()"
            hidden="true">
       <vbox class="social-share-toolbar">
-        <arrowscrollbox id="social-share-provider-buttons" orient="vertical" flex="1"/>
+        <arrowscrollbox id="social-share-provider-buttons" orient="vertical" flex="1">
+          <toolbarbutton id="add-share-provider" class="toolbarbutton share-provider-button" type="radio"
+                         group="share-providers" tooltiptext="&findShareServices.label;"
+                         oncommand="SocialShare.showDirectory()"/>
+        </arrowscrollbox>
       </vbox>
     </panel>
 
     <panel id="social-notification-panel"
            class="social-panel"
            type="arrow"
            hidden="true"
            noautofocus="true"/>
@@ -491,16 +495,21 @@
       <label class="tooltip-label" value="&forwardButton.tooltip;"/>
 #ifdef XP_MACOSX
       <label class="tooltip-label" value="&backForwardButtonMenuMac.tooltip;"/>
 #else
       <label class="tooltip-label" value="&backForwardButtonMenu.tooltip;"/>
 #endif
     </tooltip>
 
+    <tooltip id="share-button-tooltip" onpopupshowing="SocialShare.createTooltip(event);">
+      <label class="tooltip-label"/>
+      <label class="tooltip-label"/>
+    </tooltip>
+
 #include popup-notifications.inc
 
 #include ../../components/customizableui/content/panelUI.inc.xul
 
     <hbox id="downloads-animation-container" mousethrough="always">
       <vbox id="downloads-notification-anchor">
         <vbox id="downloads-indicator-notification"/>
       </vbox>
@@ -664,17 +673,17 @@
            Should you need to add items to the toolbar here, make sure to also add them
            to the default placements of buttons in CustomizableUI.jsm, so the
            customization code doesn't get confused.
       -->
     <toolbar id="nav-bar" class="toolbar-primary chromeclass-toolbar"
              aria-label="&navbarCmd.label;"
              fullscreentoolbar="true" mode="icons" customizable="true"
              iconsize="small"
-             defaultset="urlbar-container,search-container,bookmarks-menu-button,downloads-button,home-button,loop-call-button,social-share-button,social-toolbar-item"
+             defaultset="urlbar-container,search-container,bookmarks-menu-button,downloads-button,home-button,loop-call-button"
              customizationtarget="nav-bar-customization-target"
              overflowable="true"
              overflowbutton="nav-bar-overflow-button"
              overflowtarget="widget-overflow-list"
              overflowpanel="widget-overflow"
              context="toolbar-context-menu">
 
       <hbox id="nav-bar-customization-target" flex="1">
@@ -911,25 +920,16 @@
                        ondragover="homeButtonObserver.onDragOver(event)"
                        ondragenter="homeButtonObserver.onDragOver(event)"
                        ondrop="homeButtonObserver.onDrop(event)"
                        ondragexit="homeButtonObserver.onDragExit(event)"
                        key="goHome"
                        onclick="BrowserGoHome(event);"
                        cui-areatype="toolbar"
                        aboutHomeOverrideTooltip="&abouthome.pageTitle;"/>
-
-        <toolbarbutton id="social-share-button"
-                       class="toolbarbutton-1 chromeclass-toolbar-additional"
-                       label="&sharePageCmd.label;"
-                       tooltiptext="&sharePageCmd.label;"
-                       cui-areatype="toolbar"
-                       removable="true"
-                       hidden="true"
-                       command="Social:SharePage"/>
       </hbox>
 
       <toolbarbutton id="nav-bar-overflow-button"
                      class="toolbarbutton-1 chromeclass-toolbar-additional overflow-button"
                      skipintoolbarset="true"
                      tooltiptext="&navbarOverflow.label;"/>
 
       <toolbaritem id="PanelUI-button"
--- a/browser/base/content/nsContextMenu.js
+++ b/browser/base/content/nsContextMenu.js
@@ -340,17 +340,17 @@ nsContextMenu.prototype = {
     linkmenus = document.getElementsByClassName("context-marklink");
     [m.hidden = !enableLinkMarkItems for (m of linkmenus)];
 
     // SocialShare
     let shareButton = SocialShare.shareButton;
     let shareEnabled = shareButton && !shareButton.disabled && !this.onSocial;
     let pageShare = shareEnabled && !(this.isContentSelected ||
                             this.onTextInput || this.onLink || this.onImage ||
-                            this.onVideo || this.onAudio);
+                            this.onVideo || this.onAudio || this.onCanvas);
     this.showItem("context-sharepage", pageShare);
     this.showItem("context-shareselect", shareEnabled && this.isContentSelected);
     this.showItem("context-sharelink", shareEnabled && (this.onLink || this.onPlainTextLink) && !this.onMailtoLink);
     this.showItem("context-shareimage", shareEnabled && this.onImage);
     this.showItem("context-sharevideo", shareEnabled && this.onVideo);
     this.setItemAttr("context-sharevideo", "disabled", !this.mediaURL);
   },
 
--- a/browser/base/content/socialmarks.xml
+++ b/browser/base/content/socialmarks.xml
@@ -3,17 +3,17 @@
 <bindings id="socialMarkBindings"
     xmlns="http://www.mozilla.org/xbl"
     xmlns:xbl="http://www.mozilla.org/xbl"
     xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
 
 
   <binding id="toolbarbutton-marks" display="xul:button"
            extends="chrome://global/content/bindings/toolbarbutton.xml#toolbarbutton">
-    <content disabled="true">
+    <content>
       <xul:panel anonid="panel" hidden="true" type="arrow" class="social-panel"/>
       <xul:image class="toolbarbutton-icon" xbl:inherits="validate,src=image,label"/>
       <xul:label class="toolbarbutton-text" crop="right" flex="1"
                  xbl:inherits="value=label,accesskey,crop,wrap"/>
       <xul:label class="toolbarbutton-multiline-text" flex="1"
                  xbl:inherits="xbl:text=label,accesskey,wrap"/>
     </content>
     <implementation implements="nsIDOMEventListener, nsIObserver">
@@ -108,26 +108,21 @@
           this._dynamicResizer.stop();
           this._dynamicResizer = null;
         }
         this.content.setAttribute("src", "about:blank");
         // called during onhidden, make sure the docshell is updated
         if (this._frame.docShell)
           this._frame.docShell.createAboutBlankContentViewer(null);
 
-        // do we have a savable page loaded?
-        let aURI = gBrowser.currentURI;
-        let disabled = !aURI || !(aURI.schemeIs('http') || aURI.schemeIs('https'));
-        // when overflowed in toolbar, we must have the attribute set
-        if (disabled) {
-          this.setAttribute("disabled", "true");
+        // disabled attr is set by Social:PageShareOrMark command
+        if (this.hasAttribute("disabled")) {
           this.isMarked = false;
         } else {
-          this.removeAttribute("disabled");
-          Social.isURIMarked(provider.origin, aURI, (isMarked) => {
+          Social.isURIMarked(provider.origin, gBrowser.currentURI, (isMarked) => {
             this.isMarked = isMarked;
           });
         }
 
         this.content.setAttribute("origin", provider.origin);
 
         let panel = this.panel;
         // if customization is currently happening, we may not have a panel
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -384,17 +384,17 @@
           // When not using remote browsers, we can take a fast path by getting
           // directly from the content window to the browser without looping
           // over all browsers.
           if (!gMultiProcessBrowser) {
             let browser = aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
                                  .getInterface(Ci.nsIWebNavigation)
                                  .QueryInterface(Ci.nsIDocShell)
                                  .chromeEventHandler;
-            return this._getTabForBrowser(browser);
+            return this.getTabForBrowser(browser);
           }
 
           for (let i = 0; i < this.browsers.length; i++) {
             // NB: We use contentWindowAsCPOW so that this code works both
             // for remote browsers as well. aWindow may be a CPOW.
             if (this.browsers[i].contentWindowAsCPOW == aWindow)
               return this.tabs[i];
           }
--- a/browser/base/content/test/general/browser.ini
+++ b/browser/base/content/test/general/browser.ini
@@ -304,16 +304,17 @@ skip-if = e10s
 [browser_contextSearchTabPosition.js]
 skip-if = os == "mac" || e10s # bug 967013, bug 926729
 [browser_ctrlTab.js]
 skip-if = e10s # Bug ????? - thumbnail captures need e10s love (tabPreviews_capture fails with Argument 1 of CanvasRenderingContext2D.drawWindow does not implement interface Window.)
 [browser_customize_popupNotification.js]
 skip-if = e10s
 [browser_datareporting_notification.js]
 run-if = datareporting
+[browser_devedition.js]
 [browser_devices_get_user_media.js]
 skip-if = buildapp == 'mulet' || (os == "linux" && debug) || e10s # linux: bug 976544; e10s: Bug 973001 - appears user media notifications only happen in the child and don't make their way to the parent?
 [browser_devices_get_user_media_about_urls.js]
 skip-if = e10s # Bug 973001 - appears user media notifications only happen in the child and don't make their way to the parent?
 [browser_discovery.js]
 skip-if = e10s # Bug 918663 - DOMLinkAdded events don't make their way to chrome
 [browser_duplicateIDs.js]
 [browser_drag.js]
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/general/browser_devedition.js
@@ -0,0 +1,66 @@
+/*
+ * Testing changes for Developer Edition theme.
+ * A special stylesheet should be added to the browser.xul document
+ * when browser.devedition.theme.enabled is set to true and no themes
+ * are applied.
+ */
+
+const PREF_DEVEDITION_THEME = "browser.devedition.theme.enabled";
+const PREF_THEME = "general.skins.selectedSkin";
+const PREF_LWTHEME = "lightweightThemes.isThemeSelected";
+const PREF_DEVTOOLS_THEME = "devtools.theme";
+
+registerCleanupFunction(() => {
+  // Set preferences back to their original values
+  Services.prefs.clearUserPref(PREF_DEVEDITION_THEME);
+  Services.prefs.clearUserPref(PREF_THEME);
+  Services.prefs.clearUserPref(PREF_LWTHEME);
+  Services.prefs.clearUserPref(PREF_DEVTOOLS_THEME);
+});
+
+function test() {
+  waitForExplicitFinish();
+  startTests();
+}
+
+function startTests() {
+  ok (!DevEdition.styleSheet, "There is no devedition style sheet by default.");
+
+  info ("Setting browser.devedition.theme.enabled to true.");
+  Services.prefs.setBoolPref(PREF_DEVEDITION_THEME, true);
+  ok (DevEdition.styleSheet, "There is a devedition stylesheet when no themes are applied and pref is set.");
+
+  info ("Adding a lightweight theme.");
+  Services.prefs.setBoolPref(PREF_LWTHEME, true);
+  ok (!DevEdition.styleSheet, "The devedition stylesheet has been removed when a lightweight theme is applied.");
+
+  info ("Removing a lightweight theme.");
+  Services.prefs.setBoolPref(PREF_LWTHEME, false);
+  ok (DevEdition.styleSheet, "The devedition stylesheet has been added when a lightweight theme is removed.");
+
+  // There are no listeners for the complete theme pref since applying the theme
+  // requires a restart.
+  info ("Setting general.skins.selectedSkin to a custom string.");
+  Services.prefs.setCharPref(PREF_THEME, "custom-theme");
+  ok (DevEdition.styleSheet, "The devedition stylesheet is still here when a complete theme is added.");
+
+  info ("Resetting general.skins.selectedSkin to default value.");
+  Services.prefs.clearUserPref(PREF_THEME);
+  ok (DevEdition.styleSheet, "The devedition stylesheet is still here when a complete theme is removed.");
+
+  info ("Setting browser.devedition.theme.enabled to false.");
+  Services.prefs.setBoolPref(PREF_DEVEDITION_THEME, false);
+  ok (!DevEdition.styleSheet, "The devedition stylesheet has been removed.");
+
+  info ("Checking :root attributes based on devtools theme.");
+  is (document.documentElement.getAttribute("devtoolstheme"), "light",
+    "The documentElement has an attribute based on devtools theme.");
+  Services.prefs.setCharPref(PREF_DEVTOOLS_THEME, "dark");
+  is (document.documentElement.getAttribute("devtoolstheme"), "dark",
+    "The documentElement has an attribute based on devtools theme.");
+  Services.prefs.setCharPref(PREF_DEVTOOLS_THEME, "light");
+  is (document.documentElement.getAttribute("devtoolstheme"), "light",
+    "The documentElement has an attribute based on devtools theme.");
+
+  finish();
+}
--- a/browser/base/content/test/social/browser.ini
+++ b/browser/base/content/test/social/browser.ini
@@ -6,16 +6,17 @@ support-files =
   head.js
   opengraph/og_invalid_url.html
   opengraph/opengraph.html
   opengraph/shortlink_linkrel.html
   opengraph/shorturl_link.html
   opengraph/shorturl_linkrel.html
   microdata.html
   share.html
+  share_activate.html
   social_activate.html
   social_activate_iframe.html
   social_chat.html
   social_crash_content_helper.js
   social_flyout.html
   social_mark.html
   social_panel.html
   social_postActivation.html
--- a/browser/base/content/test/social/browser_aboutHome_activation.js
+++ b/browser/base/content/test/social/browser_aboutHome_activation.js
@@ -14,17 +14,17 @@ XPCOMUtils.defineLazyModuleGetter(this, 
 let snippet =
 '     <script>' +
 '       var manifest = {' +
 '         "name": "Demo Social Service",' +
 '         "origin": "https://example.com",' +
 '         "iconURL": "chrome://branding/content/icon16.png",' +
 '         "icon32URL": "chrome://branding/content/favicon32.png",' +
 '         "icon64URL": "chrome://branding/content/icon64.png",' +
-'         "sidebarURL": "https://example.com/browser/browser/base/content/test/social/social_sidebar.html",' +
+'         "sidebarURL": "https://example.com/browser/browser/base/content/test/social/social_sidebar_empty.html",' +
 '         "postActivationURL": "https://example.com/browser/browser/base/content/test/social/social_postActivation.html",' +
 '       };' +
 '       function activateProvider(node) {' +
 '         node.setAttribute("data-service", JSON.stringify(manifest));' +
 '         var event = new CustomEvent("ActivateSocialFeature");' +
 '         node.dispatchEvent(event);' +
 '       }' +
 '     </script>' +
@@ -36,17 +36,17 @@ let snippet =
 let snippet2 =
 '     <script>' +
 '       var manifest = {' +
 '         "name": "Demo Social Service",' +
 '         "origin": "https://example.com",' +
 '         "iconURL": "chrome://branding/content/icon16.png",' +
 '         "icon32URL": "chrome://branding/content/favicon32.png",' +
 '         "icon64URL": "chrome://branding/content/icon64.png",' +
-'         "sidebarURL": "https://example.com/browser/browser/base/content/test/social/social_sidebar.html",' +
+'         "sidebarURL": "https://example.com/browser/browser/base/content/test/social/social_sidebar_empty.html",' +
 '         "postActivationURL": "https://example.com/browser/browser/base/content/test/social/social_postActivation.html",' +
 '         "oneclick": true' +
 '       };' +
 '       function activateProvider(node) {' +
 '         node.setAttribute("data-service", JSON.stringify(manifest));' +
 '         var event = new CustomEvent("ActivateSocialFeature");' +
 '         node.dispatchEvent(event);' +
 '       }' +
--- a/browser/base/content/test/social/browser_share.js
+++ b/browser/base/content/test/social/browser_share.js
@@ -5,21 +5,57 @@ let baseURL = "https://example.com/brows
 
 let manifest = { // normal provider
   name: "provider 1",
   origin: "https://example.com",
   workerURL: "https://example.com/browser/browser/base/content/test/social/social_worker.js",
   iconURL: "https://example.com/browser/browser/base/content/test/general/moz.png",
   shareURL: "https://example.com/browser/browser/base/content/test/social/share.html"
 };
+let activationPage = "https://example.com/browser/browser/base/content/test/social/share_activate.html";
+
+function sendActivationEvent(subframe) {
+  // hack Social.lastEventReceived so we don't hit the "too many events" check.
+  Social.lastEventReceived = 0;
+  let doc = subframe.contentDocument;
+  // if our test has a frame, use it
+  let button = doc.getElementById("activation");
+  ok(!!button, "got the activation button");
+  EventUtils.synthesizeMouseAtCenter(button, {}, doc.defaultView);
+}
+
+function promiseShareFrameEvent(iframe, eventName) {
+  let deferred = Promise.defer();
+  iframe.addEventListener(eventName, function load() {
+    info("page load is " + iframe.contentDocument.location.href);
+    if (iframe.contentDocument.location.href != "data:text/plain;charset=utf8,") {
+      iframe.removeEventListener(eventName, load, true);
+      deferred.resolve();
+    }
+  }, true);
+  return deferred.promise;
+}
 
 function test() {
   waitForExplicitFinish();
-
-  runSocialTests(tests);
+  Services.prefs.setCharPref("social.shareDirectory", activationPage);
+  registerCleanupFunction(function () {
+    Services.prefs.clearUserPref("social.directories");
+    Services.prefs.clearUserPref("social.shareDirectory");
+    Services.prefs.clearUserPref("social.share.activationPanelEnabled");
+  });
+  runSocialTests(tests, undefined, function(next) {
+    let shareButton = SocialShare.shareButton;
+    if (shareButton) {
+      CustomizableUI.removeWidgetFromArea("social-share-button", CustomizableUI.AREA_NAVBAR)
+      shareButton.remove();
+    }
+    ok(CustomizableUI.inDefaultState, "Should start in default state.");
+    next();
+  });
 }
 
 let corpus = [
   {
     url: baseURL+"opengraph/opengraph.html",
     options: {
       // og:title
       title: ">This is my title<",
@@ -67,26 +103,16 @@ let corpus = [
     options: {
       previews: ["http://example.com/1234/56789.jpg"],
       url: "http://www.example.com/photos/56789/",
       shortUrl: "http://imshort/p/abcde"
     }
   }
 ];
 
-function loadURLInTab(url, callback) {
-  info("Loading tab with "+url);
-  let tab = gBrowser.selectedTab = gBrowser.addTab(url);
-  tab.linkedBrowser.addEventListener("load", function listener() {
-    is(tab.linkedBrowser.currentURI.spec, url, "tab loaded")
-    tab.linkedBrowser.removeEventListener("load", listener, true);
-    executeSoon(function() { callback(tab) });
-  }, true);
-}
-
 function hasoptions(testOptions, options) {
   let msg;
   for (let option in testOptions) {
     let data = testOptions[option];
     info("data: "+JSON.stringify(data));
     let message_data = options[option];
     info("message_data: "+JSON.stringify(message_data));
     if (Array.isArray(data)) {
@@ -100,40 +126,49 @@ function hasoptions(testOptions, options
   }
 }
 
 var tests = {
   testShareDisabledOnActivation: function(next) {
     // starting on about:blank page, share should be visible but disabled when
     // adding provider
     is(gBrowser.contentDocument.location.href, "about:blank");
+
+    // initialize the button into the navbar
+    CustomizableUI.addWidgetToArea("social-share-button", CustomizableUI.AREA_NAVBAR);
+    // ensure correct state
+    SocialUI.onCustomizeEnd(window);
+
     SocialService.addProvider(manifest, function(provider) {
       is(SocialUI.enabled, true, "SocialUI is enabled");
       checkSocialUI();
       // share should not be enabled since we only have about:blank page
       let shareButton = SocialShare.shareButton;
-      is(shareButton.disabled, true, "share button is disabled");
       // verify the attribute for proper css
       is(shareButton.getAttribute("disabled"), "true", "share button attribute is disabled");
       // button should be visible
       is(shareButton.hidden, false, "share button is visible");
       SocialService.disableProvider(manifest.origin, next);
     });
   },
   testShareEnabledOnActivation: function(next) {
     // starting from *some* page, share should be visible and enabled when
     // activating provider
+    // initialize the button into the navbar
+    CustomizableUI.addWidgetToArea("social-share-button", CustomizableUI.AREA_NAVBAR);
+    // ensure correct state
+    SocialUI.onCustomizeEnd(window);
+
     let testData = corpus[0];
-    loadURLInTab(testData.url, function(tab) {
+    addTab(testData.url, function(tab) {
       SocialService.addProvider(manifest, function(provider) {
         is(SocialUI.enabled, true, "SocialUI is enabled");
         checkSocialUI();
         // share should not be enabled since we only have about:blank page
         let shareButton = SocialShare.shareButton;
-        is(shareButton.disabled, false, "share button is enabled");
         // verify the attribute for proper css
         ok(!shareButton.hasAttribute("disabled"), "share button is enabled");
         // button should be visible
         is(shareButton.hidden, false, "share button is visible");
         gBrowser.removeTab(tab);
         next();
       });
     });
@@ -142,19 +177,19 @@ var tests = {
     let provider = Social._getProviderFromOrigin(manifest.origin);
     let port = provider.getWorkerPort();
     ok(port, "provider has a port");
     let testTab;
     let testIndex = 0;
     let testData = corpus[testIndex++];
 
     function runOneTest() {
-      loadURLInTab(testData.url, function(tab) {
+      addTab(testData.url, function(tab) {
         testTab = tab;
-        SocialShare.sharePage();
+        SocialShare.sharePage(manifest.origin);
       });
     }
 
     port.onmessage = function (e) {
       let topic = e.data.topic;
       switch (topic) {
         case "got-share-data-message":
           gBrowser.removeTab(testTab);
@@ -236,10 +271,53 @@ var tests = {
       let url = "https://example.com/browser/browser/base/content/test/social/microdata.html"
       addTab(url, function(tab) {
         testTab = tab;
         let doc = tab.linkedBrowser.contentDocument;
         target = doc.getElementById("simple-hcard");
         SocialShare.sharePage(manifest.origin, null, target);
       });
     });
+  },
+  testSharePanelActivation: function(next) {
+    let testTab;
+    // cleared in the cleanup function
+    Services.prefs.setCharPref("social.directories", "https://example.com");
+    Services.prefs.setBoolPref("social.share.activationPanelEnabled", true);
+    // make the iframe so we can wait on the load
+    SocialShare._createFrame();
+    let iframe = SocialShare.iframe;
+
+    promiseShareFrameEvent(iframe, "load").then(() => {
+      let subframe = iframe.contentDocument.getElementById("activation-frame");
+      waitForCondition(() => {
+          // sometimes the iframe is ready before the panel is open, we need to
+          // wait for both conditions
+          return SocialShare.panel.state == "open"
+                 && subframe.contentDocument
+                 && subframe.contentDocument.readyState == "complete";
+        }, () => {
+        is(subframe.contentDocument.location.href, activationPage, "activation page loaded");
+        promiseObserverNotified("social:provider-enabled").then(() => {
+          let provider = Social._getProviderFromOrigin(manifest.origin);
+          let port = provider.getWorkerPort();
+          ok(!!port, "got port");
+          port.onmessage = function (e) {
+            let topic = e.data.topic;
+            switch (topic) {
+              case "got-share-data-message":
+                ok(true, "share completed");
+                gBrowser.removeTab(testTab);
+                SocialService.uninstallProvider(manifest.origin, next);
+                break;
+            }
+          }
+          port.postMessage({topic: "test-init"});
+        });
+        sendActivationEvent(subframe);
+      }, "share panel did not open and load share page");
+    });
+    addTab(activationPage, function(tab) {
+      testTab = tab;
+      SocialShare.sharePage();
+    });
   }
 }
--- a/browser/base/content/test/social/browser_social_chatwindow.js
+++ b/browser/base/content/test/social/browser_social_chatwindow.js
@@ -23,67 +23,80 @@ let manifests = [
     name: "provider@test2",
     origin: "https://test2.example.com",
     sidebarURL: "https://test2.example.com/browser/browser/base/content/test/social/social_sidebar.html?test2",
     workerURL: "https://test2.example.com/browser/browser/base/content/test/social/social_worker.js",
     iconURL: "chrome://branding/content/icon48.png"
   }
 ];
 
+let ports = [];
+function getProviderPort(provider) {
+  let port = provider.getWorkerPort();
+  ok(port, "provider has a port");
+  ports.push(port);
+  return port;
+}
 let chatId = 0;
 function openChat(provider, callback) {
   let chatUrl = provider.origin + "/browser/browser/base/content/test/social/social_chat.html";
-  let port = provider.getWorkerPort();
+  let port = getProviderPort(provider);
   port.onmessage = function(e) {
     if (e.data.topic == "got-chatbox-message") {
-      port.close();
       callback();
     }
   }
   let url = chatUrl + "?" + (chatId++);
   port.postMessage({topic: "test-init"});
   port.postMessage({topic: "test-worker-chat", data: url});
   gURLsNotRemembered.push(url);
+  return port;
 }
 
 function windowHasChats(win) {
   return !!getChatBar().firstElementChild;
 }
 
 function test() {
   requestLongerTimeout(2); // only debug builds seem to need more time...
   waitForExplicitFinish();
 
   let oldwidth = window.outerWidth; // we futz with these, so we restore them
   let oldleft = window.screenX;
   window.moveTo(0, window.screenY)
   let postSubTest = function(cb) {
+    // ensure ports are closed
+    for (let port of ports) {
+      port.close()
+      ok(port._closed, "port closed");
+    }
+    ports = [];
+
     let chats = document.getElementById("pinnedchats");
     ok(chats.children.length == 0, "no chatty children left behind");
     cb();
   };
   runSocialTestWithProvider(manifests, function (finishcb) {
     ok(Social.enabled, "Social is enabled");
-    ok(Social.providers[0].getWorkerPort(), "provider 0 has port");
-    ok(Social.providers[1].getWorkerPort(), "provider 1 has port");
-    ok(Social.providers[2].getWorkerPort(), "provider 2 has port");
+    ok(getProviderPort(Social.providers[0]), "provider 0 has port");
+    ok(getProviderPort(Social.providers[1]), "provider 1 has port");
+    ok(getProviderPort(Social.providers[2]), "provider 2 has port");
     SocialSidebar.show();
     runSocialTests(tests, undefined, postSubTest, function() {
       window.moveTo(oldleft, window.screenY)
       window.resizeTo(oldwidth, window.outerHeight);
       finishcb();
     });
   });
 }
 
 var tests = {
   testOpenCloseChat: function(next) {
     let chats = document.getElementById("pinnedchats");
-    let port = SocialSidebar.provider.getWorkerPort();
-    ok(port, "provider has a port");
+    let port = getProviderPort(SocialSidebar.provider);
     port.onmessage = function (e) {
       let topic = e.data.topic;
       switch (topic) {
         case "got-sidebar-message":
           port.postMessage({topic: "test-chatbox-open"});
           break;
         case "got-chatbox-visibility":
           if (e.data.result == "hidden") {
@@ -91,17 +104,16 @@ var tests = {
             chats.selectedChat.toggle();
           } else if (e.data.result == "shown") {
             ok(true, "chatbox got shown");
             // close it now
             let content = chats.selectedChat.content;
             content.addEventListener("unload", function chatUnload() {
               content.removeEventListener("unload", chatUnload, true);
               ok(true, "got chatbox unload on close");
-              port.close();
               next();
             }, true);
             chats.selectedChat.close();
           }
           break;
         case "got-chatbox-message":
           ok(true, "got chatbox message");
           ok(e.data.result == "ok", "got chatbox windowRef result: "+e.data.result);
@@ -109,42 +121,39 @@ var tests = {
           break;
       }
     }
     port.postMessage({topic: "test-init", data: { id: 1 }});
   },
   testWorkerChatWindow: function(next) {
     const chatUrl = SocialSidebar.provider.origin + "/browser/browser/base/content/test/social/social_chat.html";
     let chats = document.getElementById("pinnedchats");
-    let port = SocialSidebar.provider.getWorkerPort();
-    ok(port, "provider has a port");
+    let port = getProviderPort(SocialSidebar.provider);
     port.postMessage({topic: "test-init"});
     port.onmessage = function (e) {
       let topic = e.data.topic;
       switch (topic) {
         case "got-chatbox-message":
           ok(true, "got a chat window opened");
           ok(chats.selectedChat, "chatbox from worker opened");
           while (chats.selectedChat) {
             chats.selectedChat.close();
           }
           ok(!chats.selectedChat, "chats are all closed");
           gURLsNotRemembered.push(chatUrl);
-          port.close();
           next();
           break;
       }
     }
     ok(!chats.selectedChat, "chats are all closed");
     port.postMessage({topic: "test-worker-chat", data: chatUrl});
   },
   testCloseSelf: function(next) {
     let chats = document.getElementById("pinnedchats");
-    let port = SocialSidebar.provider.getWorkerPort();
-    ok(port, "provider has a port");
+    let port = getProviderPort(SocialSidebar.provider);
     port.onmessage = function (e) {
       let topic = e.data.topic;
       switch (topic) {
         case "test-init-done":
           port.postMessage({topic: "test-chatbox-open"});
           break;
         case "got-chatbox-visibility":
           is(e.data.result, "shown", "chatbox shown");
@@ -152,31 +161,32 @@ var tests = {
           let chat = chats.selectedChat;
           ok(chat.parentNode, "chat has a parent node before it is closed");
           // ask it to close itself.
           let doc = chat.contentDocument;
           let evt = doc.createEvent("CustomEvent");
           evt.initCustomEvent("socialTest-CloseSelf", true, true, {});
           doc.documentElement.dispatchEvent(evt);
           ok(!chat.parentNode, "chat is now closed");
-          port.close();
           next();
           break;
       }
     }
     port.postMessage({topic: "test-init", data: { id: 1 }});
   },
 
   // Check what happens when you close the only visible chat.
   testCloseOnlyVisible: function(next) {
     let chatbar = getChatBar();
     let chatWidth = undefined;
     let num = 0;
     is(chatbar.childNodes.length, 0, "chatbar starting empty");
     is(chatbar.menupopup.childNodes.length, 0, "popup starting empty");
+    let port = getProviderPort(SocialSidebar.provider);
+    port.postMessage({topic: "test-init"});
 
     makeChat("normal", "first chat", function() {
       // got the first one.
       checkPopup();
       ok(chatbar.menupopup.parentNode.collapsed, "menu selection isn't visible");
       // we kinda cheat here and get the width of the first chat, assuming
       // that all future chats will have the same width when open.
       chatWidth = chatbar.calcTotalWidthOf(chatbar.selectedChat);
@@ -197,107 +207,102 @@ var tests = {
           closeAllChats();
           next();
         });
       });
     });
   },
 
   testShowWhenCollapsed: function(next) {
-    let port = SocialSidebar.provider.getWorkerPort();
+    let port = getProviderPort(SocialSidebar.provider);
     port.postMessage({topic: "test-init"});
     get3ChatsForCollapsing("normal", function(first, second, third) {
       let chatbar = getChatBar();
       chatbar.showChat(first);
       ok(!first.collapsed, "first should no longer be collapsed");
-      ok(second.collapsed ||  third.collapsed, false, "one of the others should be collapsed");
+      is(second.collapsed ||  third.collapsed, true, "one of the others should be collapsed");
       closeAllChats();
-      port.close();
       next();
     });
   },
 
   testOnlyOneCallback: function(next) {
     let chats = document.getElementById("pinnedchats");
-    let port = SocialSidebar.provider.getWorkerPort();
+    let port = getProviderPort(SocialSidebar.provider);
     let numOpened = 0;
     port.onmessage = function (e) {
       let topic = e.data.topic;
       switch (topic) {
         case "test-init-done":
           port.postMessage({topic: "test-chatbox-open"});
           break;
         case "chatbox-opened":
           numOpened += 1;
           port.postMessage({topic: "ping"});
           break;
         case "pong":
           executeSoon(function() {
             is(numOpened, 1, "only got one open message");
             chats.selectedChat.close();
-            port.close();
             next();
           });
       }
     }
     port.postMessage({topic: "test-init", data: { id: 1 }});
   },
 
   testMultipleProviderChat: function(next) {
     // test incomming chats from all providers
-    openChat(Social.providers[0], function() {
-      openChat(Social.providers[1], function() {
-        openChat(Social.providers[2], function() {
+    let port0 = openChat(Social.providers[0], function() {
+      let port1 = openChat(Social.providers[1], function() {
+        let port2 = openChat(Social.providers[2], function() {
           let chats = document.getElementById("pinnedchats");
           waitForCondition(function() chats.children.length == Social.providers.length,
             function() {
               ok(true, "one chat window per provider opened");
               // test logout of a single provider
-              let provider = Social.providers[2];
-              let port = provider.getWorkerPort();
-              port.postMessage({topic: "test-logout"});
+              port2.postMessage({topic: "test-logout"});
               waitForCondition(function() chats.children.length == Social.providers.length - 1,
                 function() {
                   closeAllChats();
                   waitForCondition(function() chats.children.length == 0,
                                    function() {
                                     ok(!chats.selectedChat, "multiprovider chats are all closed");
-                                    port.close();
                                     next();
                                    },
                                    "chat windows didn't close");
                 },
                 "chat window didn't close");
             }, "chat windows did not open");
         });
       });
     });
   },
 
   // XXX - note this must be the last test until we restore the login state
   // between tests...
   testCloseOnLogout: function(next) {
     const chatUrl = SocialSidebar.provider.origin + "/browser/browser/base/content/test/social/social_chat.html";
     let port = SocialSidebar.provider.getWorkerPort();
+    ports.push(port);
     ok(port, "provider has a port");
     let opened = false;
     port.onmessage = function (e) {
       let topic = e.data.topic;
       switch (topic) {
         case "test-init-done":
           info("open first chat window");
           port.postMessage({topic: "test-worker-chat", data: chatUrl});
           break;
         case "got-chatbox-message":
           ok(true, "got a chat window opened");
           if (opened) {
             port.postMessage({topic: "test-logout"});
             waitForCondition(function() document.getElementById("pinnedchats").firstChild == null,
                              function() {
-                              port.close();
                               next();
                              },
                              "chat windows didn't close");
           } else {
             // open a second chat window
             opened = true;
             port.postMessage({topic: "test-worker-chat", data: chatUrl+"?id=1"});
           }
--- a/browser/base/content/test/social/browser_social_errorPage.js
+++ b/browser/base/content/test/social/browser_social_errorPage.js
@@ -10,208 +10,217 @@ function gc() {
 }
 
 let openChatWindow = Cu.import("resource://gre/modules/MozSocialAPI.jsm", {}).openChatWindow;
 
 // Support for going on and offline.
 // (via browser/base/content/test/browser_bookmark_titles.js)
 let origProxyType = Services.prefs.getIntPref('network.proxy.type');
 
+function toggleOfflineStatus(goOffline) {
+  // Bug 968887 fix.  when going on/offline, wait for notification before continuing
+  let deferred = Promise.defer();
+  if (!goOffline) {
+    Services.prefs.setIntPref('network.proxy.type', origProxyType);
+  }
+  if (goOffline != Services.io.offline) {
+    info("initial offline state " + Services.io.offline);
+    let expect = !Services.io.offline;
+    Services.obs.addObserver(function offlineChange(subject, topic, data) {
+      Services.obs.removeObserver(offlineChange, "network:offline-status-changed");
+      info("offline state changed to " + Services.io.offline);
+      is(expect, Services.io.offline, "network:offline-status-changed successful toggle");
+      deferred.resolve();
+    }, "network:offline-status-changed", false);
+    BrowserOffline.toggleOfflineStatus();
+  } else {
+    deferred.resolve();
+  }
+  if (goOffline) {
+    Services.prefs.setIntPref('network.proxy.type', 0);
+    // LOAD_FLAGS_BYPASS_CACHE isn't good enough. So clear the cache.
+    Services.cache2.clear();
+  }
+  return deferred.promise;
+}
+
 function goOffline() {
   // Simulate a network outage with offline mode. (Localhost is still
   // accessible in offline mode, so disable the test proxy as well.)
-  if (!Services.io.offline)
-    BrowserOffline.toggleOfflineStatus();
-  Services.prefs.setIntPref('network.proxy.type', 0);
-  // LOAD_FLAGS_BYPASS_CACHE isn't good enough. So clear the cache.
-  Services.cache2.clear();
+  return toggleOfflineStatus(true);
 }
 
 function goOnline(callback) {
-  Services.prefs.setIntPref('network.proxy.type', origProxyType);
-  if (Services.io.offline)
-    BrowserOffline.toggleOfflineStatus();
-  if (callback)
-    callback();
+  return toggleOfflineStatus(false);
 }
 
 function openPanel(url, panelCallback, loadCallback) {
   // open a flyout
   SocialFlyout.open(url, 0, panelCallback);
-  SocialFlyout.panel.firstChild.addEventListener("load", function panelLoad(evt) {
-    if (evt.target != SocialFlyout.panel.firstChild.contentDocument) {
-      return;
-    }
-    SocialFlyout.panel.firstChild.removeEventListener("load", panelLoad, true);
-    loadCallback();
-  }, true);
+  // wait for both open and loaded before callback. Since the test doesn't close
+  // the panel between opens, we cannot rely on events here. We need to ensure
+  // popupshown happens before we finish out the tests.
+  waitForCondition(function() {
+                    return SocialFlyout.panel.state == "open" &&
+                           SocialFlyout.iframe.contentDocument.readyState == "complete";
+                   },
+                   function () { executeSoon(loadCallback) },
+                   "flyout is open and loaded");
 }
 
 function openChat(url, panelCallback, loadCallback) {
   // open a chat window
   let chatbar = getChatBar();
   openChatWindow(null, SocialSidebar.provider, url, panelCallback);
   chatbar.firstChild.addEventListener("DOMContentLoaded", function panelLoad() {
     chatbar.firstChild.removeEventListener("DOMContentLoaded", panelLoad, true);
-    loadCallback();
+    executeSoon(loadCallback);
   }, true);
 }
 
 function onSidebarLoad(callback) {
   let sbrowser = document.getElementById("social-sidebar-browser");
   sbrowser.addEventListener("load", function load() {
     sbrowser.removeEventListener("load", load, true);
-    callback();
+    executeSoon(callback);
   }, true);
 }
 
-function ensureWorkerLoaded(provider, callback) {
-  // once the worker responds to a ping we know it must be up.
-  let port = provider.getWorkerPort();
-  port.onmessage = function(msg) {
-    if (msg.data.topic == "pong") {
-      port.close();
-      callback();
-    }
-  }
-  port.postMessage({topic: "ping"})
-}
-
 let manifest = { // normal provider
   name: "provider 1",
   origin: "https://example.com",
-  sidebarURL: "https://example.com/browser/browser/base/content/test/social/social_sidebar.html",
-  workerURL: "https://example.com/browser/browser/base/content/test/social/social_worker.js",
+  sidebarURL: "https://example.com/browser/browser/base/content/test/social/social_sidebar_empty.html",
   iconURL: "https://example.com/browser/browser/base/content/test/general/moz.png"
 };
 
 function test() {
   waitForExplicitFinish();
 
   runSocialTestWithProvider(manifest, function (finishcb) {
-    runSocialTests(tests, undefined, goOnline, finishcb);
+    runSocialTests(tests, undefined, function(next) { goOnline().then(next) }, finishcb);
   });
 }
 
 var tests = {
   testSidebar: function(next) {
     let sbrowser = document.getElementById("social-sidebar-browser");
     onSidebarLoad(function() {
-      ok(sbrowser.contentDocument.location.href.indexOf("about:socialerror?")==0, "is on social error page");
+      ok(sbrowser.contentDocument.location.href.indexOf("about:socialerror?")==0, "sidebar is on social error page");
       gc();
       // Add a new load listener, then find and click the "try again" button.
       onSidebarLoad(function() {
         // should still be on the error page.
-        ok(sbrowser.contentDocument.location.href.indexOf("about:socialerror?")==0, "is still on social error page");
+        ok(sbrowser.contentDocument.location.href.indexOf("about:socialerror?")==0, "sidebar is still on social error page");
         // go online and try again - this should work.
-        goOnline();
-        onSidebarLoad(function() {
-          // should now be on the correct page.
-          is(sbrowser.contentDocument.location.href, manifest.sidebarURL, "is now on social sidebar page");
-          next();
+        goOnline().then(function () {
+          onSidebarLoad(function() {
+            // should now be on the correct page.
+            is(sbrowser.contentDocument.location.href, manifest.sidebarURL, "sidebar is now on social sidebar page");
+            next();
+          });
+          sbrowser.contentDocument.getElementById("btnTryAgain").click();
         });
-        sbrowser.contentDocument.getElementById("btnTryAgain").click();
       });
       sbrowser.contentDocument.getElementById("btnTryAgain").click();
     });
-    // we want the worker to be fully loaded before going offline, otherwise
-    // it might fail due to going offline.
-    ensureWorkerLoaded(SocialSidebar.provider, function() {
-      // go offline then attempt to load the sidebar - it should fail.
-      goOffline();
+    // go offline then attempt to load the sidebar - it should fail.
+    goOffline().then(function() {
       SocialSidebar.show();
-  });
+    });
   },
 
   testFlyout: function(next) {
     let panelCallbackCount = 0;
     let panel = document.getElementById("social-flyout-panel");
-    // go offline and open a flyout.
-    goOffline();
-    openPanel(
-      "https://example.com/browser/browser/base/content/test/social/social_panel.html",
-      function() { // the panel api callback
-        panelCallbackCount++;
-      },
-      function() { // the "load" callback.
-        executeSoon(function() {
+    goOffline().then(function() {
+      openPanel(
+        manifest.sidebarURL, /* empty html page */
+        function() { // the panel api callback
+          panelCallbackCount++;
+        },
+        function() { // the "load" callback.
           todo_is(panelCallbackCount, 0, "Bug 833207 - should be no callback when error page loads.");
-          ok(panel.firstChild.contentDocument.location.href.indexOf("about:socialerror?")==0, "is on social error page");
+          let href = panel.firstChild.contentDocument.location.href;
+          ok(href.indexOf("about:socialerror?")==0, "flyout is on social error page");
           // Bug 832943 - the listeners previously stopped working after a GC, so
           // force a GC now and try again.
           gc();
           openPanel(
-            "https://example.com/browser/browser/base/content/test/social/social_panel.html",
+            manifest.sidebarURL, /* empty html page */
             function() { // the panel api callback
               panelCallbackCount++;
             },
             function() { // the "load" callback.
-              executeSoon(function() {
-                todo_is(panelCallbackCount, 0, "Bug 833207 - should be no callback when error page loads.");
-                ok(panel.firstChild.contentDocument.location.href.indexOf("about:socialerror?")==0, "is on social error page");
-                gc();
-                executeSoon(function() {
-                  SocialFlyout.unload();
-                  next();
-                });
-              });
+              todo_is(panelCallbackCount, 0, "Bug 833207 - should be no callback when error page loads.");
+              let href = panel.firstChild.contentDocument.location.href;
+              ok(href.indexOf("about:socialerror?")==0, "flyout is on social error page");
+              gc();
+              SocialFlyout.unload();
+              next();
             }
           );
-        });
-      }
-    );
+        }
+      );
+    });
   },
 
   testChatWindow: function(next) {
     let panelCallbackCount = 0;
-    // go offline and open a chat.
-    goOffline();
-    openChat(
-      "https://example.com/browser/browser/base/content/test/social/social_chat.html",
-      function() { // the panel api callback
-        panelCallbackCount++;
-      },
-      function() { // the "load" callback.
-        executeSoon(function() {
+    // chatwindow tests throw errors, which muddy test output, if the worker
+    // doesn't get test-init
+    goOffline().then(function() {
+      openChat(
+        manifest.sidebarURL, /* empty html page */
+        function() { // the panel api callback
+          panelCallbackCount++;
+        },
+        function() { // the "load" callback.
           todo_is(panelCallbackCount, 0, "Bug 833207 - should be no callback when error page loads.");
           let chat = getChatBar().selectedChat;
-          waitForCondition(function() chat.contentDocument.location.href.indexOf("about:socialerror?")==0,
+          waitForCondition(function() chat.content != null && chat.contentDocument.location.href.indexOf("about:socialerror?")==0,
                            function() {
                             chat.close();
                             next();
                             },
                            "error page didn't appear");
-        });
-      }
-    );
+        }
+      );
+    });
   },
 
   testChatWindowAfterTearOff: function(next) {
     // Ensure that the error listener survives the chat window being detached.
-    let url = "https://example.com/browser/browser/base/content/test/social/social_chat.html";
+    let url = manifest.sidebarURL; /* empty html page */
     let panelCallbackCount = 0;
+    // chatwindow tests throw errors, which muddy test output, if the worker
+    // doesn't get test-init
     // open a chat while we are still online.
     openChat(
       url,
       null,
       function() { // the "load" callback.
-        executeSoon(function() {
-          let chat = getChatBar().selectedChat;
-          is(chat.contentDocument.location.href, url, "correct url loaded");
-          // toggle to a detached window.
-          chat.swapWindows().then(
-            chat => {
+        let chat = getChatBar().selectedChat;
+        is(chat.contentDocument.location.href, url, "correct url loaded");
+        // toggle to a detached window.
+        chat.swapWindows().then(
+          chat => {
+            ok(!!chat.content, "we have chat content 1");
+            waitForCondition(function() chat.content != null && chat.contentDocument.readyState == "complete",
+                             function() {
               // now go offline and reload the chat - about:socialerror should be loaded.
-              goOffline();
-              chat.contentDocument.location.reload();
-              waitForCondition(function() chat.contentDocument.location.href.indexOf("about:socialerror?")==0,
-                               function() {
-                                chat.close();
-                                next();
-                                },
-                               "error page didn't appear");
-            }
-          );
-        });
+              goOffline().then(function() {
+                ok(!!chat.content, "we have chat content 2");
+                chat.contentDocument.location.reload();
+                info("chat reload called");
+                waitForCondition(function() chat.contentDocument.location.href.indexOf("about:socialerror?")==0,
+                                 function() {
+                                  chat.close();
+                                  next();
+                                  },
+                                 "error page didn't appear");
+              });
+            }, "swapped window loaded");
+          }
+        );
       }
     );
   }
 }
--- a/browser/base/content/test/social/head.js
+++ b/browser/base/content/test/social/head.js
@@ -28,16 +28,26 @@ function waitForCondition(condition, nex
     if (conditionPassed) {
       moveOn();
     }
     tries++;
   }, 100);
   var moveOn = function() { clearInterval(interval); nextTest(); };
 }
 
+
+function promiseObserverNotified(aTopic) {
+  let deferred = Promise.defer();
+  Services.obs.addObserver(function onNotification(aSubject, aTopic, aData) {
+    Services.obs.removeObserver(onNotification, aTopic);
+      deferred.resolve({subject: aSubject, data: aData});
+    }, aTopic, false);
+  return deferred.promise;
+}
+
 // Check that a specified (string) URL hasn't been "remembered" (ie, is not
 // in history, will not appear in about:newtab or auto-complete, etc.)
 function promiseSocialUrlNotRemembered(url) {
   let deferred = Promise.defer();
   let uri = Services.io.newURI(url, null, null);
   PlacesUtils.asyncHistory.isURIVisited(uri, function(aURI, aIsVisited) {
     ok(!aIsVisited, "social URL " + url + " should not be in global history");
     deferred.resolve();
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/social/share_activate.html
@@ -0,0 +1,36 @@
+<html>
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+<head>
+  <title>Activation test</title>
+</head>
+<script>
+
+var data = {
+  // currently required
+  "name": "Demo Social Service",
+  // browser_share.js serves this page from "https://example.com"
+  "origin": "https://example.com",
+  "iconURL": "chrome://branding/content/icon16.png",
+  "icon32URL": "chrome://branding/content/favicon32.png",
+  "icon64URL": "chrome://branding/content/icon64.png",
+  "workerURL": "/browser/browser/base/content/test/social/social_worker.js",
+  "shareURL": "/browser/browser/base/content/test/social/share.html"
+}
+
+function activate(node) {
+  node.setAttribute("data-service", JSON.stringify(data));
+  var event = new CustomEvent("ActivateSocialFeature");
+  node.dispatchEvent(event);
+}
+
+</script>
+<body>
+
+nothing to see here
+
+<button id="activation" onclick="activate(this, true)">Activate the share provider</button>
+
+</body>
+</html>
--- a/browser/base/jar.mn
+++ b/browser/base/jar.mn
@@ -63,16 +63,17 @@ browser.jar:
         content/browser/aboutaccounts/images/graphic_sync_intro@2x.png        (content/aboutaccounts/images/graphic_sync_intro@2x.png)
 
         content/browser/certerror/aboutCertError.xhtml     (content/aboutcerterror/aboutCertError.xhtml)
         content/browser/certerror/aboutCertError.css       (content/aboutcerterror/aboutCertError.css)
 
         content/browser/aboutRobots-icon.png          (content/aboutRobots-icon.png)
         content/browser/aboutRobots-widget-left.png   (content/aboutRobots-widget-left.png)
         content/browser/aboutSocialError.xhtml        (content/aboutSocialError.xhtml)
+        content/browser/aboutProviderDirectory.xhtml  (content/aboutProviderDirectory.xhtml)
         content/browser/aboutTabCrashed.js            (content/aboutTabCrashed.js)
         content/browser/aboutTabCrashed.xhtml         (content/aboutTabCrashed.xhtml)
 *       content/browser/browser.css                   (content/browser.css)
 *       content/browser/browser.js                    (content/browser.js)
 *       content/browser/browser.xul                   (content/browser.xul)
 *       content/browser/browser-tabPreviews.xml       (content/browser-tabPreviews.xml)
 *       content/browser/chatWindow.xul                (content/chatWindow.xul)
         content/browser/content.js                    (content/content.js)
--- a/browser/components/about/AboutRedirector.cpp
+++ b/browser/components/about/AboutRedirector.cpp
@@ -42,16 +42,19 @@ static RedirEntry kRedirMap[] = {
 #endif
   { "certerror", "chrome://browser/content/certerror/aboutCertError.xhtml",
     nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT |
     nsIAboutModule::ALLOW_SCRIPT |
     nsIAboutModule::HIDE_FROM_ABOUTABOUT },
   { "socialerror", "chrome://browser/content/aboutSocialError.xhtml",
     nsIAboutModule::ALLOW_SCRIPT |
     nsIAboutModule::HIDE_FROM_ABOUTABOUT },
+  { "providerdirectory", "chrome://browser/content/aboutProviderDirectory.xhtml",
+    nsIAboutModule::ALLOW_SCRIPT |
+    nsIAboutModule::HIDE_FROM_ABOUTABOUT },
   { "tabcrashed", "chrome://browser/content/aboutTabCrashed.xhtml",
     nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT |
     nsIAboutModule::ALLOW_SCRIPT |
     nsIAboutModule::HIDE_FROM_ABOUTABOUT },
   { "feeds", "chrome://browser/content/feeds/subscribe.xhtml",
     nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT |
     nsIAboutModule::ALLOW_SCRIPT |
     nsIAboutModule::HIDE_FROM_ABOUTABOUT },
--- a/browser/components/build/nsModule.cpp
+++ b/browser/components/build/nsModule.cpp
@@ -85,16 +85,17 @@ static const mozilla::Module::ContractID
     { NS_SHELLSERVICE_CONTRACTID, &kNS_SHELLSERVICE_CID },
 #endif
     { NS_FEEDSNIFFER_CONTRACTID, &kNS_FEEDSNIFFER_CID },
 #ifdef MOZ_SAFE_BROWSING
     { NS_ABOUT_MODULE_CONTRACTID_PREFIX "blocked", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
 #endif
     { NS_ABOUT_MODULE_CONTRACTID_PREFIX "certerror", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
     { NS_ABOUT_MODULE_CONTRACTID_PREFIX "socialerror", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
+    { NS_ABOUT_MODULE_CONTRACTID_PREFIX "providerdirectory", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
     { NS_ABOUT_MODULE_CONTRACTID_PREFIX "tabcrashed", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
     { NS_ABOUT_MODULE_CONTRACTID_PREFIX "feeds", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
     { NS_ABOUT_MODULE_CONTRACTID_PREFIX "privatebrowsing", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
     { NS_ABOUT_MODULE_CONTRACTID_PREFIX "rights", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
     { NS_ABOUT_MODULE_CONTRACTID_PREFIX "robots", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
     { NS_ABOUT_MODULE_CONTRACTID_PREFIX "sessionrestore", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
     { NS_ABOUT_MODULE_CONTRACTID_PREFIX "welcomeback", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
 #ifdef MOZ_SERVICES_SYNC
--- a/browser/components/customizableui/CustomizableUI.jsm
+++ b/browser/components/customizableui/CustomizableUI.jsm
@@ -202,17 +202,16 @@ let CustomizableUIInternal = {
       overflowable: true,
       defaultPlacements: [
         "urlbar-container",
         "search-container",
         "bookmarks-menu-button",
         "downloads-button",
         "home-button",
         "loop-call-button",
-        "social-share-button",
       ],
       defaultCollapsed: false,
     }, true);
 #ifndef XP_MACOSX
     this.registerArea(CustomizableUI.AREA_MENUBAR, {
       legacy: true,
       type: CustomizableUI.TYPE_TOOLBAR,
       defaultPlacements: [
--- a/browser/components/customizableui/CustomizableWidgets.jsm
+++ b/browser/components/customizableui/CustomizableWidgets.jsm
@@ -389,16 +389,34 @@ const CustomizableWidgets = [
 
       fillSubviewFromMenuItems([...menu.children], doc.getElementById("PanelUI-sidebarItems"));
     },
     onViewHiding: function(aEvent) {
       let doc = aEvent.target.ownerDocument;
       clearSubview(doc.getElementById("PanelUI-sidebarItems"));
     }
   }, {
+    id: "social-share-button",
+    tooltiptext: "social-share-button.label",
+    label: "social-share-button.tooltiptext",
+    // custom build our button so we can attach to the share command
+    type: "custom",
+    onBuild: function(aDocument) {
+      let node = aDocument.createElementNS(kNSXUL, "toolbarbutton");
+      node.setAttribute("id", this.id);
+      node.classList.add("toolbarbutton-1");
+      node.classList.add("chromeclass-toolbar-additional");
+      node.setAttribute("label", CustomizableUI.getLocalizedProperty(this, "label"));
+      node.setAttribute("tooltiptext", CustomizableUI.getLocalizedProperty(this, "tooltiptext"));
+      node.setAttribute("removable", "true");
+      node.setAttribute("observes", "Social:PageShareOrMark");
+      node.setAttribute("command", "Social:SharePage");
+      return node;
+    }
+  }, {
     id: "add-ons-button",
     shortcutId: "key_openAddons",
     tooltiptext: "add-ons-button.tooltiptext3",
     defaultArea: CustomizableUI.AREA_PANEL,
     onCommand: function(aEvent) {
       let win = aEvent.target &&
                 aEvent.target.ownerDocument &&
                 aEvent.target.ownerDocument.defaultView;
@@ -900,19 +918,18 @@ const CustomizableWidgets = [
     tooltiptext: "email-link-button.tooltiptext3",
     onCommand: function(aEvent) {
       let win = aEvent.view;
       win.MailIntegration.sendLinkForWindow(win.content);
     }
   }, {
     id: "loop-call-button",
     type: "custom",
-    // XXX Bug 1013989 will provide a label for the button
-    label: "loop-call-button.label",
-    tooltiptext: "loop-call-button.tooltiptext",
+    label: "loop-call-button2.label",
+    tooltiptext: "loop-call-button2.tooltiptext",
     defaultArea: CustomizableUI.AREA_NAVBAR,
     introducedInVersion: 1,
     onBuild: function(aDocument) {
       let node = aDocument.createElementNS(kNSXUL, "toolbarbutton");
       node.setAttribute("id", this.id);
       node.classList.add("toolbarbutton-1");
       node.classList.add("chromeclass-toolbar-additional");
       node.classList.add("badged-button");
--- a/browser/components/loop/GoogleImporter.jsm
+++ b/browser/components/loop/GoogleImporter.jsm
@@ -217,17 +217,17 @@ this.GoogleImporter.prototype = {
     // The following loops runs as long as the OAuth windows' titlebar doesn't
     // yield a response from the Google service. If an error occurs, the loop
     // will terminate early.
     while (!code) {
       if (!gAuthWindow || gAuthWindow.closed) {
         throw new Error("Popup window was closed before authentication succeeded");
       }
 
-      let matches = gAuthWindow.document.title.match(/(error|code)=(.*)$/);
+      let matches = gAuthWindow.document.title.match(/(error|code)=([^\s]+)/);
       if (matches && matches.length) {
         let [, type, message] = matches;
         gAuthWindow.close();
         gAuthWindow = null;
         if (type == "error") {
           throw new Error("Google authentication failed with error: " + message.trim());
         } else if (type == "code") {
           code = message.trim();
--- a/browser/components/loop/LoopStorage.jsm
+++ b/browser/components/loop/LoopStorage.jsm
@@ -20,17 +20,19 @@ Cu.import("resource://gre/modules/Servic
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 XPCOMUtils.defineLazyGetter(this, "eventEmitter", function() {
   const {EventEmitter} = Cu.import("resource://gre/modules/devtools/event-emitter.js", {});
   return new EventEmitter();
 });
 
 this.EXPORTED_SYMBOLS = ["LoopStorage"];
 
-const kDatabaseName = "loop";
+const kDatabasePrefix = "loop-";
+const kDefaultDatabaseName = "default";
+let gDatabaseName = kDatabasePrefix + kDefaultDatabaseName;
 const kDatabaseVersion = 1;
 
 let gWaitForOpenCallbacks = new Set();
 let gDatabase = null;
 let gClosed = false;
 
 /**
  * Properly shut the database instance down. This is done on application shutdown.
@@ -78,26 +80,26 @@ const ensureDatabaseOpen = function(onOp
 
   let invokeCallbacks = err => {
     for (let callback of gWaitForOpenCallbacks) {
       callback(err, gDatabase);
     }
     gWaitForOpenCallbacks.clear();
   };
 
-  let openRequest = indexedDB.open(kDatabaseName, kDatabaseVersion);
+  let openRequest = indexedDB.open(gDatabaseName, kDatabaseVersion);
 
   openRequest.onblocked = function(event) {
     invokeCallbacks(new Error("Database cannot be upgraded cause in use: " + event.target.error));
   };
 
   openRequest.onerror = function(event) {
     // Try to delete the old database so that we can start this process over
     // next time.
-    indexedDB.deleteDatabase(kDatabaseName);
+    indexedDB.deleteDatabase(gDatabaseName);
     invokeCallbacks(new Error("Error while opening database: " + event.target.errorCode));
   };
 
   openRequest.onupgradeneeded = function(event) {
     let db = event.target.result;
     eventEmitter.emit("upgrade", db, event.oldVersion, kDatabaseVersion);
   };
 
@@ -105,16 +107,43 @@ const ensureDatabaseOpen = function(onOp
     gDatabase = event.target.result;
     invokeCallbacks();
     // Close the database instance properly on application shutdown.
     Services.obs.addObserver(closeDatabase, "quit-application", false);
   };
 };
 
 /**
+ * Switch to a database with a different name by closing the current connection
+ * and making sure that the next connection attempt will be made using the updated
+ * name.
+ *
+ * @param {String} name New name of the database to switch to.
+ */
+const switchDatabase = function(name) {
+  if (!name) {
+    name = kDefaultDatabaseName;
+  }
+  name = kDatabasePrefix + name;
+  if (name == gDatabaseName) {
+    // This is already the current database, so there's no need to switch.
+    return;
+  }
+
+  gDatabaseName = name;
+  if (gDatabase) {
+    try {
+      gDatabase.close();
+    } finally {
+      gDatabase = null;
+    }
+  }
+};
+
+/**
  * Start a transaction on the loop database and return it.
  *
  * @param {String}   store    Name of the object store to start a transaction on
  * @param {Function} callback Callback to be invoked once a database connection
  *                            is established and a transaction can be started.
  *                            It takes an Error object as first argument and the
  *                            transaction object as second argument.
  * @param {String}   mode     Mode of the transaction. May be 'readonly' or 'readwrite'
@@ -175,28 +204,46 @@ const getStore = function(store, callbac
  *
  * LoopStorage implements the EventEmitter interface by exposing two methods, `on`
  * and `off`, to subscribe to events.
  * At this point only the `upgrade` event will be emitted. This happens when the
  * database is loaded in memory and consumers will be able to change its structure.
  */
 this.LoopStorage = Object.freeze({
   /**
+   * @var {String} databaseName The name of the database that is currently active,
+   *                            WITHOUT the prefix
+   */
+  get databaseName() {
+    return gDatabaseName.substr(kDatabasePrefix.length);
+  },
+
+  /**
    * Open a connection to the IndexedDB database and return the database object.
    *
    * @param {Function} callback Callback to be invoked once a database connection
    *                            is established. It takes an Error object as first
    *                            argument and the database connection object as
    *                            second argument, if successful.
    */
   getSingleton: function(callback) {
     ensureDatabaseOpen(callback);
   },
 
   /**
+   * Switch to a database with a different name.
+   *
+   * @param {String} name New name of the database to switch to. Defaults to
+   *                      `kDefaultDatabaseName`
+   */
+  switchDatabase: function(name = kDefaultDatabaseName) {
+    switchDatabase(name);
+  },
+
+  /**
    * Start a transaction on the loop database and return it.
    * If only two arguments are passed, the default mode will be assumed and the
    * second argument is assumed to be a callback.
    *
    * @param {String}   store    Name of the object store to start a transaction on
    * @param {Function} callback Callback to be invoked once a database connection
    *                            is established and a transaction can be started.
    *                            It takes an Error object as first argument and the
--- a/browser/components/loop/MozLoopService.jsm
+++ b/browser/components/loop/MozLoopService.jsm
@@ -51,16 +51,19 @@ XPCOMUtils.defineLazyModuleGetter(this, 
                                   "resource://gre/modules/FxAccountsProfileClient.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "HawkClient",
                                   "resource://services-common/hawkclient.js");
 
 XPCOMUtils.defineLazyModuleGetter(this, "deriveHawkCredentials",
                                   "resource://services-common/hawkrequest.js");
 
+XPCOMUtils.defineLazyModuleGetter(this, "LoopStorage",
+                                  "resource:///modules/loop/LoopStorage.jsm");
+
 XPCOMUtils.defineLazyModuleGetter(this, "MozLoopPushHandler",
                                   "resource:///modules/loop/MozLoopPushHandler.jsm");
 
 XPCOMUtils.defineLazyServiceGetter(this, "uuidgen",
                                    "@mozilla.org/uuid-generator;1",
                                    "nsIUUIDGenerator");
 
 XPCOMUtils.defineLazyServiceGetter(this, "gDNSService",
@@ -362,16 +365,18 @@ let MozLoopServiceInternal = {
    */
   set doNotDisturb(aFlag) {
     Services.prefs.setBoolPref("loop.do_not_disturb", Boolean(aFlag));
     this.notifyStatusChanged();
   },
 
   notifyStatusChanged: function(aReason = null) {
     log.debug("notifyStatusChanged with reason:", aReason);
+    let profile = MozLoopService.userProfile;
+    LoopStorage.switchDatabase(profile ? profile.uid : null);
     Services.obs.notifyObservers(null, "loop-status-changed", aReason);
   },
 
   /**
    * Record an error and notify interested UI with the relevant user-facing strings attached.
    *
    * @param {String} errorType a key to identify the type of error. Only one
    *                           error of a type will be saved at a time. This value may be used to
@@ -1343,17 +1348,17 @@ this.MozLoopService = {
    *
    * @param {key} The element id to get strings for.
    * @return {String} A JSON string containing the localized
    *                  attribute/value pairs for the element.
    */
   getStrings: function(key) {
       var stringData = MozLoopServiceInternal.localizedStrings;
       if (!(key in stringData)) {
-        Cu.reportError('No string for key: ' + key + 'found');
+        log.error("No string found for key: ", key);
         return "";
       }
 
       return JSON.stringify(stringData[key]);
   },
 
   /**
    * Returns a new GUID (UUID) in curly braces format.
--- a/browser/components/loop/content/js/contacts.js
+++ b/browser/components/loop/content/js/contacts.js
@@ -148,24 +148,24 @@ loop.contacts = (function(_, mozL10n) {
         this.setState({showMenu: false});
       }
     },
 
     componentWillUnmount: function() {
       document.body.removeEventListener("click", this._onBodyClick);
     },
 
-    componentShouldUpdate: function(nextProps, nextState) {
+    shouldComponentUpdate: function(nextProps, nextState) {
       let currContact = this.props.contact;
       let nextContact = nextProps.contact;
       return (
         currContact.name[0] !== nextContact.name[0] ||
         currContact.blocked !== nextContact.blocked ||
-        getPreferredEmail(currContact).value !==
-          getPreferredEmail(nextContact).value
+        getPreferredEmail(currContact).value !== getPreferredEmail(nextContact).value ||
+        nextState.showMenu !== this.state.showMenu
       );
     },
 
     handleAction: function(actionName) {
       if (this.props.handleContactAction) {
         this.props.handleContactAction(this.props.contact, actionName);
       }
     },
@@ -210,45 +210,78 @@ loop.contacts = (function(_, mozL10n) {
         )
       );
     }
   });
 
   const ContactsList = React.createClass({displayName: 'ContactsList',
     mixins: [React.addons.LinkedStateMixin],
 
+    /**
+     * Contacts collection object
+     */
+    contacts: null,
+
+    /**
+     * User profile
+     */
+    _userProfile: null,
+
     getInitialState: function() {
       return {
-        contacts: {},
         importBusy: false,
         filter: "",
       };
     },
 
-    componentDidMount: function() {
+    refresh: function(callback = function() {}) {
       let contactsAPI = navigator.mozLoop.contacts;
 
+      this.handleContactRemoveAll();
+
       contactsAPI.getAll((err, contacts) => {
         if (err) {
-          throw err;
+          callback(err);
+          return;
         }
 
         // Add contacts already present in the DB. We do this in timed chunks to
         // circumvent blocking the main event loop.
         let addContactsInChunks = () => {
           contacts.splice(0, CONTACTS_CHUNK_SIZE).forEach(contact => {
             this.handleContactAddOrUpdate(contact, false);
           });
           if (contacts.length) {
             setTimeout(addContactsInChunks, 0);
+          } else {
+            callback();
           }
           this.forceUpdate();
         };
 
         addContactsInChunks(contacts);
+      });
+    },
+
+    componentWillMount: function() {
+      // Take the time to initialize class variables that are used outside
+      // `this.state`.
+      this.contacts = {};
+      this._userProfile = navigator.mozLoop.userProfile;
+    },
+
+    componentDidMount: function() {
+      window.addEventListener("LoopStatusChanged", this._onStatusChanged);
+
+      this.refresh(err => {
+        if (err) {
+          throw err;
+        }
+
+        let contactsAPI = navigator.mozLoop.contacts;
 
         // Listen for contact changes/ updates.
         contactsAPI.on("add", (eventName, contact) => {
           this.handleContactAddOrUpdate(contact);
         });
         contactsAPI.on("remove", (eventName, contact) => {
           this.handleContactRemove(contact);
         });
@@ -256,37 +289,55 @@ loop.contacts = (function(_, mozL10n) {
           this.handleContactRemoveAll();
         });
         contactsAPI.on("update", (eventName, contact) => {
           this.handleContactAddOrUpdate(contact);
         });
       });
     },
 
+    componentWillUnmount: function() {
+      window.removeEventListener("LoopStatusChanged", this._onStatusChanged);
+    },
+
+    _onStatusChanged: function() {
+      let profile = navigator.mozLoop.userProfile;
+      let currUid = this._userProfile ? this._userProfile.uid : null;
+      let newUid = profile ? profile.uid : null;
+      if (currUid != newUid) {
+        // On profile change (login, logout), reload all contacts.
+        this._userProfile = profile;
+        // The following will do a forceUpdate() for us.
+        this.refresh();
+      }
+    },
+
     handleContactAddOrUpdate: function(contact, render = true) {
-      let contacts = this.state.contacts;
+      let contacts = this.contacts;
       let guid = String(contact._guid);
       contacts[guid] = contact;
       if (render) {
         this.forceUpdate();
       }
     },
 
     handleContactRemove: function(contact) {
-      let contacts = this.state.contacts;
+      let contacts = this.contacts;
       let guid = String(contact._guid);
       if (!contacts[guid]) {
         return;
       }
       delete contacts[guid];
       this.forceUpdate();
     },
 
     handleContactRemoveAll: function() {
-      this.setState({contacts: {}});
+      // Do not allow any race conditions when removing all contacts.
+      this.contacts = {};
+      this.forceUpdate();
     },
 
     handleImportButtonClick: function() {
       this.setState({ importBusy: true });
       navigator.mozLoop.startImport({
         service: "google"
       }, (err, stats) => {
         this.setState({ importBusy: false });
@@ -359,21 +410,21 @@ loop.contacts = (function(_, mozL10n) {
     },
 
     render: function() {
       let viewForItem = item => {
         return ContactDetail({key: item._guid, contact: item, 
                               handleContactAction: this.handleContactAction})
       };
 
-      let shownContacts = _.groupBy(this.state.contacts, function(contact) {
+      let shownContacts = _.groupBy(this.contacts, function(contact) {
         return contact.blocked ? "blocked" : "available";
       });
 
-      let showFilter = Object.getOwnPropertyNames(this.state.contacts).length >=
+      let showFilter = Object.getOwnPropertyNames(this.contacts).length >=
                        MIN_CONTACTS_FOR_FILTERING;
       if (showFilter) {
         let filter = this.state.filter.trim().toLocaleLowerCase();
         if (filter) {
           let filterFn = contact => {
             return contact.name[0].toLocaleLowerCase().contains(filter) ||
                    getPreferredEmail(contact).value.toLocaleLowerCase().contains(filter);
           };
--- a/browser/components/loop/content/js/contacts.jsx
+++ b/browser/components/loop/content/js/contacts.jsx
@@ -148,24 +148,24 @@ loop.contacts = (function(_, mozL10n) {
         this.setState({showMenu: false});
       }
     },
 
     componentWillUnmount: function() {
       document.body.removeEventListener("click", this._onBodyClick);
     },
 
-    componentShouldUpdate: function(nextProps, nextState) {
+    shouldComponentUpdate: function(nextProps, nextState) {
       let currContact = this.props.contact;
       let nextContact = nextProps.contact;
       return (
         currContact.name[0] !== nextContact.name[0] ||
         currContact.blocked !== nextContact.blocked ||
-        getPreferredEmail(currContact).value !==
-          getPreferredEmail(nextContact).value
+        getPreferredEmail(currContact).value !== getPreferredEmail(nextContact).value ||
+        nextState.showMenu !== this.state.showMenu
       );
     },
 
     handleAction: function(actionName) {
       if (this.props.handleContactAction) {
         this.props.handleContactAction(this.props.contact, actionName);
       }
     },
@@ -210,45 +210,78 @@ loop.contacts = (function(_, mozL10n) {
         </li>
       );
     }
   });
 
   const ContactsList = React.createClass({
     mixins: [React.addons.LinkedStateMixin],
 
+    /**
+     * Contacts collection object
+     */
+    contacts: null,
+
+    /**
+     * User profile
+     */
+    _userProfile: null,
+
     getInitialState: function() {
       return {
-        contacts: {},
         importBusy: false,
         filter: "",
       };
     },
 
-    componentDidMount: function() {
+    refresh: function(callback = function() {}) {
       let contactsAPI = navigator.mozLoop.contacts;
 
+      this.handleContactRemoveAll();
+
       contactsAPI.getAll((err, contacts) => {
         if (err) {
-          throw err;
+          callback(err);
+          return;
         }
 
         // Add contacts already present in the DB. We do this in timed chunks to
         // circumvent blocking the main event loop.
         let addContactsInChunks = () => {
           contacts.splice(0, CONTACTS_CHUNK_SIZE).forEach(contact => {
             this.handleContactAddOrUpdate(contact, false);
           });
           if (contacts.length) {
             setTimeout(addContactsInChunks, 0);
+          } else {
+            callback();
           }
           this.forceUpdate();
         };
 
         addContactsInChunks(contacts);
+      });
+    },
+
+    componentWillMount: function() {
+      // Take the time to initialize class variables that are used outside
+      // `this.state`.
+      this.contacts = {};
+      this._userProfile = navigator.mozLoop.userProfile;
+    },
+
+    componentDidMount: function() {
+      window.addEventListener("LoopStatusChanged", this._onStatusChanged);
+
+      this.refresh(err => {
+        if (err) {
+          throw err;
+        }
+
+        let contactsAPI = navigator.mozLoop.contacts;
 
         // Listen for contact changes/ updates.
         contactsAPI.on("add", (eventName, contact) => {
           this.handleContactAddOrUpdate(contact);
         });
         contactsAPI.on("remove", (eventName, contact) => {
           this.handleContactRemove(contact);
         });
@@ -256,37 +289,55 @@ loop.contacts = (function(_, mozL10n) {
           this.handleContactRemoveAll();
         });
         contactsAPI.on("update", (eventName, contact) => {
           this.handleContactAddOrUpdate(contact);
         });
       });
     },
 
+    componentWillUnmount: function() {
+      window.removeEventListener("LoopStatusChanged", this._onStatusChanged);
+    },
+
+    _onStatusChanged: function() {
+      let profile = navigator.mozLoop.userProfile;
+      let currUid = this._userProfile ? this._userProfile.uid : null;
+      let newUid = profile ? profile.uid : null;
+      if (currUid != newUid) {
+        // On profile change (login, logout), reload all contacts.
+        this._userProfile = profile;
+        // The following will do a forceUpdate() for us.
+        this.refresh();
+      }
+    },
+
     handleContactAddOrUpdate: function(contact, render = true) {
-      let contacts = this.state.contacts;
+      let contacts = this.contacts;
       let guid = String(contact._guid);
       contacts[guid] = contact;
       if (render) {
         this.forceUpdate();
       }
     },
 
     handleContactRemove: function(contact) {
-      let contacts = this.state.contacts;
+      let contacts = this.contacts;
       let guid = String(contact._guid);
       if (!contacts[guid]) {
         return;
       }
       delete contacts[guid];
       this.forceUpdate();
     },
 
     handleContactRemoveAll: function() {
-      this.setState({contacts: {}});
+      // Do not allow any race conditions when removing all contacts.
+      this.contacts = {};
+      this.forceUpdate();
     },
 
     handleImportButtonClick: function() {
       this.setState({ importBusy: true });
       navigator.mozLoop.startImport({
         service: "google"
       }, (err, stats) => {
         this.setState({ importBusy: false });
@@ -359,21 +410,21 @@ loop.contacts = (function(_, mozL10n) {
     },
 
     render: function() {
       let viewForItem = item => {
         return <ContactDetail key={item._guid} contact={item}
                               handleContactAction={this.handleContactAction} />
       };
 
-      let shownContacts = _.groupBy(this.state.contacts, function(contact) {
+      let shownContacts = _.groupBy(this.contacts, function(contact) {
         return contact.blocked ? "blocked" : "available";
       });
 
-      let showFilter = Object.getOwnPropertyNames(this.state.contacts).length >=
+      let showFilter = Object.getOwnPropertyNames(this.contacts).length >=
                        MIN_CONTACTS_FOR_FILTERING;
       if (showFilter) {
         let filter = this.state.filter.trim().toLocaleLowerCase();
         if (filter) {
           let filterFn = contact => {
             return contact.name[0].toLocaleLowerCase().contains(filter) ||
                    getPreferredEmail(contact).value.toLocaleLowerCase().contains(filter);
           };
--- a/browser/components/loop/content/js/panel.js
+++ b/browser/components/loop/content/js/panel.js
@@ -163,17 +163,17 @@ loop.panel = (function(_, mozL10n) {
       return {seenToS: navigator.mozLoop.getLoopCharPref('seenToS')};
     },
 
     render: function() {
       if (this.state.seenToS == "unseen") {
         var terms_of_use_url = navigator.mozLoop.getLoopCharPref('legal.ToS_url');
         var privacy_notice_url = navigator.mozLoop.getLoopCharPref('legal.privacy_url');
         var tosHTML = __("legal_text_and_links3", {
-          "clientShortname": __("client_shortname_fallback"),
+          "clientShortname": __("clientShortname2"),
           "terms_of_use": React.renderComponentToStaticMarkup(
             React.DOM.a({href: terms_of_use_url, target: "_blank"}, 
               __("legal_text_tos")
             )
           ),
           "privacy_notice": React.renderComponentToStaticMarkup(
             React.DOM.a({href: privacy_notice_url, target: "_blank"}, 
               __("legal_text_privacy")
@@ -347,48 +347,62 @@ loop.panel = (function(_, mozL10n) {
       } else {
         try {
           var callUrl = new window.URL(callUrlData.callUrl);
           // XXX the current server vers does not implement the callToken field
           // but it exists in the API. This workaround should be removed in the future
           var token = callUrlData.callToken ||
                       callUrl.pathname.split('/').pop();
 
+          // Now that a new URL is available, indicate it has not been shared.
+          this.linkExfiltrated = false;
+
           this.setState({pending: false, copied: false,
                          callUrl: callUrl.href,
                          callUrlExpiry: callUrlData.expiresAt});
         } catch(e) {
           console.log(e);
           this.props.notifications.errorL10n("unable_retrieve_url");
           this.setState(this.getInitialState());
         }
       }
     },
 
     handleEmailButtonClick: function(event) {
       this.handleLinkExfiltration(event);
 
-      navigator.mozLoop.composeEmail(__("share_email_subject3"),
-        __("share_email_body3", { callUrl: this.state.callUrl }));
+      navigator.mozLoop.composeEmail(
+        __("share_email_subject4", { clientShortname: __("clientShortname2")}),
+        __("share_email_body4", { callUrl: this.state.callUrl,
+                                  clientShortname: __("clientShortname2"),
+                                  learnMoreUrl: navigator.mozLoop.getLoopCharPref("learnMoreUrl") }));
     },
 
     handleCopyButtonClick: function(event) {
       this.handleLinkExfiltration(event);
       // XXX the mozLoop object should be passed as a prop, to ease testing and
       //     using a fake implementation in UI components showcase.
       navigator.mozLoop.copyString(this.state.callUrl);
       this.setState({copied: true});
     },
 
+    linkExfiltrated: false,
+
     handleLinkExfiltration: function(event) {
-      try {
-        navigator.mozLoop.telemetryAdd("LOOP_CLIENT_CALL_URL_SHARED", true);
-      } catch (err) {
-        console.error("Error recording telemetry", err);
+      // Update the count of shared URLs only once per generated URL.
+      if (!this.linkExfiltrated) {
+        this.linkExfiltrated = true;
+        try {
+          navigator.mozLoop.telemetryAdd("LOOP_CLIENT_CALL_URL_SHARED", true);
+        } catch (err) {
+          console.error("Error recording telemetry", err);
+        }
       }
+
+      // Note URL expiration every time it is shared.
       if (this.state.callUrlExpiry) {
         navigator.mozLoop.noteCallUrlExpiry(this.state.callUrlExpiry);
       }
     },
 
     render: function() {
       // XXX setting elem value from a state (in the callUrl input)
       // makes it immutable ie read only but that is fine in our case.
@@ -620,17 +634,19 @@ loop.panel = (function(_, mozL10n) {
         });
       } else {
         this.props.notifications.remove(this.props.notifications.get("service-error"));
       }
     },
 
     _onStatusChanged: function() {
       var profile = navigator.mozLoop.userProfile;
-      if (profile != this.state.userProfile) {
+      var currUid = this.state.userProfile ? this.state.userProfile.uid : null;
+      var newUid = profile ? profile.uid : null;
+      if (currUid != newUid) {
         // On profile change (login, logout), switch back to the default tab.
         this.selectTab("call");
       }
       this.setState({userProfile: profile});
       this.updateServiceErrors();
     },
 
     /**
--- a/browser/components/loop/content/js/panel.jsx
+++ b/browser/components/loop/content/js/panel.jsx
@@ -163,17 +163,17 @@ loop.panel = (function(_, mozL10n) {
       return {seenToS: navigator.mozLoop.getLoopCharPref('seenToS')};
     },
 
     render: function() {
       if (this.state.seenToS == "unseen") {
         var terms_of_use_url = navigator.mozLoop.getLoopCharPref('legal.ToS_url');
         var privacy_notice_url = navigator.mozLoop.getLoopCharPref('legal.privacy_url');
         var tosHTML = __("legal_text_and_links3", {
-          "clientShortname": __("client_shortname_fallback"),
+          "clientShortname": __("clientShortname2"),
           "terms_of_use": React.renderComponentToStaticMarkup(
             <a href={terms_of_use_url} target="_blank">
               {__("legal_text_tos")}
             </a>
           ),
           "privacy_notice": React.renderComponentToStaticMarkup(
             <a href={privacy_notice_url} target="_blank">
               {__("legal_text_privacy")}
@@ -347,48 +347,62 @@ loop.panel = (function(_, mozL10n) {
       } else {
         try {
           var callUrl = new window.URL(callUrlData.callUrl);
           // XXX the current server vers does not implement the callToken field
           // but it exists in the API. This workaround should be removed in the future
           var token = callUrlData.callToken ||
                       callUrl.pathname.split('/').pop();
 
+          // Now that a new URL is available, indicate it has not been shared.
+          this.linkExfiltrated = false;
+
           this.setState({pending: false, copied: false,
                          callUrl: callUrl.href,
                          callUrlExpiry: callUrlData.expiresAt});
         } catch(e) {
           console.log(e);
           this.props.notifications.errorL10n("unable_retrieve_url");
           this.setState(this.getInitialState());
         }
       }
     },
 
     handleEmailButtonClick: function(event) {
       this.handleLinkExfiltration(event);
 
-      navigator.mozLoop.composeEmail(__("share_email_subject3"),
-        __("share_email_body3", { callUrl: this.state.callUrl }));
+      navigator.mozLoop.composeEmail(
+        __("share_email_subject4", { clientShortname: __("clientShortname2")}),
+        __("share_email_body4", { callUrl: this.state.callUrl,
+                                  clientShortname: __("clientShortname2"),
+                                  learnMoreUrl: navigator.mozLoop.getLoopCharPref("learnMoreUrl") }));
     },
 
     handleCopyButtonClick: function(event) {
       this.handleLinkExfiltration(event);
       // XXX the mozLoop object should be passed as a prop, to ease testing and
       //     using a fake implementation in UI components showcase.
       navigator.mozLoop.copyString(this.state.callUrl);
       this.setState({copied: true});
     },
 
+    linkExfiltrated: false,
+
     handleLinkExfiltration: function(event) {
-      try {
-        navigator.mozLoop.telemetryAdd("LOOP_CLIENT_CALL_URL_SHARED", true);
-      } catch (err) {
-        console.error("Error recording telemetry", err);
+      // Update the count of shared URLs only once per generated URL.
+      if (!this.linkExfiltrated) {
+        this.linkExfiltrated = true;
+        try {
+          navigator.mozLoop.telemetryAdd("LOOP_CLIENT_CALL_URL_SHARED", true);
+        } catch (err) {
+          console.error("Error recording telemetry", err);
+        }
       }
+
+      // Note URL expiration every time it is shared.
       if (this.state.callUrlExpiry) {
         navigator.mozLoop.noteCallUrlExpiry(this.state.callUrlExpiry);
       }
     },
 
     render: function() {
       // XXX setting elem value from a state (in the callUrl input)
       // makes it immutable ie read only but that is fine in our case.
@@ -620,17 +634,19 @@ loop.panel = (function(_, mozL10n) {
         });
       } else {
         this.props.notifications.remove(this.props.notifications.get("service-error"));
       }
     },
 
     _onStatusChanged: function() {
       var profile = navigator.mozLoop.userProfile;
-      if (profile != this.state.userProfile) {
+      var currUid = this.state.userProfile ? this.state.userProfile.uid : null;
+      var newUid = profile ? profile.uid : null;
+      if (currUid != newUid) {
         // On profile change (login, logout), switch back to the default tab.
         this.selectTab("call");
       }
       this.setState({userProfile: profile});
       this.updateServiceErrors();
     },
 
     /**
--- a/browser/components/loop/standalone/content/js/webapp.js
+++ b/browser/components/loop/standalone/content/js/webapp.js
@@ -113,17 +113,17 @@ loop.webapp = (function($, _, OT, mozL10
     }
   });
 
   var ConversationBranding = React.createClass({displayName: 'ConversationBranding',
     render: function() {
       return (
         React.DOM.h1({className: "standalone-header-title"}, 
           React.DOM.strong(null, mozL10n.get("brandShortname")), 
-          mozL10n.get("clientShortname")
+          mozL10n.get("clientShortname2")
         )
       );
     }
   });
 
   /**
    * The Firefox Marketplace exposes a web page that contains a postMesssage
    * based API that wraps a small set of functionality from the WebApps API
--- a/browser/components/loop/standalone/content/js/webapp.jsx
+++ b/browser/components/loop/standalone/content/js/webapp.jsx
@@ -113,17 +113,17 @@ loop.webapp = (function($, _, OT, mozL10
     }
   });
 
   var ConversationBranding = React.createClass({
     render: function() {
       return (
         <h1 className="standalone-header-title">
           <strong>{mozL10n.get("brandShortname")}</strong>
-          {mozL10n.get("clientShortname")}
+          {mozL10n.get("clientShortname2")}
         </h1>
       );
     }
   });
 
   /**
    * The Firefox Marketplace exposes a web page that contains a postMesssage
    * based API that wraps a small set of functionality from the WebApps API
--- a/browser/components/loop/standalone/content/l10n/loop.en-US.properties
+++ b/browser/components/loop/standalone/content/l10n/loop.en-US.properties
@@ -36,18 +36,25 @@ get_firefox_button=Get {{brandShortname}
 initiate_call_button_label2=Ready to start your conversation?
 initiate_audio_video_call_button2=Start
 initiate_audio_video_call_tooltip2=Start a video conversation
 initiate_audio_call_button2=Voice conversation
 initiate_call_cancel_button=Cancel
 legal_text_and_links=By using this product you agree to the {{terms_of_use_url}} and {{privacy_notice_url}}
 terms_of_use_link_text=Terms of use
 privacy_notice_link_text=Privacy notice
+invite_header_text=Invite someone to join you.
+
+## LOCALIZATION NOTE(brandShortname): This should not be localized and
+## should remain "Firefox" for all locales.
 brandShortname=Firefox
-clientShortname=WebRTC!
+## LOCALIZATION NOTE(clientShortname2): This should not be localized and
+## should remain "Firefox Hello" for all locales.
+clientShortname2=Firefox Hello
+
 ## LOCALIZATION NOTE (call_url_creation_date_label): Example output: (from May 26, 2014)
 call_url_creation_date_label=(from {{call_url_creation_date}})
 call_progress_connecting_description=Connecting…
 call_progress_ringing_description=Ringing…
 fxos_app_needed=Please install the {{fxosAppName}} app from the Firefox Marketplace.
 
 feedback_call_experience_heading2=How was your conversation?
 feedback_what_makes_you_sad=What makes you sad?
@@ -72,8 +79,31 @@ feedback_window_will_close_in2[other] = 
 
 ## LOCALIZATION_NOTE (feedback_rejoin_button): Displayed on the feedback form after
 ## a signed-in to signed-in user call.
 ## https://people.mozilla.org/~dhenein/labs/loop-mvp-spec/#feedback
 feedback_rejoin_button=Rejoin
 ## LOCALIZATION NOTE (feedback_report_user_button): Used to report a user in the case of
 ## an abusive user.
 feedback_report_user_button=Report User
+
+## LOCALIZATION_NOTE(first_time_experience.title): clientShortname will be
+## replaced by the brand name
+first_time_experience_title={{clientShortname}} — Join the conversation
+first_time_experience_button_label=Get Started
+
+help_label=Help
+tour_label=Tour
+
+rooms_default_room_name_template=Conversation {{conversationLabel}}
+rooms_leave_button_label=Leave
+rooms_list_copy_url_tooltip=Copy Link
+rooms_list_delete_tooltip=Delete conversation
+rooms_list_deleteConfirmation_label=Are you sure?
+rooms_name_this_room_label=Name this conversation
+rooms_new_room_button_label=Start a conversation
+rooms_only_occupant_label=You're the first one here.
+rooms_panel_title=Choose a conversation or start a new one
+rooms_room_full_label=There are already two people in this conversation.
+rooms_room_full_call_to_action_nonFx_label=Download {{brandShortname}} to start your own
+rooms_room_full_call_to_action_label=Learn more about {{clientShortname}} »
+rooms_room_joined_label=Someone has joined the conversation!
+rooms_room_join_label=Join the conversation
--- a/browser/components/loop/test/desktop-local/panel_test.js
+++ b/browser/components/loop/test/desktop-local/panel_test.js
@@ -389,19 +389,19 @@ describe("loop.panel", function() {
 
     describe("Rendering the component should generate a call URL", function() {
 
       beforeEach(function() {
         document.mozL10n.initialize({
           getStrings: function(key) {
             var text;
 
-            if (key === "share_email_subject3")
+            if (key === "share_email_subject4")
               text = "email-subject";
-            else if (key === "share_email_body3")
+            else if (key === "share_email_body4")
               text = "{{callUrl}}";
 
             return JSON.stringify({textContent: text});
           }
         });
       });
 
       it("should make a request to requestCallUrl", function() {
@@ -508,16 +508,18 @@ describe("loop.panel", function() {
           }));
           view.setState({
             pending: false,
             copied: false,
             callUrl: "http://example.com",
             callUrlExpiry: 6000
           });
 
+          // Multiple clicks should result in the URL being counted only once.
+          TestUtils.Simulate.click(view.getDOMNode().querySelector(".button-copy"));
           TestUtils.Simulate.click(view.getDOMNode().querySelector(".button-copy"));
 
           sinon.assert.calledOnce(navigator.mozLoop.telemetryAdd);
           sinon.assert.calledWith(navigator.mozLoop.telemetryAdd,
                                   "LOOP_CLIENT_CALL_URL_SHARED",
                                   true);
         });
 
@@ -549,16 +551,18 @@ describe("loop.panel", function() {
           }));
           view.setState({
             pending: false,
             copied: false,
             callUrl: "http://example.com",
             callUrlExpiry: 6000
           });
 
+          // Multiple clicks should result in the URL being counted only once.
+          TestUtils.Simulate.click(view.getDOMNode().querySelector(".button-email"));
           TestUtils.Simulate.click(view.getDOMNode().querySelector(".button-email"));
 
           sinon.assert.calledOnce(navigator.mozLoop.telemetryAdd);
           sinon.assert.calledWith(navigator.mozLoop.telemetryAdd,
                                   "LOOP_CLIENT_CALL_URL_SHARED",
                                   true);
         });
 
@@ -591,18 +595,20 @@ describe("loop.panel", function() {
           }));
           view.setState({
             pending: false,
             copied: false,
             callUrl: "http://example.com",
             callUrlExpiry: 6000
           });
 
+          // Multiple copies should result in the URL being counted only once.
           var urlField = view.getDOMNode().querySelector("input[type='url']");
           TestUtils.Simulate.copy(urlField);
+          TestUtils.Simulate.copy(urlField);
 
           sinon.assert.calledOnce(navigator.mozLoop.telemetryAdd);
           sinon.assert.calledWith(navigator.mozLoop.telemetryAdd,
                                   "LOOP_CLIENT_CALL_URL_SHARED",
                                   true);
         });
 
       it("should notify the user when the operation failed", function() {
--- a/browser/components/loop/test/mochitest/browser_LoopContacts.js
+++ b/browser/components/loop/test/mochitest/browser_LoopContacts.js
@@ -1,12 +1,17 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 const {LoopContacts} = Cu.import("resource:///modules/loop/LoopContacts.jsm", {});
+const {LoopStorage} = Cu.import("resource:///modules/loop/LoopStorage.jsm", {});
+
+XPCOMUtils.defineLazyServiceGetter(this, "uuidgen",
+                                   "@mozilla.org/uuid-generator;1",
+                                   "nsIUUIDGenerator");
 
 const kContacts = [{
   id: 1,
   name: ["Ally Avocado"],
   email: [{
     "pref": true,
     "type": ["work"],
     "value": "ally@mail.com"
@@ -395,8 +400,36 @@ add_task(function* () {
 // Test if the event emitter implementation doesn't leak and is working as expected.
 add_task(function* () {
   yield promiseLoadContacts();
 
   Assert.strictEqual(gExpectedAdds.length, 0, "No contact additions should be expected anymore");
   Assert.strictEqual(gExpectedRemovals.length, 0, "No contact removals should be expected anymore");
   Assert.strictEqual(gExpectedUpdates.length, 0, "No contact updates should be expected anymore");
 });
+
+// Test switching between different databases.
+add_task(function* () {
+  Assert.equal(LoopStorage.databaseName, "default", "First active partition should be the default");
+  yield promiseLoadContacts();
+
+  let uuid = uuidgen.generateUUID().toString().replace(/[{}]+/g, "");
+  LoopStorage.switchDatabase(uuid);
+  Assert.equal(LoopStorage.databaseName, uuid, "The active partition should have changed");
+
+  yield promiseLoadContacts();
+
+  let contacts = yield promiseLoadContacts();
+  for (let i = 0, l = contacts.length; i < l; ++i) {
+    compareContacts(contacts[i], kContacts[i]);
+  }
+
+  LoopStorage.switchDatabase();
+  Assert.equal(LoopStorage.databaseName, "default", "The active partition should have changed");
+
+  LoopContacts.getAll(function(err, contacts) {
+    Assert.equal(err, null, "There shouldn't be an error");
+
+    for (let i = 0, l = contacts.length; i < l; ++i) {
+      compareContacts(contacts[i], kContacts[i]);
+    }
+  });
+});
--- a/browser/components/nsBrowserGlue.js
+++ b/browser/components/nsBrowserGlue.js
@@ -735,23 +735,16 @@ BrowserGlue.prototype = {
     WebappManager.uninit();
 #ifdef NIGHTLY_BUILD
     if (Services.prefs.getBoolPref("dom.identity.enabled")) {
       SignInToWebsiteUX.uninit();
     }
 #endif
     webrtcUI.uninit();
     FormValidationHandler.uninit();
-
-    // XXX: Temporary hack to allow Loop FxA login after a restart to work.
-    // Remove this once bug 1071247 is deployed.
-    if (Services.prefs.getPrefType("loop.autologin-after-restart") != Ci.nsIPrefBranch.PREF_BOOL ||
-        !Services.prefs.getBoolPref("loop.autologin-after-restart")) {
-      Services.prefs.clearUserPref("loop.hawk-session-token.fxa");
-    }
   },
 
   // All initial windows have opened.
   _onWindowsRestored: function BG__onWindowsRestored() {
     // Show update notification, if needed.
     if (Services.prefs.prefHasUserValue("app.update.postupdate"))
       this._showUpdateNotification();
 
new file mode 100644
--- /dev/null
+++ b/browser/components/sessionstore/RevivableWindows.jsm
@@ -0,0 +1,45 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+this.EXPORTED_SYMBOLS = ["RevivableWindows"];
+
+// List of closed windows that we can revive when closing
+// windows in succession until the browser quits.
+let closedWindows = [];
+
+/**
+ * This module keeps track of closed windows that are revivable. On Windows
+ * and Linux we can revive windows before saving to disk - i.e. moving them
+ * from state._closedWindows[] to state.windows[] so that they're opened
+ * automatically on next startup. This feature lets us properly support
+ * closing windows in succession until the browser quits.
+ *
+ * The length of the list is not capped by max_undo_windows unlike
+ * state._closedWindows[].
+ */
+this.RevivableWindows = Object.freeze({
+  // Returns whether there are windows to revive.
+  get isEmpty() {
+    return closedWindows.length == 0;
+  },
+
+  // Add a window to the list.
+  add(winState) {
+#ifndef XP_MACOSX
+    closedWindows.push(winState);
+#endif
+  },
+
+  // Get the list of revivable windows.
+  get() {
+    return [...closedWindows];
+  },
+
+  // Clear the list of revivable windows.
+  clear() {
+    closedWindows.length = 0;
+  }
+});
--- a/browser/components/sessionstore/SessionSaver.jsm
+++ b/browser/components/sessionstore/SessionSaver.jsm
@@ -14,16 +14,18 @@ Cu.import("resource://gre/modules/Timer.
 Cu.import("resource://gre/modules/Services.jsm", this);
 Cu.import("resource://gre/modules/XPCOMUtils.jsm", this);
 Cu.import("resource://gre/modules/TelemetryStopwatch.jsm", this);
 
 XPCOMUtils.defineLazyModuleGetter(this, "console",
   "resource://gre/modules/devtools/Console.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "PrivacyFilter",
   "resource:///modules/sessionstore/PrivacyFilter.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "RevivableWindows",
+  "resource:///modules/sessionstore/RevivableWindows.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "SessionStore",
   "resource:///modules/sessionstore/SessionStore.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "SessionFile",
   "resource:///modules/sessionstore/SessionFile.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils",
   "resource://gre/modules/PrivateBrowsingUtils.jsm");
 
 // Minimal interval between two save operations (in milliseconds).
@@ -200,33 +202,34 @@ let SessionSaverInternal = {
 
     // Make sure that we keep the previous session if we started with a single
     // private window and no non-private windows have been opened, yet.
     if (state.deferredInitialState) {
       state.windows = state.deferredInitialState.windows || [];
       delete state.deferredInitialState;
     }
 
-#ifndef XP_MACOSX
-    // We want to restore closed windows that are marked with _shouldRestore.
-    // We're doing this here because we want to control this only when saving
-    // the file.
-    while (state._closedWindows.length) {
-      let i = state._closedWindows.length - 1;
+    // We want to revive closed windows that have been closed in succession
+    // without any user action in between closing those. This happens here in
+    // the SessionSaver because we only want to revive when saving to disk.
+    // On Mac OS X this list will always be empty.
+    let windowsToRevive = RevivableWindows.get();
+    state.windows.unshift(...windowsToRevive);
+    let revivedWindows = state._closedWindows.splice(0, windowsToRevive.length);
+#ifdef DEBUG
+    // Check that the windows to revive equal the windows
+    // that we removed from the list of closed windows.
+    let match = revivedWindows.every((win, idx) => {
+      return win == windowsToRevive[windowsToRevive.length - 1 - idx];
+    });
 
-      if (!state._closedWindows[i]._shouldRestore) {
-        // We only need to go until _shouldRestore
-        // is falsy since we're going in reverse.
-        break;
-      }
-
-      delete state._closedWindows[i]._shouldRestore;
-      state.windows.unshift(state._closedWindows.pop());
+    if (!match) {
+      throw new Error("SessionStore: revived windows didn't match closed windows");
     }
-#endif
+#endif DEBUG
 
     stopWatchFinish("COLLECT_DATA_MS", "COLLECT_DATA_LONGEST_OP_MS");
     return this._writeState(state);
   },
 
   /**
    * Saves the current session state. Collects data asynchronously and calls
    * _saveState() to collect data again (with a cache hit rate of hopefully
--- a/browser/components/sessionstore/SessionStore.jsm
+++ b/browser/components/sessionstore/SessionStore.jsm
@@ -102,16 +102,18 @@ XPCOMUtils.defineLazyModuleGetter(this, 
   "resource://gre/modules/devtools/Console.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "RecentWindow",
   "resource:///modules/RecentWindow.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "GlobalState",
   "resource:///modules/sessionstore/GlobalState.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "PrivacyFilter",
   "resource:///modules/sessionstore/PrivacyFilter.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "RevivableWindows",
+  "resource:///modules/sessionstore/RevivableWindows.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "RunState",
   "resource:///modules/sessionstore/RunState.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "ScratchpadManager",
   "resource:///modules/devtools/scratchpad-manager.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "SessionSaver",
   "resource:///modules/sessionstore/SessionSaver.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "SessionCookies",
   "resource:///modules/sessionstore/SessionCookies.jsm");
@@ -696,17 +698,19 @@ let SessionStoreInternal = {
         this.onTabHide(win, aEvent.originalTarget);
         break;
       case "TabPinned":
       case "TabUnpinned":
       case "SwapDocShells":
         this.saveStateDelayed(win);
         break;
     }
-    this._clearRestoringWindows();
+
+    // Any event handled here indicates a user action.
+    RevivableWindows.clear();
   },
 
   /**
    * Generate a unique window identifier
    * @return string
    *         A unique string to identify a window
    */
   _generateWindowID: function ssi_generateWindowID() {
@@ -1008,21 +1012,19 @@ let SessionStoreInternal = {
 
       if (isFullyLoaded) {
         winData.title = tabbrowser.selectedBrowser.contentTitle || tabbrowser.selectedTab.label;
         winData.title = this._replaceLoadingTitle(winData.title, tabbrowser,
                                                   tabbrowser.selectedTab);
         SessionCookies.update([winData]);
       }
 
-#ifndef XP_MACOSX
       // Until we decide otherwise elsewhere, this window is part of a series
       // of closing windows to quit.
-      winData._shouldRestore = true;
-#endif
+      RevivableWindows.add(winData);
 
       // Store the window's close date to figure out when each individual tab
       // was closed. This timestamp should allow re-arranging data based on how
       // recently something was closed.
       winData.closedAt = Date.now();
 
       // Save non-private windows if they have at
       // least one saveable tab or are the last window.
@@ -1034,19 +1036,18 @@ let SessionStoreInternal = {
         let hasSaveableTabs = winData.tabs.some(this._shouldSaveTabState);
 
         // When closing windows one after the other until Firefox quits, we
         // will move those closed in series back to the "open windows" bucket
         // before writing to disk. If however there is only a single window
         // with tabs we deem not worth saving then we might end up with a
         // random closed or even a pop-up window re-opened. To prevent that
         // we explicitly allow saving an "empty" window state.
-        let isLastWindow =
-          Object.keys(this._windows).length == 1 &&
-          !this._closedWindows.some(win => win._shouldRestore || false);
+        let numOpenWindows = Object.keys(this._windows).length;
+        let isLastWindow = numOpenWindows == 1 && RevivableWindows.isEmpty;
 
         if (hasSaveableTabs || isLastWindow) {
           // we don't want to save the busy state
           delete winData.busy;
 
           this._closedWindows.unshift(winData);
           this._capClosedWindows();
         }
@@ -1150,27 +1151,28 @@ let SessionStoreInternal = {
     // also clear all data about closed tabs and windows
     for (let ix in this._windows) {
       if (ix in openWindows) {
         this._windows[ix]._closedTabs = [];
       } else {
         delete this._windows[ix];
       }
     }
+
     // also clear all data about closed windows
     this._closedWindows = [];
+    RevivableWindows.clear();
+
     // give the tabbrowsers a chance to clear their histories first
     var win = this._getMostRecentBrowserWindow();
     if (win) {
       win.setTimeout(() => SessionSaver.run(), 0);
     } else if (RunState.isRunning) {
       SessionSaver.run();
     }
-
-    this._clearRestoringWindows();
   },
 
   /**
    * On purge of domain data
    * @param aData
    *        String domain data
    */
   onPurgeDomainData: function ssi_onPurgeDomainData(aData) {
@@ -1214,21 +1216,22 @@ let SessionStoreInternal = {
         // some duplication from restoreHistory - make sure we get the correct title
         let activeIndex = (selectedTab.index || selectedTab.entries.length) - 1;
         if (activeIndex >= selectedTab.entries.length)
           activeIndex = selectedTab.entries.length - 1;
         this._closedWindows[ix].title = selectedTab.entries[activeIndex].title;
       }
     }
 
+    // Purging domain data indicates a user action.
+    RevivableWindows.clear();
+
     if (RunState.isRunning) {
       SessionSaver.run();
     }
-
-    this._clearRestoringWindows();
   },
 
   /**
    * On preference change
    * @param aData
    *        String preference changed
    */
   onPrefChange: function ssi_onPrefChange(aData) {
@@ -3342,31 +3345,16 @@ let SessionStoreInternal = {
       normalWindowIndex++;
     if (normalWindowIndex >= this._max_windows_undo)
       spliceTo = normalWindowIndex + 1;
 #endif
     this._closedWindows.splice(spliceTo, this._closedWindows.length);
   },
 
   /**
-   * Clears the set of windows that are "resurrected" before writing to disk to
-   * make closing windows one after the other until shutdown work as expected.
-   *
-   * This function should only be called when we are sure that there has been
-   * a user action that indicates the browser is actively being used and all
-   * windows that have been closed before are not part of a series of closing
-   * windows.
-   */
-  _clearRestoringWindows: function ssi_clearRestoringWindows() {
-    for (let i = 0; i < this._closedWindows.length; i++) {
-      delete this._closedWindows[i]._shouldRestore;
-    }
-  },
-
-  /**
    * Reset state to prepare for a new session state to be restored.
    */
   _resetRestoringState: function ssi_initRestoringState() {
     TabRestoreQueue.reset();
     this._tabsRestoringCount = 0;
   },
 
   /**
--- a/browser/components/sessionstore/moz.build
+++ b/browser/components/sessionstore/moz.build
@@ -41,12 +41,13 @@ EXTRA_JS_MODULES.sessionstore = [
     'SessionWorker.jsm',
     'TabAttributes.jsm',
     'TabState.jsm',
     'TabStateCache.jsm',
     'Utils.jsm',
 ]
 
 EXTRA_PP_JS_MODULES.sessionstore += [
+    'RevivableWindows.jsm',
     'SessionSaver.jsm',
     'SessionStore.jsm',
 ]
 
--- a/browser/components/sessionstore/test/browser.ini
+++ b/browser/components/sessionstore/test/browser.ini
@@ -76,16 +76,17 @@ skip-if = buildapp == 'mulet'
 [browser_frametree.js]
 [browser_frame_history.js]
 [browser_global_store.js]
 [browser_history_cap.js]
 [browser_label_and_icon.js]
 [browser_merge_closed_tabs.js]
 [browser_pageStyle.js]
 [browser_privatetabs.js]
+[browser_revive_windows.js]
 [browser_scrollPositions.js]
 [browser_sessionHistory.js]
 skip-if = e10s
 [browser_sessionStorage.js]
 skip-if = e10s
 [browser_swapDocShells.js]
 skip-if = e10s # See bug 918634
 [browser_telemetry.js]
new file mode 100644
--- /dev/null
+++ b/browser/components/sessionstore/test/browser_revive_windows.js
@@ -0,0 +1,150 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const IS_MAC = ("nsILocalFileMac" in Ci);
+const URL_PREFIX = "about:mozilla?t=browser_revive_windows&r=";
+const PREF_MAX_UNDO = "browser.sessionstore.max_windows_undo";
+
+const URL_MAIN_WINDOW = URL_PREFIX + Math.random();
+const URL_ADD_WINDOW1 = URL_PREFIX + Math.random();
+const URL_ADD_WINDOW2 = URL_PREFIX + Math.random();
+const URL_CLOSED_WINDOW = URL_PREFIX + Math.random();
+
+add_task(function* setup() {
+  registerCleanupFunction(() => Services.prefs.clearUserPref(PREF_MAX_UNDO));
+});
+
+/**
+ * This test ensure that when closing windows in succession until the browser
+ * quits we are able to revive more windows than we keep around for the
+ * "Undo Close Window" feature.
+ */
+add_task(function* test_revive_windows() {
+  // We can restore a single window max.
+  Services.prefs.setIntPref(PREF_MAX_UNDO, 1);
+
+  // Clear list of closed windows.
+  forgetClosedWindows();
+
+  let windows = [];
+
+  // Create three windows.
+  for (let i = 0; i < 3; i++) {
+    let win = yield promiseNewWindow();
+    windows.push(win);
+
+    let tab = win.gBrowser.addTab("about:mozilla");
+    yield promiseBrowserLoaded(tab.linkedBrowser);
+  }
+
+  // Close all windows.
+  for (let win of windows) {
+    yield promiseWindowClosed(win);
+  }
+
+  is(ss.getClosedWindowCount(), 1, "one window restorable");
+
+  // Save to disk and read.
+  let state = JSON.parse(yield promiseRecoveryFileContents());
+
+  // Check number of windows.
+  if (IS_MAC) {
+    is(state.windows.length, 1, "one open window");
+    is(state._closedWindows.length, 1, "one closed window");
+  } else {
+    is(state.windows.length, 4, "four open windows");
+    is(state._closedWindows.length, 0, "closed windows");
+  }
+});
+
+/**
+ * This test ensures that when closing windows one after the other until the
+ * browser shuts down (on Windows and Linux) we revive closed windows in the
+ * right order.
+ */
+add_task(function* test_revive_windows_order() {
+  // We can restore up to three windows max.
+  Services.prefs.setIntPref(PREF_MAX_UNDO, 3);
+
+  // Clear list of closed windows.
+  forgetClosedWindows();
+
+  let tab = gBrowser.addTab(URL_MAIN_WINDOW);
+  yield promiseBrowserLoaded(tab.linkedBrowser);
+  TabState.flush(tab.linkedBrowser);
+  registerCleanupFunction(() => gBrowser.removeTab(tab));
+
+  let win0 = yield promiseNewWindow();
+  let tab0 = win0.gBrowser.addTab(URL_CLOSED_WINDOW);
+  yield promiseBrowserLoaded(tab0.linkedBrowser);
+
+  yield promiseWindowClosed(win0);
+  let data = ss.getClosedWindowData();
+  ok(data.contains(URL_CLOSED_WINDOW), "window is restorable");
+
+  let win1 = yield promiseNewWindow();
+  let tab1 = win1.gBrowser.addTab(URL_ADD_WINDOW1);
+  yield promiseBrowserLoaded(tab1.linkedBrowser);
+
+  let win2 = yield promiseNewWindow();
+  let tab2 = win2.gBrowser.addTab(URL_ADD_WINDOW2);
+  yield promiseBrowserLoaded(tab2.linkedBrowser);
+
+  // Close both windows so that |win1| will be opened first and would be
+  // behind |win2| that was closed later.
+  yield promiseWindowClosed(win1);
+  yield promiseWindowClosed(win2);
+
+  // Repeat the checks once.
+  for (let i = 0; i < 2; i++) {
+    info(`checking window data, iteration #${i}`);
+    let contents = yield promiseRecoveryFileContents();
+    let {windows, _closedWindows: closedWindows} = JSON.parse(contents);
+
+    if (IS_MAC) {
+      // Check number of windows.
+      is(windows.length, 1, "one open window");
+      is(closedWindows.length, 3, "three closed windows");
+
+      // Check open window.
+      ok(JSON.stringify(windows).contains(URL_MAIN_WINDOW),
+        "open window is correct");
+
+      // Check closed windows.
+      ok(JSON.stringify(closedWindows[0]).contains(URL_ADD_WINDOW2),
+        "correct first additional window");
+      ok(JSON.stringify(closedWindows[1]).contains(URL_ADD_WINDOW1),
+        "correct second additional window");
+      ok(JSON.stringify(closedWindows[2]).contains(URL_CLOSED_WINDOW),
+        "correct main window");
+    } else {
+      // Check number of windows.
+      is(windows.length, 3, "three open windows");
+      is(closedWindows.length, 1, "one closed window");
+
+      // Check closed window.
+      ok(JSON.stringify(closedWindows).contains(URL_CLOSED_WINDOW),
+        "closed window is correct");
+
+      // Check that windows are in the right order.
+      ok(JSON.stringify(windows[0]).contains(URL_ADD_WINDOW1),
+        "correct first additional window");
+      ok(JSON.stringify(windows[1]).contains(URL_ADD_WINDOW2),
+        "correct second additional window");
+      ok(JSON.stringify(windows[2]).contains(URL_MAIN_WINDOW),
+        "correct main window");
+    }
+  }
+});
+
+function promiseNewWindow() {
+  return new Promise(resolve => whenNewWindowLoaded({private: false}, resolve));
+}
+
+function forgetClosedWindows() {
+  while (ss.getClosedWindowCount()) {
+    ss.forgetClosedWindow(0);
+  }
+}
--- a/browser/devtools/debugger/test/browser.ini
+++ b/browser/devtools/debugger/test/browser.ini
@@ -134,17 +134,16 @@ skip-if = os == "mac" || e10s # Bug 8954
 [browser_dbg_breakpoints-contextmenu.js]
 [browser_dbg_breakpoints-disabled-reload.js]
 [browser_dbg_breakpoints-editor.js]
 [browser_dbg_breakpoints-highlight.js]
 [browser_dbg_breakpoints-new-script.js]
 [browser_dbg_breakpoints-other-tabs.js]
 [browser_dbg_breakpoints-pane.js]
 [browser_dbg_breakpoints-reload.js]
-skip-if = (os == "linux") && debug # Bug 1076830
 [browser_dbg_chrome-create.js]
 [browser_dbg_chrome-debugging.js]
 [browser_dbg_clean-exit-window.js]
 skip-if = true # Bug 933950 (leaky test)
 [browser_dbg_clean-exit.js]
 [browser_dbg_closure-inspection.js]
 [browser_dbg_cmd-blackbox.js]
 [browser_dbg_cmd-break.js]
--- a/browser/devtools/debugger/test/doc_breakpoints-reload.html
+++ b/browser/devtools/debugger/test/doc_breakpoints-reload.html
@@ -1,12 +1,13 @@
 <!-- Any copyright is dedicated to the Public Domain.
      http://creativecommons.org/publicdomain/zero/1.0/ -->
 <!DOCTYPE html>
 <head>
   <meta charset="utf-8"/>
   <title>Debugger Breakpoints Other Tabs Test Page</title>
 </head>
 <script>
-  (function () {
+  function theTest() {
     window.foo = "break on me";
-  }());
+  }
+  theTest();
 </script>
--- a/browser/extensions/pdfjs/content/PdfStreamConverter.jsm
+++ b/browser/extensions/pdfjs/content/PdfStreamConverter.jsm
@@ -72,17 +72,17 @@ function getChromeWindow(domWindow) {
 
 function getFindBar(domWindow) {
   if (PdfjsContentUtils.isRemote) {
     return PdfjsContentUtils.getFindBar(domWindow);
   }
   var browser = getContainingBrowser(domWindow);
   try {
     var tabbrowser = browser.getTabBrowser();
-    var tab = tabbrowser._getTabForBrowser(browser);
+    var tab = tabbrowser.getTabForBrowser(browser);
     return tabbrowser.getFindBar(tab);
   } catch (e) {
     // FF22 has no _getTabForBrowser, and FF24 has no getFindBar
     var chromeWindow = browser.ownerDocument.defaultView;
     return chromeWindow.gFindBar;
   }
 }
 
--- a/browser/extensions/pdfjs/content/PdfjsChromeUtils.jsm
+++ b/browser/extensions/pdfjs/content/PdfjsChromeUtils.jsm
@@ -280,17 +280,17 @@ let PdfjsChromeUtils = {
 /*
  * CPOW security features require chrome objects declare exposed
  * properties via __exposedProps__. We don't want to expose things
  * directly on the findbar, so we wrap the findbar in a smaller
  * object here that supports the features pdf.js needs.
  */
 function PdfjsFindbarWrapper(aBrowser) {
   let tabbrowser = aBrowser.getTabBrowser();
-  let tab = tabbrowser._getTabForBrowser(aBrowser);
+  let tab = tabbrowser.getTabForBrowser(aBrowser);
   this._findbar = tabbrowser.getFindBar(tab);
 };
 
 PdfjsFindbarWrapper.prototype = {
   __exposedProps__: {
     addEventListener: "r",
     removeEventListener: "r",
     updateControlState: "r",
--- a/browser/locales/en-US/chrome/browser/browser.dtd
+++ b/browser/locales/en-US/chrome/browser/browser.dtd
@@ -135,27 +135,32 @@ These should match what Safari and other
 <!ENTITY closeWindow.accesskey "d">
 
 <!ENTITY bookmarksMenu.label "Bookmarks">
 <!ENTITY bookmarksMenu.accesskey "B">
 <!ENTITY bookmarkThisPageCmd.label "Bookmark This Page">
 <!ENTITY editThisBookmarkCmd.label "Edit This Bookmark">
 <!ENTITY bookmarkThisPageCmd.commandkey "d">
 <!ENTITY markPageCmd.commandkey "l">
+<!-- LOCALIZATION NOTE (findShareServices.label):
+  -  Use the unicode ellipsis char, \u2026,
+  -  or use "..." if \u2026 doesn't suit traditions in your locale. -->
+<!ENTITY findShareServices.label "Find more Share services…">
 <!ENTITY sharePageCmd.label "Share This Page">
 <!ENTITY sharePageCmd.commandkey "S">
 <!ENTITY sharePageCmd.accesskey "s">
-<!ENTITY shareLinkCmd.label "Share This Link">
-<!ENTITY shareLinkCmd.accesskey "s">
-<!ENTITY shareImageCmd.label "Share This Image">
-<!ENTITY shareImageCmd.accesskey "s">
-<!ENTITY shareSelectCmd.label "Share Selection">
-<!ENTITY shareSelectCmd.accesskey "s">
-<!ENTITY shareVideoCmd.label "Share This Video">
-<!ENTITY shareVideoCmd.accesskey "s">
+<!-- LOCALIZATION NOTE (shareLink.accesskey): must be different than the following share access keys -->
+<!ENTITY shareLink.label "Share This Link">
+<!ENTITY shareLink.accesskey "h">
+<!ENTITY shareImage.label "Share This Image">
+<!ENTITY shareImage.accesskey "r">
+<!ENTITY shareSelect.label "Share Selection">
+<!ENTITY shareSelect.accesskey "r">
+<!ENTITY shareVideo.label "Share This Video">
+<!ENTITY shareVideo.accesskey "r">
 <!ENTITY feedsMenu.label "Subscribe">
 <!ENTITY subscribeToPageMenupopup.label "Subscribe to This Page">
 <!ENTITY subscribeToPageMenuitem.label "Subscribe to This Page…">
 <!ENTITY addCurPagesCmd.label "Bookmark All Tabs…">
 <!ENTITY showAllBookmarks2.label "Show All Bookmarks">
 <!ENTITY unsortedBookmarksCmd.label "Unsorted Bookmarks">
 <!ENTITY bookmarksToolbarChevron.tooltip "Show more bookmarks">
 
@@ -687,17 +692,21 @@ just addresses the organization to follo
 
 <!ENTITY social.activated.description "Services from <label/> have been enabled. You can change your settings for services in the <label class='text-link'>Add-on Manager</label>.">
 <!ENTITY social.activated.undo.label "Oops, undo this!">
 <!ENTITY social.activated.undo.accesskey "U">
 <!ENTITY social.learnMore.label "Learn more…">
 <!ENTITY social.learnMore.accesskey "l">
 <!ENTITY social.closeNotificationItem.label "Not Now">
 
-
+<!ENTITY social.directory.label "Activations Directory">
+<!ENTITY social.directory.text "You can activate Share services from the directory.">
+<!ENTITY social.directory.button "Take me there!">
+<!ENTITY social.directory.introText "Click on a service to add it to &brandShortName;.">
+<!ENTITY social.directory.viewmore.text "View More">
 
 <!ENTITY customizeMode.tabTitle "Customize &brandShortName;">
 <!ENTITY customizeMode.menuAndToolbars.header2 "Additional Tools and Features">
 <!ENTITY customizeMode.menuAndToolbars.empty "Want more tools?">
 <!ENTITY customizeMode.menuAndToolbars.emptyLink "Choose from thousands of add-ons">
 <!ENTITY customizeMode.restoreDefaults "Restore Defaults">
 <!ENTITY customizeMode.toolbars "Show / Hide Toolbars">
 <!ENTITY customizeMode.titlebar "Title Bar">
--- a/browser/locales/en-US/chrome/browser/customizableui/customizableWidgets.properties
+++ b/browser/locales/en-US/chrome/browser/customizableui/customizableWidgets.properties
@@ -92,16 +92,19 @@ email-link-button.tooltiptext3 = Email a
 
 # LOCALIZATION NOTE(quit-button.tooltiptext.linux2): %1$S is the brand name (e.g. Firefox),
 # %2$S is the keyboard shortcut
 quit-button.tooltiptext.linux2 = Quit %1$S (%2$S)
 # LOCALIZATION NOTE(quit-button.tooltiptext.mac): %1$S is the brand name (e.g. Firefox),
 # %2$S is the keyboard shortcut
 quit-button.tooltiptext.mac = Quit %1$S (%2$S)
 
-loop-call-button.label = Invite someone to talk
-loop-call-button.tooltiptext = Invite someone to talk
+loop-call-button2.label = Start a conversation
+loop-call-button2.tooltiptext = Start a conversation
+
+social-share-button.label = Share This Page
+social-share-button.tooltiptext = Share This Page
 
 panic-button.label = Forget
 panic-button.tooltiptext = Forget about some browsing history
 
 web-apps-button.label = Apps
 web-apps-button.tooltiptext = Discover Apps
--- a/browser/locales/en-US/chrome/browser/loop/loop.properties
+++ b/browser/locales/en-US/chrome/browser/loop/loop.properties
@@ -1,15 +1,25 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 # Panel Strings
 
+## LOCALIZATION NOTE(clientShortname2): This should not be localized and
+## should remain "Firefox Hello" for all locales.
+clientShortname2=Firefox Hello
+
+## LOCALIZATION_NOTE(first_time_experience.title): clientShortname will be
+## replaced by the brand name
+first_time_experience_title={{clientShortname}} — Join the conversation
+first_time_experience_button_label=Get Started
+
 share_link_header_text=Share this link to invite someone to talk:
+invite_header_text=Invite someone to join you.
 
 ## LOCALIZATION NOTE(invitee_name_label): Displayed when obtaining a url.
 ## See https://people.mozilla.org/~dhenein/labs/loop-mvp-spec/#precall-firstrun
 ## Click the label icon at the end of the url field.
 invitee_name_label=Who are you inviting?
 ## LOCALIZATION NOTE(invitee_expire_days_label): Allows the user to adjust
 ## the expiry time. Click the label icon at the end of the url field to see where
 ## this is:
@@ -47,22 +57,24 @@ login_expired=Your Login Has Expired
 service_not_available=Service Unavailable At This Time
 problem_accessing_account=There Was A Problem Accessing Your Account
 
 ## LOCALIZATION NOTE(retry_button): Displayed when there is an error to retry
 ## the appropriate action.
 ## See https://people.mozilla.org/~dhenein/labs/loop-mvp-spec/#error for location
 retry_button=Retry
 
-share_email_subject3=You have been invited to a conversation
-## LOCALIZATION NOTE (share_email_body3): In this item, don't translate the
+share_email_subject4={{clientShortname}} — Join the conversation
+## LOCALIZATION NOTE (share_email_body4): In this item, don't translate the
 ## part between {{..}} and leave the \r\n\r\n part alone
-share_email_body3=To accept this invitation, just copy or click this link to start your conversation:\r\n\r\n{{callUrl}}
+share_email_body4=Hello!\r\n\r\nJoin me for a video conversation using {{clientShortname}}:\r\n\r\nYou don't have to download or install anything. Just copy and paste this URL into your browser:\r\n\r\n{{callUrl}}\r\n\r\nIf you want, you can also learn more about {{clientShortname}} at {{learnMoreUrl}}\r\n\r\nTalk to you soon!
 share_button=Email
+share_button2=Email Link
 copy_url_button=Copy
+copy_url_button2=Copy Link
 copied_url_button=Copied!
 
 panel_footer_signin_or_signup_link=Sign In or Sign Up
 
 settings_menu_item_account=Account
 settings_menu_item_settings=Settings
 settings_menu_item_signout=Sign Out
 settings_menu_item_signin=Sign In
@@ -237,22 +249,27 @@ cancel_button=Cancel
 
 cannot_start_call_session_not_ready=Can't start call, session is not ready.
 network_disconnected=The network connection terminated abruptly.
 connection_error_see_console_notification=Call failed; see console for details.
 
 ## LOCALIZATION NOTE (legal_text_and_links3): In this item, don't translate the
 ## parts between {{..}} because these will be replaced with links with the labels
 ## from legal_text_tos and legal_text_privacy. clientShortname will be replaced
-## by the brand name, or fall back to client_shortname_fallback
+## by the brand name.
 legal_text_and_links3=By using {{clientShortname}} you agree to the {{terms_of_use}} \
   and {{privacy_notice}}.
 legal_text_tos = Terms of Use
 legal_text_privacy = Privacy Notice
-client_shortname_fallback=this product
+
+## LOCALIZATION NOTE (powered_by_beforeLogo, powered_by_afterLogo):
+## These 2 strings are displayed before and after a 'Telefonica'
+## logo.
+powered_by_beforeLogo=Powered by
+powered_by_afterLogo=
 
 feedback_call_experience_heading2=How was your conversation?
 feedback_what_makes_you_sad=What makes you sad?
 feedback_thank_you_heading=Thank you for your feedback!
 feedback_category_audio_quality=Audio quality
 feedback_category_video_quality=Video quality
 feedback_category_was_disconnected=Was disconnected
 feedback_category_confusing=Confusing
@@ -268,12 +285,31 @@ feedback_window_will_close_in2=This wind
 ## LOCALIZATION_NOTE (feedback_rejoin_button): Displayed on the feedback form after
 ## a signed-in to signed-in user call.
 ## https://people.mozilla.org/~dhenein/labs/loop-mvp-spec/#feedback
 feedback_rejoin_button=Rejoin
 ## LOCALIZATION NOTE (feedback_report_user_button): Used to report a user in the case of
 ## an abusive user.
 feedback_report_user_button=Report User
 
+help_label=Help
+tour_label=Tour
+
+## LOCALIZATION NOTE(rooms_default_room_name_template): {{conversationLabel}}
+## will be replaced by a number. For example "Conversation 1" or "Conversation 12".
+rooms_default_room_name_template=Conversation {{conversationLabel}}
+rooms_leave_button_label=Leave
+rooms_list_copy_url_tooltip=Copy Link
 ## LOCALIZATION NOTE (rooms_list_current_conversations): We prefer to have no
 ## number in the string, but if you need it for your language please use {{num}}.
 rooms_list_current_conversations=Current conversation;Current conversations
+rooms_list_delete_tooltip=Delete conversation
+rooms_list_deleteConfirmation_label=Are you sure?
 rooms_list_no_current_conversations=No current conversations
+rooms_name_this_room_label=Name this conversation
+rooms_new_room_button_label=Start a conversation
+rooms_only_occupant_label=You're the first one here.
+rooms_panel_title=Choose a conversation or start a new one
+rooms_room_full_label=There are already two people in this conversation.
+rooms_room_full_call_to_action_nonFx_label=Download {{brandShortname}} to start your own
+rooms_room_full_call_to_action_label=Learn more about {{clientShortname}} »
+rooms_room_joined_label=Someone has joined the conversation!
+rooms_room_join_label=Join the conversation
--- a/browser/modules/Social.jsm
+++ b/browser/modules/Social.jsm
@@ -7,18 +7,19 @@
 this.EXPORTED_SYMBOLS = ["Social", "CreateSocialStatusWidget",
                          "CreateSocialMarkWidget", "OpenGraphBuilder",
                          "DynamicResizeWatcher", "sizeSocialPanelToContent"];
 
 const Ci = Components.interfaces;
 const Cc = Components.classes;
 const Cu = Components.utils;
 
-// The minimum sizes for the auto-resize panel code.
-const PANEL_MIN_HEIGHT = 100;
+// The minimum sizes for the auto-resize panel code, minimum size necessary to
+// properly show the error page in the panel.
+const PANEL_MIN_HEIGHT = 200;
 const PANEL_MIN_WIDTH = 330;
 
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "CustomizableUI",
   "resource:///modules/CustomizableUI.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "SocialService",
@@ -308,16 +309,17 @@ function CreateSocialMarkWidget(aId, aPr
       node.setAttribute('type', "socialmark");
       node.style.listStyleImage = "url(" + (aProvider.unmarkedIcon || aProvider.icon32URL || aProvider.iconURL) + ")";
       node.setAttribute("origin", aProvider.origin);
 
       let window = aDocument.defaultView;
       let menuLabel = window.gNavigatorBundle.getFormattedString("social.markpageMenu.label", [aProvider.name]);
       node.setAttribute("label", menuLabel);
       node.setAttribute("tooltiptext", menuLabel);
+      node.setAttribute("observes", "Social:PageShareOrMark");
 
       return node;
     }
   });
 };
 
 // Error handling class used to listen for network errors in the social frames
 // and replace them with a social-specific error page
@@ -345,40 +347,48 @@ SocialErrorListener.prototype = {
   },
 
   onStateChange: function SPL_onStateChange(aWebProgress, aRequest, aState, aStatus) {
     let failure = false;
     if ((aState & Ci.nsIWebProgressListener.STATE_STOP)) {
       if (aRequest instanceof Ci.nsIHttpChannel) {
         try {
           // Change the frame to an error page on 4xx (client errors)
-          // and 5xx (server errors)
+          // and 5xx (server errors).  responseStatus throws if it is not set.
           failure = aRequest.responseStatus >= 400 &&
                     aRequest.responseStatus < 600;
-        } catch (e) {}
+        } catch (e) {
+          failure = aStatus == Components.results.NS_ERROR_CONNECTION_REFUSED;
+        }
       }
     }
 
     // Calling cancel() will raise some OnStateChange notifications by itself,
     // so avoid doing that more than once
     if (failure && aStatus != Components.results.NS_BINDING_ABORTED) {
       aRequest.cancel(Components.results.NS_BINDING_ABORTED);
-      let provider = Social._getProviderFromOrigin(this.iframe.getAttribute("origin"));
-      provider.errorState = "content-error";
+      let origin = this.iframe.getAttribute("origin");
+      if (origin) {
+        let provider = Social._getProviderFromOrigin(origin);
+        provider.errorState = "content-error";
+      }
       this.setErrorMessage(aWebProgress.QueryInterface(Ci.nsIDocShell)
                               .chromeEventHandler);
     }
   },
 
   onLocationChange: function SPL_onLocationChange(aWebProgress, aRequest, aLocation, aFlags) {
     if (aFlags & Ci.nsIWebProgressListener.LOCATION_CHANGE_ERROR_PAGE) {
       aRequest.cancel(Components.results.NS_BINDING_ABORTED);
-      let provider = Social._getProviderFromOrigin(this.iframe.getAttribute("origin"));
-      if (!provider.errorState)
-        provider.errorState = "content-error";
+      let origin = this.iframe.getAttribute("origin");
+      if (origin) {
+        let provider = Social._getProviderFromOrigin(origin);
+        if (!provider.errorState)
+          provider.errorState = "content-error";
+      }
       schedule(function() {
         this.setErrorMessage(aWebProgress.QueryInterface(Ci.nsIDocShell)
                               .chromeEventHandler);
       }.bind(this));
     }
   },
 
   onProgressChange: function SPL_onProgressChange() {},
--- a/browser/modules/webrtcUI.jsm
+++ b/browser/modules/webrtcUI.jsm
@@ -60,17 +60,17 @@ this.webrtcUI = {
              aScreen && state.screen;
     }).map(aStream => {
       let state = aStream.state;
       let types = {camera: state.camera, microphone: state.microphone,
                    screen: state.screen};
       let browser = aStream.browser;
       let browserWindow = browser.ownerDocument.defaultView;
       let tab = browserWindow.gBrowser &&
-                browserWindow.gBrowser._getTabForBrowser(browser);
+                browserWindow.gBrowser.getTabForBrowser(browser);
       return {uri: state.documentURI, tab: tab, browser: browser, types: types};
     });
   },
 
   showSharingDoorhanger: function(aActiveStream, aType) {
     let browserWindow = aActiveStream.browser.ownerDocument.defaultView;
     if (aActiveStream.tab) {
       browserWindow.gBrowser.selectedTab = aActiveStream.tab;
deleted file mode 100644
--- a/browser/themes/linux/aboutSocialError.css
+++ /dev/null
@@ -1,98 +0,0 @@
-body {
-  background-color: rgb(241, 244, 248);
-  margin-top: 2em;
-  font: message-box;
-  font-size: 100%;
-}
-
-p {
-  font-size: .8em;
-}
-
-#error-box {
-  background: url('chrome://global/skin/icons/information-24.png') no-repeat left 4px;
-  -moz-padding-start: 30px;
-}
-
-#error-box:-moz-locale-dir(rtl) {
-  background-position: right 4px;
-}
-
-#main-error-msg {
-  color: #4b4b4b;
-  font-weight: bold;
-}
-
-
-#button-box {
-  text-align: center;
-  width: 75%;
-  margin: 0 auto;
-}
-
-@media all and (min-width: 300px) {
-  #error-box {
-    max-width: 50%;
-    margin: 0 auto;
-    background-image: url('chrome://global/skin/icons/information-32.png');
-    min-height: 36px;
-    -moz-padding-start: 38px;
-  }
-
-  button {
-    width: auto !important;
-    min-width: 150px;
-  }
-}
-
-@media all and (min-width: 780px) {
-  #error-box {
-    max-width: 30%;
-  }
-}
-
-button {
-  font: message-box;
-  font-size: 0.6875em;
-  -moz-appearance: none;
-  -moz-user-select: none;
-  width: 100%;
-  margin: 2px 0;
-  padding: 2px 6px;
-  line-height: 1.2;
-  background-color: hsla(210,30%,95%,.1);
-  background-image: linear-gradient(hsla(0,0%,100%,.6), hsla(0,0%,100%,.1));
-  background-clip: padding-box;
-  border: 1px solid hsla(210,15%,25%,.4);
-  border-color: hsla(210,15%,25%,.3) hsla(210,15%,25%,.35) hsla(210,15%,25%,.4);
-  border-radius: 3px;
-  box-shadow: 0 1px 0 hsla(0,0%,100%,.3) inset,
-              0 0 0 1px hsla(0,0%,100%,.3) inset,
-              0 1px 0 hsla(0,0%,100%,.1);
-
-  transition-property: background-color, border-color, box-shadow;
-  transition-duration: 150ms;
-  transition-timing-function: ease;
-
-}
-
-button:hover {
-  background-color: hsla(210,30%,95%,.8);
-  border-color: hsla(210,15%,25%,.45) hsla(210,15%,25%,.5) hsla(210,15%,25%,.55);
-  box-shadow: 0 1px 0 hsla(0,0%,100%,.3) inset,
-              0 0 0 1px hsla(0,0%,100%,.3) inset,
-              0 1px 0 hsla(0,0%,100%,.1),
-              0 0 3px hsla(210,15%,25%,.1);
-  transition-property: background-color, border-color, box-shadow;
-  transition-duration: 150ms;
-  transition-timing-function: ease;
-}
-
-button:hover:active {
-  background-color: hsla(210,15%,25%,.2);
-  box-shadow: 0 1px 1px hsla(210,15%,25%,.2) inset,
-              0 0 2px hsla(210,15%,25%,.4) inset;
-  transition-property: background-color, border-color, box-shadow;
-  transition-duration: 10ms;
-  transition-timing-function: linear;
-}
new file mode 100644
--- /dev/null
+++ b/browser/themes/linux/devedition.css
@@ -0,0 +1,5 @@
+% This Source Code Form is subject to the terms of the Mozilla Public
+% License, v. 2.0. If a copy of the MPL was not distributed with this
+% file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+%include ../shared/devedition.inc.css
--- a/browser/themes/linux/jar.mn
+++ b/browser/themes/linux/jar.mn
@@ -10,23 +10,25 @@ browser.jar:
   skin/classic/browser/aboutSessionRestore-window-icon.png
   skin/classic/browser/aboutWelcomeBack.css                     (../shared/aboutWelcomeBack.css)
   skin/classic/browser/aboutCertError.css
   skin/classic/browser/aboutCertError_sectionCollapsed.png
   skin/classic/browser/aboutCertError_sectionCollapsed-rtl.png
   skin/classic/browser/aboutCertError_sectionExpanded.png
   skin/classic/browser/aboutNetError.css                        (../shared/aboutNetError.css)
   skin/classic/browser/aboutNetError_info.svg                   (../shared/aboutNetError_info.svg)
-  skin/classic/browser/aboutSocialError.css
+  skin/classic/browser/aboutSocialError.css                     (../shared/aboutSocialError.css)
+* skin/classic/browser/aboutProviderDirectory.css               (../shared/aboutProviderDirectory.css)
 #ifdef MOZ_SERVICES_SYNC
   skin/classic/browser/aboutSyncTabs.css
 #endif
   skin/classic/browser/aboutTabCrashed.css
   skin/classic/browser/actionicon-tab.png
 * skin/classic/browser/browser.css
+* skin/classic/browser/devedition.css
 * skin/classic/browser/browser-lightweightTheme.css
   skin/classic/browser/click-to-play-warning-stripes.png
 * skin/classic/browser/content-contextmenu.svg
 * skin/classic/browser/engineManager.css
   skin/classic/browser/fullscreen-darknoise.png
   skin/classic/browser/Geolocation-16.png
   skin/classic/browser/Geolocation-64.png
   skin/classic/browser/identity.png
--- a/browser/themes/osx/browser.css
+++ b/browser/themes/osx/browser.css
@@ -1363,16 +1363,21 @@ toolbar .toolbarbutton-1 > .toolbarbutto
 
   #PanelUI-fxa-status > .toolbarbutton-icon,
   #PanelUI-quit > .toolbarbutton-icon,
   #PanelUI-customize > .toolbarbutton-icon,
   #PanelUI-help > .toolbarbutton-icon {
     width: 16px;
   }
 
+  #add-share-provider {
+    list-style-image: url(chrome://browser/skin/menuPanel-small@2x.png);
+    -moz-image-region: rect(0px, 192px, 32px, 160px);
+  }
+
   #loop-call-button > .toolbarbutton-badge-container {
     list-style-image: url("chrome://browser/skin/loop/toolbar@2x.png");
     -moz-image-region: rect(0, 36px, 36px, 0);
   }
 
   toolbar[brighttext] #loop-call-button > .toolbarbutton-badge-container {
     list-style-image: url("chrome://browser/skin/loop/toolbar-inverted@2x.png");
   }
new file mode 100644
--- /dev/null
+++ b/browser/themes/osx/devedition.css
@@ -0,0 +1,5 @@
+% This Source Code Form is subject to the terms of the Mozilla Public
+% License, v. 2.0. If a copy of the MPL was not distributed with this
+% file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+%include ../shared/devedition.inc.css
--- a/browser/themes/osx/jar.mn
+++ b/browser/themes/osx/jar.mn
@@ -9,24 +9,26 @@ browser.jar:
   skin/classic/browser/aboutNetError_info.svg               (../shared/aboutNetError_info.svg)
 * skin/classic/browser/aboutSessionRestore.css              (aboutSessionRestore.css)
   skin/classic/browser/aboutSessionRestore-window-icon.png
   skin/classic/browser/aboutWelcomeBack.css                 (../shared/aboutWelcomeBack.css)
   skin/classic/browser/aboutCertError.css
   skin/classic/browser/aboutCertError_sectionCollapsed.png
   skin/classic/browser/aboutCertError_sectionCollapsed-rtl.png
   skin/classic/browser/aboutCertError_sectionExpanded.png
-  skin/classic/browser/aboutSocialError.css
+  skin/classic/browser/aboutSocialError.css                 (../shared/aboutSocialError.css)
+* skin/classic/browser/aboutProviderDirectory.css           (../shared/aboutProviderDirectory.css)
 #ifdef MOZ_SERVICES_SYNC
   skin/classic/browser/aboutSyncTabs.css
 #endif
   skin/classic/browser/aboutTabCrashed.css
   skin/classic/browser/actionicon-tab.png
   skin/classic/browser/actionicon-tab@2x.png
 * skin/classic/browser/browser.css                          (browser.css)
+* skin/classic/browser/devedition.css
 * skin/classic/browser/browser-lightweightTheme.css
   skin/classic/browser/click-to-play-warning-stripes.png
 * skin/classic/browser/content-contextmenu.svg
 * skin/classic/browser/engineManager.css                    (engineManager.css)
   skin/classic/browser/fullscreen-darknoise.png
   skin/classic/browser/Geolocation-16.png
   skin/classic/browser/Geolocation-16@2x.png
   skin/classic/browser/Geolocation-64.png
new file mode 100644
--- /dev/null
+++ b/browser/themes/shared/aboutProviderDirectory.css
@@ -0,0 +1,30 @@
+%include aboutSocialError.css
+
+body {
+  width: 310px;
+  margin: 1em auto;
+}
+
+#message-box {
+  margin-top: 2em;
+  background: url('chrome://global/skin/icons/information-24.png') no-repeat left 4px;
+  -moz-padding-start: 30px;
+}
+
+#activation-frame {
+  border: none;
+  margin: 0;
+  width: 310px;
+  height: 200px;
+}
+#activation > p {
+  width: 100%;
+  text-align: center;
+  margin: 0;
+  line-height: 2em;
+}
+.link {
+  text-decoration: none;
+  color: -moz-nativehyperlinktext;
+  cursor: pointer;
+}
rename from browser/themes/osx/aboutSocialError.css
rename to browser/themes/shared/aboutSocialError.css
new file mode 100644
--- /dev/null
+++ b/browser/themes/shared/devedition.inc.css
@@ -0,0 +1,3 @@
+% This Source Code Form is subject to the terms of the Mozilla Public
+% License, v. 2.0. If a copy of the MPL was not distributed with this
+% file, You can obtain one at http://mozilla.org/MPL/2.0/.
--- a/browser/themes/shared/menupanel.inc.css
+++ b/browser/themes/shared/menupanel.inc.css
@@ -229,8 +229,13 @@ toolbarpaletteitem[place="palette"] > #e
 toolbarpaletteitem[place="palette"] > #zoom-controls > #zoom-out-button {
   -moz-image-region: rect(0px, 80px, 16px, 64px);
 }
 
 #zoom-controls@inAnyPanel@ > #zoom-in-button,
 toolbarpaletteitem[place="palette"] > #zoom-controls > #zoom-in-button {
   -moz-image-region: rect(0px, 96px, 16px, 80px);
 }
+
+#add-share-provider {
+  list-style-image: url(chrome://browser/skin/menuPanel-small.png);
+  -moz-image-region: rect(0px, 96px, 16px, 80px);
+}
\ No newline at end of file
deleted file mode 100644
--- a/browser/themes/windows/aboutSocialError.css
+++ /dev/null
@@ -1,98 +0,0 @@
-body {
-  background-color: rgb(241, 244, 248);
-  margin-top: 2em;
-  font: message-box;
-  font-size: 100%;
-}
-
-p {
-  font-size: .8em;
-}
-
-#error-box {
-  background: url('chrome://global/skin/icons/information-24.png') no-repeat left 4px;
-  -moz-padding-start: 30px;
-}
-
-#error-box:-moz-locale-dir(rtl) {
-  background-position: right 4px;
-}
-
-#main-error-msg {
-  color: #4b4b4b;
-  font-weight: bold;
-}
-
-
-#button-box {
-  text-align: center;
-  width: 75%;
-  margin: 0 auto;
-}
-
-@media all and (min-width: 300px) {
-  #error-box {
-    max-width: 50%;
-    margin: 0 auto;
-    background-image: url('chrome://global/skin/icons/information-32.png');
-    min-height: 36px;
-    -moz-padding-start: 38px;
-  }
-
-  button {
-    width: auto !important;
-    min-width: 150px;
-  }
-}
-
-@media all and (min-width: 780px) {
-  #error-box {
-    max-width: 30%;
-  }
-}
-
-button {
-  font: message-box;
-  font-size: 0.6875em;
-  -moz-appearance: none;
-  -moz-user-select: none;
-  width: 100%;
-  margin: 2px 0;
-  padding: 2px 6px;
-  line-height: 1.2;
-  background-color: hsla(210,30%,95%,.1);
-  background-image: linear-gradient(hsla(0,0%,100%,.6), hsla(0,0%,100%,.1));
-  background-clip: padding-box;
-  border: 1px solid hsla(210,15%,25%,.4);
-  border-color: hsla(210,15%,25%,.3) hsla(210,15%,25%,.35) hsla(210,15%,25%,.4);
-  border-radius: 3px;
-  box-shadow: 0 1px 0 hsla(0,0%,100%,.3) inset,
-              0 0 0 1px hsla(0,0%,100%,.3) inset,
-              0 1px 0 hsla(0,0%,100%,.1);
-
-  transition-property: background-color, border-color, box-shadow;
-  transition-duration: 150ms;
-  transition-timing-function: ease;
-
-}
-
-button:hover {
-  background-color: hsla(210,30%,95%,.8);
-  border-color: hsla(210,15%,25%,.45) hsla(210,15%,25%,.5) hsla(210,15%,25%,.55);
-  box-shadow: 0 1px 0 hsla(0,0%,100%,.3) inset,
-              0 0 0 1px hsla(0,0%,100%,.3) inset,
-              0 1px 0 hsla(0,0%,100%,.1),
-              0 0 3px hsla(210,15%,25%,.1);
-  transition-property: background-color, border-color, box-shadow;
-  transition-duration: 150ms;
-  transition-timing-function: ease;
-}
-
-button:hover:active {
-  background-color: hsla(210,15%,25%,.2);
-  box-shadow: 0 1px 1px hsla(210,15%,25%,.2) inset,
-              0 0 2px hsla(210,15%,25%,.4) inset;
-  transition-property: background-color, border-color, box-shadow;
-  transition-duration: 10ms;
-  transition-timing-function: linear;
-}
new file mode 100644
--- /dev/null
+++ b/browser/themes/windows/devedition-aero.css
@@ -0,0 +1,7 @@
+% This Source Code Form is subject to the terms of the Mozilla Public
+% License, v. 2.0. If a copy of the MPL was not distributed with this
+% file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+%define WINDOWS_AERO
+%include devedition.css
+%undef WINDOWS_AERO
new file mode 100644
--- /dev/null
+++ b/browser/themes/windows/devedition.css
@@ -0,0 +1,5 @@
+% This Source Code Form is subject to the terms of the Mozilla Public
+% License, v. 2.0. If a copy of the MPL was not distributed with this
+% file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+%include ../shared/devedition.inc.css
--- a/browser/themes/windows/jar.mn
+++ b/browser/themes/windows/jar.mn
@@ -12,23 +12,25 @@ browser.jar:
         skin/classic/browser/aboutSessionRestore-window-icon.png     (preferences/application.png)
         skin/classic/browser/aboutWelcomeBack.css                    (../shared/aboutWelcomeBack.css)
         skin/classic/browser/aboutCertError.css
         skin/classic/browser/aboutCertError_sectionCollapsed.png
         skin/classic/browser/aboutCertError_sectionCollapsed-rtl.png
         skin/classic/browser/aboutCertError_sectionExpanded.png
         skin/classic/browser/aboutNetError.css                       (../shared/aboutNetError.css)
         skin/classic/browser/aboutNetError_info.svg                  (../shared/aboutNetError_info.svg)
-        skin/classic/browser/aboutSocialError.css
+        skin/classic/browser/aboutSocialError.css                    (../shared/aboutSocialError.css)
+*       skin/classic/browser/aboutProviderDirectory.css              (../shared/aboutProviderDirectory.css)
 #ifdef MOZ_SERVICES_SYNC
         skin/classic/browser/aboutSyncTabs.css
 #endif
         skin/classic/browser/aboutTabCrashed.css
         skin/classic/browser/actionicon-tab.png
 *       skin/classic/browser/browser.css
+*       skin/classic/browser/devedition.css
 *       skin/classic/browser/browser-lightweightTheme.css
         skin/classic/browser/click-to-play-warning-stripes.png
 *       skin/classic/browser/content-contextmenu.svg
 *       skin/classic/browser/engineManager.css
         skin/classic/browser/fullscreen-darknoise.png
         skin/classic/browser/Geolocation-16.png
         skin/classic/browser/Geolocation-64.png
         skin/classic/browser/Info.png
@@ -433,24 +435,26 @@ browser.jar:
 *       skin/classic/aero/browser/aboutSessionRestore.css            (aboutSessionRestore.css)
         skin/classic/aero/browser/aboutSessionRestore-window-icon.png (aboutSessionRestore-window-icon-aero.png)
         skin/classic/aero/browser/aboutCertError.css
         skin/classic/aero/browser/aboutCertError_sectionCollapsed.png
         skin/classic/aero/browser/aboutCertError_sectionCollapsed-rtl.png
         skin/classic/aero/browser/aboutCertError_sectionExpanded.png
         skin/classic/aero/browser/aboutNetError.css                   (../shared/aboutNetError.css)
         skin/classic/aero/browser/aboutNetError_info.svg              (../shared/aboutNetError_info.svg)
-        skin/classic/aero/browser/aboutSocialError.css
+        skin/classic/aero/browser/aboutSocialError.css                (../shared/aboutSocialError.css)
+*       skin/classic/aero/browser/aboutProviderDirectory.css          (../shared/aboutProviderDirectory.css)
 #ifdef MOZ_SERVICES_SYNC
         skin/classic/aero/browser/aboutSyncTabs.css
 #endif
         skin/classic/aero/browser/aboutTabCrashed.css
         skin/classic/aero/browser/aboutWelcomeBack.css               (../shared/aboutWelcomeBack.css)
         skin/classic/aero/browser/actionicon-tab.png
 *       skin/classic/aero/browser/browser.css                        (browser-aero.css)
+*       skin/classic/aero/browser/devedition.css                     (devedition-aero.css)
 *       skin/classic/aero/browser/browser-lightweightTheme.css
         skin/classic/aero/browser/click-to-play-warning-stripes.png
 *       skin/classic/aero/browser/content-contextmenu.svg
 *       skin/classic/aero/browser/engineManager.css
         skin/classic/aero/browser/fullscreen-darknoise.png
         skin/classic/aero/browser/Geolocation-16.png
         skin/classic/aero/browser/Geolocation-64.png
         skin/classic/aero/browser/Info.png                           (Info-aero.png)
--- a/dom/tests/mochitest/general/mochitest.ini
+++ b/dom/tests/mochitest/general/mochitest.ini
@@ -1,9 +1,10 @@
 [DEFAULT]
+skip-if = e10s
 support-files =
   497633.html
   file_MozEnteredDomFullscreen.html
   file_bug628069.html
   file_clonewrapper.html
   file_domWindowUtils_scrollbarSize.html
   file_frameElementWrapping.html
   file_interfaces.xml
@@ -29,62 +30,62 @@ support-files =
   res6.resource^headers^
   res7.resource
   res7.resource^headers^
   res8.resource
   res8.resource^headers^
   resource_timing.js
 
 [test_497898.html]
-skip-if = ((buildapp == 'mulet' || buildapp == 'b2g') && toolkit != 'gonk') || toolkit == 'android' #Bug 931116, b2g desktop specific, initial triage
+skip-if = (buildapp == 'b2g' && toolkit != 'gonk') || toolkit == 'android' #Bug 931116, b2g desktop specific, initial triage
 [test_bug504220.html]
 [test_bug628069_1.html]
 skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage
 [test_bug628069_2.html]
 [test_bug631440.html]
 skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage
 [test_bug653364.html]
 [test_bug861217.html]
 [test_clientRects.html]
 [test_clipboard_events.html]
-skip-if = e10s || buildapp == 'b2g' # b2g(clipboard undefined) b2g-debug(clipboard undefined) b2g-desktop(clipboard undefined)
+skip-if = buildapp == 'b2g' # b2g(clipboard undefined) b2g-debug(clipboard undefined) b2g-desktop(clipboard undefined)
 [test_consoleAPI.html]
 [test_DOMMatrix.html]
 [test_domWindowUtils.html]
 [test_domWindowUtils_scrollXY.html]
 [test_domWindowUtils_scrollbarSize.html]
 [test_donottrack.html]
 skip-if = buildapp == 'mulet'
 [test_focus_legend_noparent.html]
 [test_focusrings.xul]
-skip-if = e10s || buildapp == 'mulet' || buildapp == 'b2g' || toolkit == 'android' #TIMED_OUT
+skip-if = buildapp == 'mulet' || buildapp == 'b2g' || toolkit == 'android' #TIMED_OUT
 [test_for_of.html]
 [test_frameElementWrapping.html]
 [test_framedhistoryframes.html]
 [test_idleapi_permissions.html]
-skip-if = e10s || buildapp == 'b2g' || buildapp == 'mulet'
+skip-if = buildapp == 'b2g' || buildapp == 'mulet'
 [test_interfaces.html]
 skip-if = ((buildapp == 'mulet' || buildapp == 'b2g') && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage
 # [test_network_events.html]
 # Disable this test until bug 795711 is fixed.
 [test_offsets.html]
 [test_offsets.js]
 [test_outerHTML.html]
 [test_outerHTML.xhtml]
 skip-if = buildapp == 'mulet'
 [test_paste_selection.html]
 skip-if = buildapp == 'mulet'
 [test_picture_pref.html]
 [test_resource_timing.html]
-skip-if = buildapp == 'b2g' || buildapp == 'mulet'
+skip-if = buildapp == 'b2g' || buildapp == 'mulet' # b2g(No clipboard) b2g-debug(No clipboard) b2g-desktop(No clipboard)
 [test_resource_timing_cross_origin.html]
 skip-if = buildapp == 'b2g' || buildapp == 'mulet'
 [test_performance_now.html]
 [test_srcset_pref.html]
 [test_showModalDialog.html]
-skip-if = e10s || buildapp == 'mulet' || buildapp == 'b2g' || toolkit == 'android' #Don't run modal tests on Android # b2g(showmodaldialog) b2g-debug(showmodaldialog) b2g-desktop(showmodaldialog)
+skip-if = buildapp == 'mulet' || buildapp == 'b2g' || toolkit == 'android' #Don't run modal tests on Android # b2g(showmodaldialog) b2g-debug(showmodaldialog) b2g-desktop(showmodaldialog)
 [test_stylesheetPI.html]
 [test_vibrator.html]
 skip-if = buildapp == 'mulet' || toolkit == 'android' #CRASH_SUTAGENT
 [test_windowProperties.html]
 skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage
 [test_windowedhistoryframes.html]
 skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage
--- a/dom/tests/mochitest/general/test_resource_timing.html
+++ b/dom/tests/mochitest/general/test_resource_timing.html
@@ -15,24 +15,21 @@ https://bugzilla.mozilla.org/show_bug.cg
 <body>
 
 <pre id="test">
 <script type="application/javascript">
 
 SimpleTest.waitForExplicitFinish();
 
 // Resource timing is prefed off by default, so we had to use this workaround
-SpecialPowers.pushPrefEnv({"set": [["dom.enable_resource_timing", true]]}, start);
-var subwindow = null;
-
-function start() {
-  subwindow = window.open("resource_timing_main_test.html");
-}
+SpecialPowers.setBoolPref("dom.enable_resource_timing", true);
+var subwindow = window.open("resource_timing_main_test.html");
 
 function finishTests() {
   subwindow.close();
+  SpecialPowers.setBoolPref("dom.enable_resource_timing", false);
   SimpleTest.finish();
 }
 </script>
 </pre>
 
 </body>
 </html>
--- a/embedding/browser/nsDocShellTreeOwner.cpp
+++ b/embedding/browser/nsDocShellTreeOwner.cpp
@@ -1130,17 +1130,17 @@ NS_IMPL_ISUPPORTS(ChromeTooltipListener,
 // ChromeTooltipListener ctor
 //
 
 ChromeTooltipListener::ChromeTooltipListener(nsWebBrowser* inBrowser,
                                              nsIWebBrowserChrome* inChrome) 
   : mWebBrowser(inBrowser), mWebBrowserChrome(inChrome),
      mTooltipListenerInstalled(false),
      mMouseClientX(0), mMouseClientY(0),
-     mShowingTooltip(false)
+     mShowingTooltip(false), mTooltipShownOnce(false)
 {
   mTooltipTextProvider = do_GetService(NS_TOOLTIPTEXTPROVIDER_CONTRACTID);
   if (!mTooltipTextProvider) {
     nsISupports *pProvider = (nsISupports *) new DefaultTooltipTextProvider;
     mTooltipTextProvider = do_QueryInterface(pProvider);
   }
 } // ctor
 
@@ -1265,21 +1265,27 @@ ChromeTooltipListener::RemoveTooltipList
 
 NS_IMETHODIMP
 ChromeTooltipListener::HandleEvent(nsIDOMEvent* aEvent)
 {
   nsAutoString eventType;
   aEvent->GetType(eventType);
 
   if (eventType.EqualsLiteral("keydown") ||
-      eventType.EqualsLiteral("mousedown") ||
-      eventType.EqualsLiteral("mouseout"))
+      eventType.EqualsLiteral("mousedown")) {
     return HideTooltip();
-  if (eventType.EqualsLiteral("mousemove"))
+  }
+  else if (eventType.EqualsLiteral("mouseout")) {
+    // Reset flag so that tooltip will display on the next MouseMove
+    mTooltipShownOnce = false;
+    return HideTooltip();
+  }
+  else if (eventType.EqualsLiteral("mousemove")) {
     return MouseMove(aEvent);
+  }
 
   NS_ERROR("Unexpected event type");
   return NS_OK;
 }
 
 //
 // MouseMove
 //
@@ -1297,45 +1303,53 @@ ChromeTooltipListener::MouseMove(nsIDOME
   // timer callback. On win32, we'll get a MouseMove event even when a popup goes away --
   // even when the mouse doesn't change position! To get around this, we make sure the
   // mouse has really moved before proceeding.
   int32_t newMouseX, newMouseY;
   mouseEvent->GetClientX(&newMouseX);
   mouseEvent->GetClientY(&newMouseY);
   if ( mMouseClientX == newMouseX && mMouseClientY == newMouseY )
     return NS_OK;
+
+  // Filter out minor mouse movements.
+  if (mShowingTooltip &&
+      (abs(mMouseClientX - newMouseX) <= kTooltipMouseMoveTolerance) &&
+      (abs(mMouseClientY - newMouseY) <= kTooltipMouseMoveTolerance))
+    return NS_OK;
+
   mMouseClientX = newMouseX; mMouseClientY = newMouseY;
   mouseEvent->GetScreenX(&mMouseScreenX);
   mouseEvent->GetScreenY(&mMouseScreenY);
 
-  // We want to close the tip if it is being displayed and the mouse moves. Recall 
-  // that |mShowingTooltip| is set when the popup is showing. Furthermore, as the mouse
-  // moves, we want to make sure we reset the timer to show it, so that the delay
-  // is from when the mouse stops moving, not when it enters the element.
-  if ( mShowingTooltip )
-    return HideTooltip();
-  if ( mTooltipTimer )
+  if ( mTooltipTimer ) {
     mTooltipTimer->Cancel();
+  }
 
-  mTooltipTimer = do_CreateInstance("@mozilla.org/timer;1");
-  if ( mTooltipTimer ) {
-    nsCOMPtr<EventTarget> eventTarget = aMouseEvent->InternalDOMEvent()->GetTarget();
-    if ( eventTarget )
-      mPossibleTooltipNode = do_QueryInterface(eventTarget);
-    if ( mPossibleTooltipNode ) {
-      nsresult rv =
-        mTooltipTimer->InitWithFuncCallback(sTooltipCallback, this,
-          LookAndFeel::GetInt(LookAndFeel::eIntID_TooltipDelay, 500),
-          nsITimer::TYPE_ONE_SHOT);
-      if (NS_FAILED(rv))
-        mPossibleTooltipNode = nullptr;
+  if (!mShowingTooltip && !mTooltipShownOnce) {
+    mTooltipTimer = do_CreateInstance("@mozilla.org/timer;1");
+    if ( mTooltipTimer ) {
+      nsCOMPtr<EventTarget> eventTarget = aMouseEvent->InternalDOMEvent()->GetTarget();
+      if ( eventTarget )
+        mPossibleTooltipNode = do_QueryInterface(eventTarget);
+      if ( mPossibleTooltipNode ) {
+        nsresult rv =
+          mTooltipTimer->InitWithFuncCallback(sTooltipCallback, this,
+            LookAndFeel::GetInt(LookAndFeel::eIntID_TooltipDelay, 500),
+            nsITimer::TYPE_ONE_SHOT);
+        if (NS_FAILED(rv))
+          mPossibleTooltipNode = nullptr;
+      }
     }
+    else
+      NS_WARNING ( "Could not create a timer for tooltip tracking" );
   }
-  else
-    NS_WARNING ( "Could not create a timer for tooltip tracking" );
+  else {
+    mTooltipShownOnce = true;
+    return HideTooltip();
+  }
 
   return NS_OK;
 
 } // MouseMove
 
 
 //
 // ShowTooltip
@@ -1374,20 +1388,16 @@ ChromeTooltipListener::HideTooltip()
   
   // shut down the relevant timers
   if ( mTooltipTimer ) {
     mTooltipTimer->Cancel();
     mTooltipTimer = nullptr;
     // release tooltip target
     mPossibleTooltipNode = nullptr;
   }
-  if ( mAutoHideTimer ) {
-    mAutoHideTimer->Cancel();
-    mAutoHideTimer = nullptr;
-  }
 
   // if we're showing the tip, tell the chrome to hide it
   if ( mShowingTooltip ) {
     nsCOMPtr<nsITooltipListener> tooltipListener ( do_QueryInterface(mWebBrowserChrome) );
     if ( tooltipListener ) {
       rv = tooltipListener->OnHideTooltip ( );
       if ( NS_SUCCEEDED(rv) )
         mShowingTooltip = false;
@@ -1456,72 +1466,30 @@ ChromeTooltipListener::sTooltipCallback(
     if (self->mTooltipTextProvider) {
       bool textFound = false;
 
       self->mTooltipTextProvider->GetNodeText(
           self->mPossibleTooltipNode, getter_Copies(tooltipText), &textFound);
       
       if (textFound) {
         nsString tipText(tooltipText);
-        self->CreateAutoHideTimer();
         nsIntPoint screenDot = widget->WidgetToScreenOffset();
         self->ShowTooltip (self->mMouseScreenX - screenDot.x,
                            self->mMouseScreenY - screenDot.y,
                            tipText);
       }
     }
     
     // release tooltip target if there is one, NO MATTER WHAT
     self->mPossibleTooltipNode = nullptr;
   } // if "self" data valid
   
 } // sTooltipCallback
 
 
-//
-// CreateAutoHideTimer
-//
-// Create a new timer to see if we should auto-hide. It's ok if this fails.
-//
-void
-ChromeTooltipListener::CreateAutoHideTimer()
-{
-  // just to be anal (er, safe)
-  if ( mAutoHideTimer ) {
-    mAutoHideTimer->Cancel();
-    mAutoHideTimer = nullptr;
-  }
-  
-  mAutoHideTimer = do_CreateInstance("@mozilla.org/timer;1");
-  if ( mAutoHideTimer )
-    mAutoHideTimer->InitWithFuncCallback(sAutoHideCallback, this, kTooltipAutoHideTime, 
-                                         nsITimer::TYPE_ONE_SHOT);
-
-} // CreateAutoHideTimer
-
-
-//
-// sAutoHideCallback
-//
-// This fires after a tooltip has been open for a certain length of time. Just tell
-// the listener to close the popup. We don't have to worry, because HideTooltip() can
-// be called multiple times, even if the tip has already been closed.
-//
-void
-ChromeTooltipListener::sAutoHideCallback(nsITimer *aTimer, void* aListener)
-{
-  ChromeTooltipListener* self = static_cast<ChromeTooltipListener*>(aListener);
-  if ( self )
-    self->HideTooltip();
-
-  // NOTE: |aTimer| and |self->mAutoHideTimer| are invalid after calling ClosePopup();
-  
-} // sAutoHideCallback
-
-
 NS_IMPL_ISUPPORTS(ChromeContextMenuListener, nsIDOMEventListener)
 
 
 //
 // ChromeTooltipListener ctor
 //
 ChromeContextMenuListener::ChromeContextMenuListener(nsWebBrowser* inBrowser, nsIWebBrowserChrome* inChrome ) 
   : mContextMenuListenerInstalled(false),
--- a/embedding/browser/nsDocShellTreeOwner.h
+++ b/embedding/browser/nsDocShellTreeOwner.h
@@ -161,17 +161,18 @@ public:
     // the embedding chrome implements.
   NS_IMETHOD AddChromeListeners();
   NS_IMETHOD RemoveChromeListeners();
 
 private:
 
     // various delays for tooltips
   enum {
-    kTooltipAutoHideTime = 5000        // 5000ms = 5 seconds
+    kTooltipAutoHideTime = 5000,       // 5000ms = 5 seconds
+    kTooltipMouseMoveTolerance = 7     // 7 pixel tolerance for mousemove event
   };
 
   NS_IMETHOD AddTooltipListener();
   NS_IMETHOD RemoveTooltipListener();
 
   NS_IMETHOD ShowTooltip ( int32_t inXCoords, int32_t inYCoords, const nsAString & inTipText ) ;
   NS_IMETHOD HideTooltip ( ) ;
 
@@ -187,21 +188,17 @@ private:
 
   bool mTooltipListenerInstalled;
 
   nsCOMPtr<nsITimer> mTooltipTimer;
   static void sTooltipCallback ( nsITimer* aTimer, void* aListener ) ;
   int32_t mMouseClientX, mMouseClientY;       // mouse coordinates for last mousemove event we saw
   int32_t mMouseScreenX, mMouseScreenY;       // mouse coordinates for tooltip event
   bool mShowingTooltip;
-
-    // a timer for auto-hiding the tooltip after a certain delay
-  nsCOMPtr<nsITimer> mAutoHideTimer;
-  static void sAutoHideCallback ( nsITimer* aTimer, void* aListener ) ;
-  void CreateAutoHideTimer ( ) ;
+  bool mTooltipShownOnce;
 
     // The node hovered over that fired the timer. This may turn into the node that
     // triggered the tooltip, but only if the timer ever gets around to firing.
     // This is a strong reference, because the tooltip content can be destroyed while we're
     // waiting for the tooltip to pup up, and we need to detect that.
     // It's set only when the tooltip timer is created and launched. The timer must
     // either fire or be cancelled (or possibly released?), and we release this
     // reference in each of those cases. So we don't leak.
--- a/gfx/angle/AUTHORS
+++ b/gfx/angle/AUTHORS
@@ -9,16 +9,17 @@
 
 Google Inc.
 TransGaming Inc.
 3DLabs Inc. Ltd.
 
 Adobe Systems Inc.
 Autodesk, Inc.
 BlackBerry Limited
+Borbitsoft
 Cable Television Laboratories, Inc.
 Cloud Party, Inc.
 Intel Corporation
 Mozilla Corporation
 Turbulenz
 Klarälvdalens Datakonsult AB
 Microsoft Open Technologies, Inc.
 
--- a/gfx/angle/BUILD.gn
+++ b/gfx/angle/BUILD.gn
@@ -48,20 +48,19 @@ component("translator") {
   defines = [ "ANGLE_TRANSLATOR_IMPLEMENTATION" ]
 
   configs -= [ "//build/config/compiler:chromium_code" ]
   configs += [
     ":internal_config",
     "//build/config/compiler:no_chromium_code",
   ]
 
-  deps = [
+  public_deps = [
     ":translator_lib",
   ]
-  forward_dependent_configs_from = [ ":translator_lib" ]
 }
 
 # Holds the shared includes so we only need to list them once.
 source_set("includes") {
   sources = [
     "include/EGL/egl.h",
     "include/EGL/eglext.h",
     "include/EGL/eglplatform.h",
@@ -94,17 +93,17 @@ static_library("translator_lib") {
   sources = rebase_path(compiler_gypi.angle_translator_lib_sources, ".", "src")
 
   configs -= [ "//build/config/compiler:chromium_code" ]
   configs += [
     ":internal_config",
     ":translator_static_config",
     "//build/config/compiler:no_chromium_code",
   ]
-  direct_dependent_configs = [ ":external_config" ]
+  public_configs = [ ":external_config" ]
 
   deps = [
     ":includes",
     ":preprocessor",
   ]
 }
 
 static_library("translator_static") {
@@ -113,22 +112,21 @@ static_library("translator_static") {
     "src/compiler/translator/ShaderVars.cpp",
   ]
 
   configs -= [ "//build/config/compiler:chromium_code" ]
   configs += [
     ":internal_config",
     "//build/config/compiler:no_chromium_code",
   ]
-  direct_dependent_configs = [ ":translator_static_config" ]
+  public_configs = [ ":translator_static_config" ]
 
-  deps = [
+  public_deps = [
     ":translator_lib",
   ]
-  forward_dependent_configs_from = [ ":translator_lib" ]
 }
 
 config("commit_id_config") {
   include_dirs = [ "$root_gen_dir/angle" ]
 }
 
 action("commit_id") {
   script = "src/commit_id.py"
@@ -137,25 +135,30 @@ action("commit_id") {
   outputs = [ output_file ]
 
   args = [
     "gen",
     rebase_path(".", root_build_dir),
     rebase_path(output_file, root_build_dir),
   ]
 
-  direct_dependent_configs = [ ":commit_id_config" ]
+  public_configs = [ ":commit_id_config" ]
 }
 
 if (is_win) {
   angle_enable_d3d9 = true
   angle_enable_d3d11 = true
 
   shared_library("libGLESv2") {
-    sources = rebase_path(gles_gypi.angle_libglesv2_sources, ".", "src")
+    sources = rebase_path(gles_gypi.angle_libangle_sources, ".", "src")
+    sources += [
+      "src/libGLESv2/libGLESv2.cpp",
+      "src/libGLESv2/libGLESv2.def",
+      "src/libGLESv2/libGLESv2.rc",
+    ]
 
     defines = [
       "ANGLE_PRELOADED_D3DCOMPILER_MODULE_NAMES={ " +
         "\"d3dcompiler_46.dll\", \"d3dcompiler_43.dll\" }",
       "GL_APICALL=",
       "GL_GLEXT_PROTOTYPES=",
       "EGLAPI=",
     ]
--- a/gfx/angle/CONTRIBUTORS
+++ b/gfx/angle/CONTRIBUTORS
@@ -44,16 +44,19 @@ Google Inc.
 Adobe Systems Inc.
  Alexandru Chiculita
  Steve Minns
  Max Vujovic
 
 Autodesk, Inc.
  Ranger Harke
 
+Borbitsoft
+ Tibor den Ouden
+
 Cloud Party, Inc.
  Conor Dickinson
 
 Digia Plc
  Andrew Knight
 
 Intel Corporation
  Jin Yang
@@ -73,12 +76,13 @@ Mozilla Corp.
  Vladimir Vukicevic
 
 Turbulenz
  Michael Braithwaite
 
 Ulrik Persson (ddefrostt)
 Mark Banner (standard8mbp)
 David Kilzer
+Jacek Caban
 
 Microsoft Open Technologies, Inc.
 Cooper Partin
 Austin Kinross
--- a/gfx/angle/DEPS
+++ b/gfx/angle/DEPS
@@ -1,11 +1,11 @@
 deps = {
   "third_party/gyp":
-      "http://gyp.googlecode.com/svn/trunk@1806",
+      "http://gyp.googlecode.com/svn/trunk@1987",
 
   "tests/third_party/googletest":
       "http://googletest.googlecode.com/svn/trunk@629",
 
   "tests/third_party/googlemock":
       "http://googlemock.googlecode.com/svn/trunk@410",
 }
 
--- a/gfx/angle/moz.build
+++ b/gfx/angle/moz.build
@@ -1,12 +1,13 @@
 
 # Please note this file is autogenerated from generate_mozbuild.py, so do not modify it directly
 
 UNIFIED_SOURCES += [
+    'src/common/angleutils.cpp',
     'src/common/blocklayout.cpp',
     'src/common/debug.cpp',
     'src/common/event_tracer.cpp',
     'src/common/mathutil.cpp',
     'src/common/RefCountObject.cpp',
     'src/common/tls.cpp',
     'src/common/utilities.cpp',
     'src/compiler/preprocessor/DiagnosticsBase.cpp',
--- a/gfx/angle/src/angle.gypi
+++ b/gfx/angle/src/angle.gypi
@@ -21,16 +21,17 @@
         'libEGL.gypi'
     ],
 
     'targets':
     [
         {
             'target_name': 'copy_scripts',
             'type': 'none',
+            'hard_dependency': 1,
             'copies':
             [
                 {
                     'destination': '<(angle_gen_path)',
                     'files': [ 'copy_compiler_dll.bat', '<(angle_id_script_base)' ],
                 },
             ],
         },
@@ -41,55 +42,57 @@
         {
             'targets':
             [
                 {
                     'target_name': 'commit_id',
                     'type': 'none',
                     'includes': [ '../build/common_defines.gypi', ],
                     'dependencies': [ 'copy_scripts', ],
+                    'hard_dependency': 1,
                     'actions':
                     [
                         {
                             'action_name': 'Generate ANGLE Commit ID Header',
                             'message': 'Generating ANGLE Commit ID',
                             # reference the git index as an input, so we rebuild on changes to the index
                             'inputs': [ '<(angle_id_script)', '<(angle_path)/.git/index' ],
                             'outputs': [ '<(angle_id_header)' ],
                             'msvs_cygwin_shell': 0,
                             'action':
                             [
                                 'python', '<(angle_id_script)', 'gen', '<(angle_path)', '<(angle_id_header)'
                             ],
                         },
                     ],
-                    'direct_dependent_settings':
+                    'all_dependent_settings':
                     {
                         'include_dirs':
                         [
                             '<(angle_gen_path)',
                         ],
                     },
                 }
             ]
         },
         { # angle_use_commit_id==0
             'targets':
             [
                 {
                     'target_name': 'commit_id',
                     'type': 'none',
+                    'hard_dependency': 1,
                     'copies':
                     [
                         {
                             'destination': '<(angle_gen_path)/id',
                             'files': [ '<(angle_id_header_base)' ]
                         }
                     ],
-                    'direct_dependent_settings':
+                    'all_dependent_settings':
                     {
                         'include_dirs':
                         [
                             '<(angle_gen_path)',
                         ],
                     },
                 }
             ]
--- a/gfx/angle/src/commit.h
+++ b/gfx/angle/src/commit.h
@@ -1,3 +1,3 @@
-#define ANGLE_COMMIT_HASH "9a22db4058f8"
+#define ANGLE_COMMIT_HASH "fc329b4537cf"
 #define ANGLE_COMMIT_HASH_SIZE 12
-#define ANGLE_COMMIT_DATE "2014-09-19 12:49:11 -0700"
+#define ANGLE_COMMIT_DATE "2014-10-09 15:05:59 -0400"
new file mode 100644
--- /dev/null
+++ b/gfx/angle/src/common/NativeWindow.h
@@ -0,0 +1,53 @@
+//
+// Copyright (c) 2002-2014 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+// NativeWindow.h: Defines NativeWindow, a class for managing and
+// performing operations on an EGLNativeWindowType.
+// It is used for HWND (Desktop Windows) and IInspectable objects
+//(Windows Store Applications).
+
+#ifndef COMMON_NATIVEWINDOW_H_
+#define COMMON_NATIVEWINDOW_H_
+
+#include <EGL/eglplatform.h>
+#include "common/debug.h"
+#include "common/platform.h"
+
+// DXGISwapChain and DXGIFactory are typedef'd to specific required
+// types. The HWND NativeWindow implementation requires IDXGISwapChain
+// and IDXGIFactory and the Windows Store NativeWindow
+// implementation requires IDXGISwapChain1 and IDXGIFactory2.
+typedef IDXGISwapChain DXGISwapChain;
+typedef IDXGIFactory DXGIFactory;
+
+namespace rx
+{
+class NativeWindow
+{
+  public:
+    explicit NativeWindow(EGLNativeWindowType window);
+
+    // The HWND NativeWindow implementation can benefit
+    // by having inline versions of these methods to
+    // reduce the calling overhead.
+    inline bool initialize() { return true; }
+    inline bool getClientRect(LPRECT rect) { return GetClientRect(mWindow, rect) == TRUE; }
+    inline bool isIconic() { return IsIconic(mWindow) == TRUE; }
+
+    HRESULT createSwapChain(ID3D11Device* device, DXGIFactory* factory,
+                            DXGI_FORMAT format, UINT width, UINT height,
+                            DXGISwapChain** swapChain);
+
+    inline EGLNativeWindowType getNativeWindow() const { return mWindow; }
+
+  private:
+    EGLNativeWindowType mWindow;
+};
+}
+
+bool isValidEGLNativeWindowType(EGLNativeWindowType window);
+
+#endif // COMMON_NATIVEWINDOW_H_
--- a/gfx/angle/src/common/angleutils.cpp
+++ b/gfx/angle/src/common/angleutils.cpp
@@ -19,17 +19,17 @@ std::string FormatString(const char *fmt
         // Buffer was not large enough, calculate the required size and resize the buffer
         len = vsnprintf(NULL, 0, fmt, vararg);
         buffer.resize(len + 1);
 
         // Print again
         vsnprintf(&buffer[0], buffer.size(), fmt, vararg);
     }
 
-    return std::string(buffer.data(), len);
+    return std::string(&buffer[0], len);
 }
 
 std::string FormatString(const char *fmt, ...)
 {
     va_list vararg;
     va_start(vararg, fmt);
     std::string result = FormatString(fmt, vararg);
     va_end(vararg);
--- a/gfx/angle/src/common/debug.cpp
+++ b/gfx/angle/src/common/debug.cpp
@@ -82,28 +82,30 @@ bool perfActive()
     return active;
 #else
     return false;
 #endif
 }
 
 ScopedPerfEventHelper::ScopedPerfEventHelper(const char* format, ...)
 {
-#if defined(ANGLE_ENABLE_PERF)
 #if !defined(ANGLE_ENABLE_TRACE)
     if (!perfActive())
     {
         return;
     }
 #endif // !ANGLE_ENABLE_TRACE
     va_list vararg;
     va_start(vararg, format);
+#if defined(ANGLE_ENABLE_PERF)
     output(true, reinterpret_cast<PerfOutputFunction>(D3DPERF_BeginEvent), format, vararg);
+#else
+    output(true, NULL, format, vararg);
+#endif // ANGLE_ENABLE_PERF
     va_end(vararg);
-#endif // ANGLE_ENABLE_PERF
 }
 
 ScopedPerfEventHelper::~ScopedPerfEventHelper()
 {
 #if defined(ANGLE_ENABLE_PERF)
     if (perfActive())
     {
         D3DPERF_EndEvent();
--- a/gfx/angle/src/common/debug.h
+++ b/gfx/angle/src/common/debug.h
@@ -86,16 +86,20 @@ namespace gl
 #ifndef ANGLE_ENABLE_TRACE
 #define UNUSED_TRACE_VARIABLE(variable) ((void)variable)
 #else
 #define UNUSED_TRACE_VARIABLE(variable)
 #endif
 
 // A macro to indicate unimplemented functionality
 
+#if defined (ANGLE_TEST_CONFIG)
+#define NOASSERT_UNIMPLEMENTED 1
+#endif
+
 // Define NOASSERT_UNIMPLEMENTED to non zero to skip the assert fail in the unimplemented checks
 // This will allow us to test with some automated test suites (eg dEQP) without crashing
 #ifndef NOASSERT_UNIMPLEMENTED
 #define NOASSERT_UNIMPLEMENTED 0
 #endif
 
 #if !defined(NDEBUG)
 #define UNIMPLEMENTED() do { \
--- a/gfx/angle/src/common/mathutil.h
+++ b/gfx/angle/src/common/mathutil.h
@@ -502,22 +502,22 @@ inline unsigned int averageFloat10(unsig
 
 namespace rx
 {
 
 template <typename T>
 struct Range
 {
     Range() {}
-    Range(T lo, T hi) : start(lo), end(hi) { }
+    Range(T lo, T hi) : start(lo), end(hi) { ASSERT(lo <= hi); }
 
     T start;
     T end;
 
-    T length() const { return (end > start ? (end - start) : 0); }
+    T length() const { return end - start; }
 };
 
 typedef Range<int> RangeI;
 typedef Range<unsigned int> RangeUI;
 
 template <typename T>
 T roundUp(const T value, const T alignment)
 {
new file mode 100644
--- /dev/null
+++ b/gfx/angle/src/common/win32/NativeWindow.cpp
@@ -0,0 +1,51 @@
+//
+// Copyright (c) 2002-2014 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+// NativeWindow.cpp: Handler for managing HWND native window types.
+
+#include "common/NativeWindow.h"
+#include "common/debug.h"
+
+bool isValidEGLNativeWindowType(EGLNativeWindowType window)
+{
+    return (IsWindow(window) == TRUE);
+}
+
+namespace rx
+{
+NativeWindow::NativeWindow(EGLNativeWindowType window) : mWindow(window)
+{
+}
+
+HRESULT NativeWindow::createSwapChain(ID3D11Device* device, DXGIFactory* factory,
+                                      DXGI_FORMAT format, unsigned int width, unsigned int height,
+                                      DXGISwapChain** swapChain)
+{
+    if (device == NULL || factory == NULL || swapChain == NULL || width == 0 || height == 0)
+    {
+        return E_INVALIDARG;
+    }
+
+    DXGI_SWAP_CHAIN_DESC swapChainDesc = { 0 };
+    swapChainDesc.BufferCount = 1;
+    swapChainDesc.BufferDesc.Format = format;
+    swapChainDesc.BufferDesc.Width = width;
+    swapChainDesc.BufferDesc.Height = height;
+    swapChainDesc.BufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;
+    swapChainDesc.BufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
+    swapChainDesc.BufferDesc.RefreshRate.Numerator = 0;
+    swapChainDesc.BufferDesc.RefreshRate.Denominator = 1;
+    swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT | DXGI_USAGE_BACK_BUFFER;
+    swapChainDesc.Flags = 0;
+    swapChainDesc.OutputWindow = mWindow;
+    swapChainDesc.SampleDesc.Count = 1;
+    swapChainDesc.SampleDesc.Quality = 0;
+    swapChainDesc.Windowed = TRUE;
+    swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;
+
+    return factory->CreateSwapChain(device, &swapChainDesc, swapChain);
+}
+};
\ No newline at end of file
--- a/gfx/angle/src/compiler.gypi
+++ b/gfx/angle/src/compiler.gypi
@@ -19,16 +19,17 @@
             '../include/GLES3/gl3platform.h',
             '../include/GLSLANG/ShaderLang.h',
             '../include/GLSLANG/ShaderVars.h',
             '../include/KHR/khrplatform.h',
             '../include/angle_gl.h',
             'common/RefCountObject.cpp',
             'common/RefCountObject.h',
             'common/angleutils.h',
+            'common/angleutils.cpp',
             'common/blocklayout.cpp',
             'common/blocklayout.h',
             'common/debug.cpp',
             'common/debug.h',
             'common/event_tracer.cpp',
             'common/event_tracer.h',
             'common/mathutil.cpp',
             'common/mathutil.h',
--- a/gfx/angle/src/compiler/translator/Compiler.cpp
+++ b/gfx/angle/src/compiler/translator/Compiler.cpp
@@ -497,28 +497,28 @@ bool TCompiler::enforceVertexShaderTimin
 {
     RestrictVertexShaderTiming restrictor(infoSink.info);
     restrictor.enforceRestrictions(root);
     return restrictor.numErrors() == 0;
 }
 
 void TCompiler::collectVariables(TIntermNode* root)
 {
-    CollectVariables collect(&attributes,
-                             &outputVariables,
-                             &uniforms,
-                             &varyings,
-                             &interfaceBlocks,
-                             hashFunction);
+    sh::CollectVariables collect(&attributes,
+                                 &outputVariables,
+                                 &uniforms,
+                                 &varyings,
+                                 &interfaceBlocks,
+                                 hashFunction);
     root->traverse(&collect);
 
     // For backwards compatiblity with ShGetVariableInfo, expand struct
     // uniforms and varyings into separate variables for each field.
-    ExpandVariables(uniforms, &expandedUniforms);
-    ExpandVariables(varyings, &expandedVaryings);
+    sh::ExpandVariables(uniforms, &expandedUniforms);
+    sh::ExpandVariables(varyings, &expandedVaryings);
 }
 
 bool TCompiler::enforcePackingRestrictions()
 {
     VariablePacker packer;
     return packer.CheckVariablesWithinPackingLimits(maxUniformVectors, expandedUniforms);
 }
 
--- a/gfx/angle/src/compiler/translator/Compiler.h
+++ b/gfx/angle/src/compiler/translator/Compiler.h
@@ -78,16 +78,19 @@ class TCompiler : public TShHandleBase
 
     ShHashFunction64 getHashFunction() const { return hashFunction; }
     NameMap& getNameMap() { return nameMap; }
     TSymbolTable& getSymbolTable() { return symbolTable; }
     ShShaderSpec getShaderSpec() const { return shaderSpec; }
     ShShaderOutput getOutputType() const { return outputType; }
     std::string getBuiltInResourcesString() const { return builtInResourcesString; }
 
+    // Get the resources set by InitBuiltInSymbolTable
+    const ShBuiltInResources& getResources() const;
+
   protected:
     sh::GLenum getShaderType() const { return shaderType; }
     // Initialize symbol-table with built-in symbols.
     bool InitBuiltInSymbolTable(const ShBuiltInResources& resources);
     // Compute the string representation of the built-in resources
     void setResourceString();
     // Clears the results from the previous compilation.
     void clearResults();
@@ -123,18 +126,16 @@ class TCompiler : public TShHandleBase
     bool enforceVertexShaderTimingRestrictions(TIntermNode* root);
     // Returns true if the shader does not use sampler dependent values to affect control
     // flow or in operations whose time can depend on the input values.
     bool enforceFragmentShaderTimingRestrictions(const TDependencyGraph& graph);
     // Return true if the maximum expression complexity is below the limit.
     bool limitExpressionComplexity(TIntermNode* root);
     // Get built-in extensions with default behavior.
     const TExtensionBehavior& getExtensionBehavior() const;
-    // Get the resources set by InitBuiltInSymbolTable
-    const ShBuiltInResources& getResources() const;
 
     const ArrayBoundsClamper& getArrayBoundsClamper() const;
     ShArrayIndexClampingStrategy getArrayIndexClampingStrategy() const;
     const BuiltInFunctionEmulator& getBuiltInFunctionEmulator() const;
 
     std::vector<sh::Attribute> attributes;
     std::vector<sh::Attribute> outputVariables;
     std::vector<sh::Uniform> uniforms;
--- a/gfx/angle/src/compiler/translator/OutputHLSL.cpp
+++ b/gfx/angle/src/compiler/translator/OutputHLSL.cpp
@@ -16,36 +16,25 @@
 #include "compiler/translator/UnfoldShortCircuit.h"
 #include "compiler/translator/FlagStd140Structs.h"
 #include "compiler/translator/NodeSearch.h"
 #include "compiler/translator/RewriteElseBlocks.h"
 #include "compiler/translator/UtilsHLSL.h"
 #include "compiler/translator/util.h"
 #include "compiler/translator/UniformHLSL.h"
 #include "compiler/translator/StructureHLSL.h"
+#include "compiler/translator/TranslatorHLSL.h"
 
 #include <algorithm>
 #include <cfloat>
 #include <stdio.h>
 
 namespace sh
 {
 
-static sh::Attribute MakeAttributeFromType(const TType &type, const TString &name)
-{
-    sh::Attribute attributeVar;
-    attributeVar.type = GLVariableType(type);
-    attributeVar.precision = GLVariablePrecision(type);
-    attributeVar.name = name.c_str();
-    attributeVar.arraySize = static_cast<unsigned int>(type.getArraySize());
-    attributeVar.location = type.getLayoutQualifier().location;
-
-    return attributeVar;
-}
-
 TString OutputHLSL::TextureFunction::name() const
 {
     TString name = "gl_texture";
 
     if (IsSampler2D(sampler))
     {
         name += "2D";
     }
@@ -100,18 +89,20 @@ bool OutputHLSL::TextureFunction::operat
     if (offset && !rhs.offset) return false;
 
     if (method < rhs.method)   return true;
     if (method > rhs.method)   return false;
 
     return false;
 }
 
-OutputHLSL::OutputHLSL(TParseContext &context, const ShBuiltInResources& resources, ShShaderOutput outputType)
-    : TIntermTraverser(true, true, true), mContext(context), mOutputType(outputType)
+OutputHLSL::OutputHLSL(TParseContext &context, TranslatorHLSL *parentTranslator)
+    : TIntermTraverser(true, true, true),
+      mContext(context),
+      mOutputType(parentTranslator->getOutputType())
 {
     mUnfoldShortCircuit = new UnfoldShortCircuit(context, this);
     mInsideFunction = false;
 
     mUsesFragColor = false;
     mUsesFragData = false;
     mUsesDepthRange = false;
     mUsesFragCoord = false;
@@ -133,29 +124,30 @@ OutputHLSL::OutputHLSL(TParseContext &co
     mUsesFaceforward4 = false;
     mUsesAtan2_1 = false;
     mUsesAtan2_2 = false;
     mUsesAtan2_3 = false;
     mUsesAtan2_4 = false;
     mUsesDiscardRewriting = false;
     mUsesNestedBreak = false;
 
+    const ShBuiltInResources &resources = parentTranslator->getResources();
     mNumRenderTargets = resources.EXT_draw_buffers ? resources.MaxDrawBuffers : 1;
 
     mUniqueIndex = 0;
 
     mContainsLoopDiscontinuity = false;
     mOutputLod0Function = false;
     mInsideDiscontinuousLoop = false;
     mNestedLoopDepth = 0;
 
     mExcessiveLoopIndex = NULL;
 
     mStructureHLSL = new StructureHLSL;
-    mUniformHLSL = new UniformHLSL(mStructureHLSL, mOutputType);
+    mUniformHLSL = new UniformHLSL(mStructureHLSL, parentTranslator);
 
     if (mOutputType == SH_HLSL9_OUTPUT)
     {
         if (mContext.shaderType == GL_FRAGMENT_SHADER)
         {
             // Reserve registers for dx_DepthRange, dx_ViewCoords and dx_DepthFront
             mUniformHLSL->reserveUniformRegisters(3);
         }
@@ -219,41 +211,16 @@ void OutputHLSL::makeFlaggedStructMaps(c
     }
 }
 
 TInfoSinkBase &OutputHLSL::getBodyStream()
 {
     return mBody;
 }
 
-const std::vector<sh::Uniform> &OutputHLSL::getUniforms()
-{
-    return mUniformHLSL->getUniforms();
-}
-
-const std::vector<sh::InterfaceBlock> &OutputHLSL::getInterfaceBlocks() const
-{
-    return mUniformHLSL->getInterfaceBlocks();
-}
-
-const std::vector<sh::Attribute> &OutputHLSL::getOutputVariables() const
-{
-    return mActiveOutputVariables;
-}
-
-const std::vector<sh::Attribute> &OutputHLSL::getAttributes() const
-{
-    return mActiveAttributes;
-}
-
-const std::vector<sh::Varying> &OutputHLSL::getVaryings() const
-{
-    return mActiveVaryings;
-}
-
 const std::map<std::string, unsigned int> &OutputHLSL::getInterfaceBlockRegisterMap() const
 {
     return mUniformHLSL->getInterfaceBlockRegisterMap();
 }
 
 const std::map<std::string, unsigned int> &OutputHLSL::getUniformRegisterMap() const
 {
     return mUniformHLSL->getUniformRegisterMap();
@@ -331,29 +298,24 @@ void OutputHLSL::header()
     for (ReferencedSymbols::const_iterator varying = mReferencedVaryings.begin(); varying != mReferencedVaryings.end(); varying++)
     {
         const TType &type = varying->second->getType();
         const TString &name = varying->second->getSymbol();
 
         // Program linking depends on this exact format
         varyings += "static " + InterpolationString(type.getQualifier()) + " " + TypeString(type) + " " +
                     Decorate(name) + ArrayString(type) + " = " + initializer(type) + ";\n";
-
-        declareVaryingToList(type, type.getQualifier(), name, mActiveVaryings);
     }
 
     for (ReferencedSymbols::const_iterator attribute = mReferencedAttributes.begin(); attribute != mReferencedAttributes.end(); attribute++)
     {
         const TType &type = attribute->second->getType();
         const TString &name = attribute->second->getSymbol();
 
         attributes += "static " + TypeString(type) + " " + Decorate(name) + ArrayString(type) + " = " + initializer(type) + ";\n";
-
-        sh::Attribute attributeVar = MakeAttributeFromType(type, name);
-        mActiveAttributes.push_back(attributeVar);
     }
 
     out << mStructureHLSL->structsHeader();
 
     out << mUniformHLSL->uniformsHeader(mOutputType, mReferencedUniforms);
     out << mUniformHLSL->interfaceBlocksHeader(mReferencedInterfaceBlocks);
 
     if (mUsesDiscardRewriting)
@@ -379,19 +341,16 @@ void OutputHLSL::header()
         {
             for (ReferencedSymbols::const_iterator outputVariableIt = mReferencedOutputVariables.begin(); outputVariableIt != mReferencedOutputVariables.end(); outputVariableIt++)
             {
                 const TString &variableName = outputVariableIt->first;
                 const TType &variableType = outputVariableIt->second->getType();
 
                 out << "static " + TypeString(variableType) + " out_" + variableName + ArrayString(variableType) +
                        " = " + initializer(variableType) + ";\n";
-
-                sh::Attribute outputVar = MakeAttributeFromType(variableType, variableName);
-                mActiveOutputVariables.push_back(outputVar);
             }
         }
         else
         {
             const unsigned int numColorValues = usingMRTExtension ? mNumRenderTargets : 1;
 
             out << "static float4 gl_Color[" << numColorValues << "] =\n"
                    "{\n";
@@ -2917,17 +2876,9 @@ const ConstantUnion *OutputHLSL::writeCo
         {
             out << ")";
         }
     }
 
     return constUnion;
 }
 
-void OutputHLSL::declareVaryingToList(const TType &type, TQualifier baseTypeQualifier,
-                                      const TString &name, std::vector<Varying> &fieldsOut)
-{
-    GetVariableTraverser traverser;
-    traverser.traverse(type, name, &fieldsOut);
-    fieldsOut.back().interpolation = GetInterpolationType(baseTypeQualifier);
 }
-
-}
--- a/gfx/angle/src/compiler/translator/OutputHLSL.h
+++ b/gfx/angle/src/compiler/translator/OutputHLSL.h
@@ -22,27 +22,22 @@ class UnfoldShortCircuit;
 class StructureHLSL;
 class UniformHLSL;
 
 typedef std::map<TString, TIntermSymbol*> ReferencedSymbols;
 
 class OutputHLSL : public TIntermTraverser
 {
   public:
-    OutputHLSL(TParseContext &context, const ShBuiltInResources& resources, ShShaderOutput outputType);
+    OutputHLSL(TParseContext &context, TranslatorHLSL *parentTranslator);
     ~OutputHLSL();
 
     void output();
 
     TInfoSinkBase &getBodyStream();
-    const std::vector<sh::Uniform> &getUniforms();
-    const std::vector<sh::InterfaceBlock> &getInterfaceBlocks() const;
-    const std::vector<sh::Attribute> &getOutputVariables() const;
-    const std::vector<sh::Attribute> &getAttributes() const;
-    const std::vector<sh::Varying> &getVaryings() const;
 
     const std::map<std::string, unsigned int> &getInterfaceBlockRegisterMap() const;
     const std::map<std::string, unsigned int> &getUniformRegisterMap() const;
 
     static TString initializer(const TType &type);
 
   protected:
     void header();
@@ -150,23 +145,18 @@ class OutputHLSL : public TIntermTravers
 
     bool mContainsLoopDiscontinuity;
     bool mOutputLod0Function;
     bool mInsideDiscontinuousLoop;
     int mNestedLoopDepth;
 
     TIntermSymbol *mExcessiveLoopIndex;
 
-    void declareVaryingToList(const TType &type, TQualifier baseTypeQualifier, const TString &name, std::vector<sh::Varying>& fieldsOut);
-
     TString structInitializerString(int indent, const TStructure &structure, const TString &rhsStructName);
 
-    std::vector<sh::Attribute> mActiveOutputVariables;
-    std::vector<sh::Attribute> mActiveAttributes;
-    std::vector<sh::Varying> mActiveVaryings;
     std::map<TIntermTyped*, TString> mFlaggedStructMappedNames;
     std::map<TIntermTyped*, TString> mFlaggedStructOriginalNames;
 
     void makeFlaggedStructMaps(const std::vector<TIntermTyped *> &flaggedStructs);
 };
 
 }
 
--- a/gfx/angle/src/compiler/translator/TranslatorHLSL.cpp
+++ b/gfx/angle/src/compiler/translator/TranslatorHLSL.cpp
@@ -12,26 +12,20 @@
 TranslatorHLSL::TranslatorHLSL(sh::GLenum type, ShShaderSpec spec, ShShaderOutput output)
     : TCompiler(type, spec, output)
 {
 }
 
 void TranslatorHLSL::translate(TIntermNode *root)
 {
     TParseContext& parseContext = *GetGlobalParseContext();
-    sh::OutputHLSL outputHLSL(parseContext, getResources(), getOutputType());
+    sh::OutputHLSL outputHLSL(parseContext, this);
 
     outputHLSL.output();
 
-    attributes      = outputHLSL.getAttributes();
-    outputVariables = outputHLSL.getOutputVariables();
-    uniforms        = outputHLSL.getUniforms();
-    varyings        = outputHLSL.getVaryings();
-    interfaceBlocks = outputHLSL.getInterfaceBlocks();
-
     mInterfaceBlockRegisterMap = outputHLSL.getInterfaceBlockRegisterMap();
     mUniformRegisterMap = outputHLSL.getUniformRegisterMap();
 }
 
 bool TranslatorHLSL::hasInterfaceBlock(const std::string &interfaceBlockName) const
 {
     return (mInterfaceBlockRegisterMap.count(interfaceBlockName) > 0);
 }
--- a/gfx/angle/src/compiler/translator/UniformHLSL.cpp
+++ b/gfx/angle/src/compiler/translator/UniformHLSL.cpp
@@ -9,16 +9,17 @@
 
 #include "OutputHLSL.h"
 #include "common/blocklayout.h"
 #include "common/utilities.h"
 #include "compiler/translator/UniformHLSL.h"
 #include "compiler/translator/StructureHLSL.h"
 #include "compiler/translator/util.h"
 #include "compiler/translator/UtilsHLSL.h"
+#include "compiler/translator/TranslatorHLSL.h"
 
 namespace sh
 {
 
 static const char *UniformRegisterPrefix(const TType &type)
 {
     if (IsSampler(type.getBasicType()))
     {
@@ -55,46 +56,61 @@ static TString InterfaceBlockFieldTypeSt
     }
 }
 
 static TString InterfaceBlockStructName(const TInterfaceBlock &interfaceBlock)
 {
     return DecoratePrivate(interfaceBlock.name()) + "_type";
 }
 
-UniformHLSL::UniformHLSL(StructureHLSL *structureHLSL, ShShaderOutput outputType)
+UniformHLSL::UniformHLSL(StructureHLSL *structureHLSL, TranslatorHLSL *translator)
     : mUniformRegister(0),
       mInterfaceBlockRegister(0),
       mSamplerRegister(0),
       mStructureHLSL(structureHLSL),
-      mOutputType(outputType)
+      mOutputType(translator->getOutputType()),
+      mUniforms(translator->getUniforms())
 {}
 
 void UniformHLSL::reserveUniformRegisters(unsigned int registerCount)
 {
     mUniformRegister = registerCount;
 }
 
 void UniformHLSL::reserveInterfaceBlockRegisters(unsigned int registerCount)
 {
     mInterfaceBlockRegister = registerCount;
 }
 
+const Uniform *UniformHLSL::findUniformByName(const TString &name) const
+{
+    for (size_t uniformIndex = 0; uniformIndex < mUniforms.size(); ++uniformIndex)
+    {
+        if (mUniforms[uniformIndex].name == name.c_str())
+        {
+            return &mUniforms[uniformIndex];
+        }
+    }
+
+    UNREACHABLE();
+    return NULL;
+}
+
 unsigned int UniformHLSL::declareUniformAndAssignRegister(const TType &type, const TString &name)
 {
     unsigned int registerIndex = (IsSampler(type.getBasicType()) ? mSamplerRegister : mUniformRegister);
 
-    GetVariableTraverser traverser;
-    traverser.traverse(type, name, &mActiveUniforms);
+    const Uniform *uniform = findUniformByName(name);
+    ASSERT(uniform);
 
-    const sh::Uniform &activeUniform = mActiveUniforms.back();
-    mUniformRegisterMap[activeUniform.name] = registerIndex;
+    mUniformRegisterMap[uniform->name] = registerIndex;
 
-    unsigned int registerCount = HLSLVariableRegisterCount(activeUniform, mOutputType);
-    if (IsSampler(type.getBasicType()))
+    unsigned int registerCount = HLSLVariableRegisterCount(*uniform, mOutputType);
+
+    if (gl::IsSampler(uniform->type))
     {
         mSamplerRegister += registerCount;
     }
     else
     {
         mUniformRegister += registerCount;
     }
 
@@ -149,33 +165,20 @@ TString UniformHLSL::interfaceBlocksHead
          interfaceBlockIt != referencedInterfaceBlocks.end(); interfaceBlockIt++)
     {
         const TType &nodeType = interfaceBlockIt->second->getType();
         const TInterfaceBlock &interfaceBlock = *nodeType.getInterfaceBlock();
 
         unsigned int arraySize = static_cast<unsigned int>(interfaceBlock.arraySize());
         unsigned int activeRegister = mInterfaceBlockRegister;
 
-        InterfaceBlock activeBlock;
-        activeBlock.name = interfaceBlock.name().c_str();
-        activeBlock.arraySize = arraySize;
-
-        GetInterfaceBlockFields(interfaceBlock, &activeBlock.fields);
-
-        mInterfaceBlockRegisterMap[activeBlock.name] = activeRegister;
+        mInterfaceBlockRegisterMap[interfaceBlock.name().c_str()] = activeRegister;
         mInterfaceBlockRegister += std::max(1u, arraySize);
 
-        activeBlock.layout = GetBlockLayoutType(interfaceBlock.blockStorage());
-
-        if (interfaceBlock.matrixPacking() == EmpRowMajor)
-        {
-            activeBlock.isRowMajorLayout = true;
-        }
-
-        mActiveInterfaceBlocks.push_back(activeBlock);
+        // FIXME: interface block field names
 
         if (interfaceBlock.hasInstanceName())
         {
             interfaceBlocks += interfaceBlockStructString(interfaceBlock);
         }
 
         if (arraySize > 0)
         {
--- a/gfx/angle/src/compiler/translator/UniformHLSL.h
+++ b/gfx/angle/src/compiler/translator/UniformHLSL.h
@@ -14,52 +14,50 @@
 
 namespace sh
 {
 class StructureHLSL;
 
 class UniformHLSL
 {
   public:
-    UniformHLSL(StructureHLSL *structureHLSL, ShShaderOutput outputType);
+    UniformHLSL(StructureHLSL *structureHLSL, TranslatorHLSL *translator);
 
     void reserveUniformRegisters(unsigned int registerCount);
     void reserveInterfaceBlockRegisters(unsigned int registerCount);
     TString uniformsHeader(ShShaderOutput outputType, const ReferencedSymbols &referencedUniforms);
     TString interfaceBlocksHeader(const ReferencedSymbols &referencedInterfaceBlocks);
 
     // Used for direct index references
     static TString interfaceBlockInstanceString(const TInterfaceBlock& interfaceBlock, unsigned int arrayIndex);
 
-    const std::vector<Uniform> &getUniforms() const { return mActiveUniforms; }
-    const std::vector<InterfaceBlock> &getInterfaceBlocks() const { return mActiveInterfaceBlocks; }
     const std::map<std::string, unsigned int> &getInterfaceBlockRegisterMap() const
     {
         return mInterfaceBlockRegisterMap;
     }
     const std::map<std::string, unsigned int> &getUniformRegisterMap() const
     {
         return mUniformRegisterMap;
     }
 
   private:
     TString interfaceBlockString(const TInterfaceBlock &interfaceBlock, unsigned int registerIndex, unsigned int arrayIndex);
     TString interfaceBlockMembersString(const TInterfaceBlock &interfaceBlock, TLayoutBlockStorage blockStorage);
     TString interfaceBlockStructString(const TInterfaceBlock &interfaceBlock);
+    const Uniform *findUniformByName(const TString &name) const;
 
     // Returns the uniform's register index
     unsigned int declareUniformAndAssignRegister(const TType &type, const TString &name);
 
     unsigned int mUniformRegister;
     unsigned int mInterfaceBlockRegister;
     unsigned int mSamplerRegister;
     StructureHLSL *mStructureHLSL;
     ShShaderOutput mOutputType;
 
-    std::vector<Uniform> mActiveUniforms;
-    std::vector<InterfaceBlock> mActiveInterfaceBlocks;
+    const std::vector<Uniform> &mUniforms;
     std::map<std::string, unsigned int> mInterfaceBlockRegisterMap;
     std::map<std::string, unsigned int> mUniformRegisterMap;
 };
 
 }
 
 #endif // TRANSLATOR_UNIFORMHLSL_H_
--- a/gfx/angle/src/compiler/translator/VariableInfo.cpp
+++ b/gfx/angle/src/compiler/translator/VariableInfo.cpp
@@ -4,47 +4,76 @@
 // found in the LICENSE file.
 //
 
 #include "angle_gl.h"
 #include "compiler/translator/VariableInfo.h"
 #include "compiler/translator/util.h"
 #include "common/utilities.h"
 
-static void ExpandUserDefinedVariable(const sh::ShaderVariable &variable,
-                                      const std::string &name,
-                                      const std::string &mappedName,
-                                      bool markStaticUse,
-                                      std::vector<sh::ShaderVariable> *expanded);
+namespace sh
+{
+
+namespace
+{
+
+TString InterfaceBlockFieldName(const TInterfaceBlock &interfaceBlock, const TField &field)
+{
+    if (interfaceBlock.hasInstanceName())
+    {
+        return interfaceBlock.name() + "." + field.name();
+    }
+    else
+    {
+        return field.name();
+    }
+}
 
-static void ExpandVariable(const sh::ShaderVariable &variable,
-                           const std::string &name,
-                           const std::string &mappedName,
-                           bool markStaticUse,
-                           std::vector<sh::ShaderVariable> *expanded)
+BlockLayoutType GetBlockLayoutType(TLayoutBlockStorage blockStorage)
+{
+    switch (blockStorage)
+    {
+      case EbsPacked:         return BLOCKLAYOUT_PACKED;
+      case EbsShared:         return BLOCKLAYOUT_SHARED;
+      case EbsStd140:         return BLOCKLAYOUT_STANDARD;
+      default: UNREACHABLE(); return BLOCKLAYOUT_SHARED;
+    }
+}
+
+void ExpandUserDefinedVariable(const ShaderVariable &variable,
+                               const std::string &name,
+                               const std::string &mappedName,
+                               bool markStaticUse,
+                               std::vector<ShaderVariable> *expanded);
+
+void ExpandVariable(const ShaderVariable &variable,
+                    const std::string &name,
+                    const std::string &mappedName,
+                    bool markStaticUse,
+                    std::vector<ShaderVariable> *expanded)
 {
     if (variable.isStruct())
     {
         if (variable.isArray())
         {
             for (size_t elementIndex = 0; elementIndex < variable.elementCount(); elementIndex++)
             {
-                std::string lname = name + ArrayString(elementIndex);
-                std::string lmappedName = mappedName + ArrayString(elementIndex);
+                std::string lname = name + ::ArrayString(elementIndex);
+                std::string lmappedName = mappedName + ::ArrayString(elementIndex);
                 ExpandUserDefinedVariable(variable, lname, lmappedName, markStaticUse, expanded);
             }
         }
         else
         {
             ExpandUserDefinedVariable(variable, name, mappedName, markStaticUse, expanded);
         }
     }
     else
     {
-        sh::ShaderVariable expandedVar = variable;
+        ShaderVariable expandedVar = variable;
 
         expandedVar.name = name;
         expandedVar.mappedName = mappedName;
 
         // Mark all expanded fields as used if the parent is used
         if (markStaticUse)
         {
             expandedVar.staticUse = true;
@@ -55,51 +84,53 @@ static void ExpandVariable(const sh::Sha
             expandedVar.name += "[0]";
             expandedVar.mappedName += "[0]";
         }
 
         expanded->push_back(expandedVar);
     }
 }
 
-static void ExpandUserDefinedVariable(const sh::ShaderVariable &variable,
-                                      const std::string &name,
-                                      const std::string &mappedName,
-                                      bool markStaticUse,
-                                      std::vector<sh::ShaderVariable> *expanded)
+void ExpandUserDefinedVariable(const ShaderVariable &variable,
+                               const std::string &name,
+                               const std::string &mappedName,
+                               bool markStaticUse,
+                               std::vector<ShaderVariable> *expanded)
 {
     ASSERT(variable.isStruct());
 
-    const std::vector<sh::ShaderVariable> &fields = variable.fields;
+    const std::vector<ShaderVariable> &fields = variable.fields;
 
     for (size_t fieldIndex = 0; fieldIndex < fields.size(); fieldIndex++)
     {
-        const sh::ShaderVariable &field = fields[fieldIndex];
+        const ShaderVariable &field = fields[fieldIndex];
         ExpandVariable(field,
                        name + "." + field.name,
                        mappedName + "." + field.mappedName,
                        markStaticUse,
                        expanded);
     }
 }
 
 template <class VarT>
-static VarT *FindVariable(const TString &name,
-                          std::vector<VarT> *infoList)
+VarT *FindVariable(const TString &name,
+                  std::vector<VarT> *infoList)
 {
     // TODO(zmo): optimize this function.
     for (size_t ii = 0; ii < infoList->size(); ++ii)
     {
         if ((*infoList)[ii].name.c_str() == name)
             return &((*infoList)[ii]);
     }
 
     return NULL;
 }
 
+}
+
 CollectVariables::CollectVariables(std::vector<sh::Attribute> *attribs,
                                    std::vector<sh::Attribute> *outputVariables,
                                    std::vector<sh::Uniform> *uniforms,
                                    std::vector<sh::Varying> *varyings,
                                    std::vector<sh::InterfaceBlock> *interfaceBlocks,
                                    ShHashFunction64 hashFunction)
     : mAttribs(attribs),
       mOutputVariables(outputVariables),
@@ -116,20 +147,20 @@ CollectVariables::CollectVariables(std::
 // We want to check whether a uniform/varying is statically used
 // because we only count the used ones in packing computing.
 // Also, gl_FragCoord, gl_PointCoord, and gl_FrontFacing count
 // toward varying counting if they are statically used in a fragment
 // shader.
 void CollectVariables::visitSymbol(TIntermSymbol *symbol)
 {
     ASSERT(symbol != NULL);
-    sh::ShaderVariable *var = NULL;
+    ShaderVariable *var = NULL;
     const TString &symbolName = symbol->getSymbol();
 
-    if (sh::IsVarying(symbol->getQualifier()))
+    if (IsVarying(symbol->getQualifier()))
     {
         var = FindVariable(symbolName, mVaryings);
     }
     else if (symbol->getType().getBasicType() == EbtInterfaceBlock)
     {
         UNREACHABLE();
     }
     else
@@ -143,17 +174,17 @@ void CollectVariables::visitSymbol(TInte
           case EvqFragmentOut:
             var = FindVariable(symbolName, mOutputVariables);
             break;
           case EvqUniform:
             {
                 const TInterfaceBlock *interfaceBlock = symbol->getType().getInterfaceBlock();
                 if (interfaceBlock)
                 {
-                    sh::InterfaceBlock *namedBlock = FindVariable(interfaceBlock->name(), mInterfaceBlocks);
+                    InterfaceBlock *namedBlock = FindVariable(interfaceBlock->name(), mInterfaceBlocks);
                     ASSERT(namedBlock);
                     var = FindVariable(symbolName, &namedBlock->fields);
 
                     // Set static use on the parent interface block here
                     namedBlock->staticUse = true;
 
                 }
                 else
@@ -163,45 +194,45 @@ void CollectVariables::visitSymbol(TInte
 
                 // It's an internal error to reference an undefined user uniform
                 ASSERT(symbolName.compare(0, 3, "gl_") == 0 || var);
             }
             break;
           case EvqFragCoord:
             if (!mFragCoordAdded)
             {
-                sh::Varying info;
+                Varying info;
                 info.name = "gl_FragCoord";
                 info.mappedName = "gl_FragCoord";
                 info.type = GL_FLOAT_VEC4;
                 info.arraySize = 0;
                 info.precision = GL_MEDIUM_FLOAT;  // Use mediump as it doesn't really matter.
                 info.staticUse = true;
                 mVaryings->push_back(info);
                 mFragCoordAdded = true;
             }
             return;
           case EvqFrontFacing:
             if (!mFrontFacingAdded)
             {
-                sh::Varying info;
+                Varying info;
                 info.name = "gl_FrontFacing";
                 info.mappedName = "gl_FrontFacing";
                 info.type = GL_BOOL;
                 info.arraySize = 0;
                 info.precision = GL_NONE;
                 info.staticUse = true;
                 mVaryings->push_back(info);
                 mFrontFacingAdded = true;
             }
             return;
           case EvqPointCoord:
             if (!mPointCoordAdded)
             {
-                sh::Varying info;
+                Varying info;
                 info.name = "gl_PointCoord";
                 info.mappedName = "gl_PointCoord";
                 info.type = GL_FLOAT_VEC2;
                 info.arraySize = 0;
                 info.precision = GL_MEDIUM_FLOAT;  // Use mediump as it doesn't really matter.
                 info.staticUse = true;
                 mVaryings->push_back(info);
                 mPointCoordAdded = true;
@@ -212,73 +243,85 @@ void CollectVariables::visitSymbol(TInte
         }
     }
     if (var)
     {
         var->staticUse = true;
     }
 }
 
-class NameHashingTraverser : public sh::GetVariableTraverser
+class NameHashingTraverser : public GetVariableTraverser
 {
   public:
     NameHashingTraverser(ShHashFunction64 hashFunction)
         : mHashFunction(hashFunction)
     {}
 
   private:
     DISALLOW_COPY_AND_ASSIGN(NameHashingTraverser);
 
-    virtual void visitVariable(sh::ShaderVariable *variable)
+    virtual void visitVariable(ShaderVariable *variable)
     {
         TString stringName = TString(variable->name.c_str());
         variable->mappedName = TIntermTraverser::hash(stringName, mHashFunction).c_str();
     }
 
     ShHashFunction64 mHashFunction;
 };
 
 // Attributes, which cannot have struct fields, are a special case
 template <>
 void CollectVariables::visitVariable(const TIntermSymbol *variable,
-                                     std::vector<sh::Attribute> *infoList) const
+                                     std::vector<Attribute> *infoList) const
 {
     ASSERT(variable);
     const TType &type = variable->getType();
     ASSERT(!type.getStruct());
 
-    sh::Attribute attribute;
+    Attribute attribute;
 
-    attribute.type = sh::GLVariableType(type);
-    attribute.precision = sh::GLVariablePrecision(type);
+    attribute.type = GLVariableType(type);
+    attribute.precision = GLVariablePrecision(type);
     attribute.name = variable->getSymbol().c_str();
     attribute.arraySize = static_cast<unsigned int>(type.getArraySize());
     attribute.mappedName = TIntermTraverser::hash(variable->getSymbol(), mHashFunction).c_str();
     attribute.location = variable->getType().getLayoutQualifier().location;
 
     infoList->push_back(attribute);
 }
 
 template <>
 void CollectVariables::visitVariable(const TIntermSymbol *variable,
-                                     std::vector<sh::InterfaceBlock> *infoList) const
+                                     std::vector<InterfaceBlock> *infoList) const
 {
-    sh::InterfaceBlock interfaceBlock;
+    InterfaceBlock interfaceBlock;
     const TInterfaceBlock *blockType = variable->getType().getInterfaceBlock();
     ASSERT(blockType);
 
     interfaceBlock.name = blockType->name().c_str();
     interfaceBlock.mappedName = TIntermTraverser::hash(variable->getSymbol(), mHashFunction).c_str();
     interfaceBlock.instanceName = (blockType->hasInstanceName() ? blockType->instanceName().c_str() : "");
     interfaceBlock.arraySize = variable->getArraySize();
     interfaceBlock.isRowMajorLayout = (blockType->matrixPacking() == EmpRowMajor);
-    interfaceBlock.layout = sh::GetBlockLayoutType(blockType->blockStorage());
+    interfaceBlock.layout = GetBlockLayoutType(blockType->blockStorage());
 
     // Gather field information
-    sh::GetInterfaceBlockFields(*blockType, &interfaceBlock.fields);
+    const TFieldList &fieldList = blockType->fields();
+
+    for (size_t fieldIndex = 0; fieldIndex < fieldList.size(); ++fieldIndex)
+    {
+        const TField &field = *fieldList[fieldIndex];
+        const TString &fullFieldName = InterfaceBlockFieldName(*blockType, field);
+        const TType &fieldType = *field.type();
+
+        GetVariableTraverser traverser;
+        traverser.traverse(fieldType, fullFieldName, &interfaceBlock.fields);
+
+        interfaceBlock.fields.back().isRowMajorLayout = (fieldType.getLayoutQualifier().matrixPacking == EmpRowMajor);
+    }
 
     infoList->push_back(interfaceBlock);
 }
 
 template <typename VarT>
 void CollectVariables::visitVariable(const TIntermSymbol *variable,
                                      std::vector<VarT> *infoList) const
 {
@@ -319,17 +362,17 @@ bool CollectVariables::visitAggregate(Vi
 
             if (typedNode.getBasicType() == EbtInterfaceBlock)
             {
                 visitInfoList(sequence, mInterfaceBlocks);
                 visitChildren = false;
             }
             else if (qualifier == EvqAttribute || qualifier == EvqVertexIn ||
                      qualifier == EvqFragmentOut || qualifier == EvqUniform ||
-                     sh::IsVarying(qualifier))
+                     IsVarying(qualifier))
             {
                 switch (qualifier)
                 {
                   case EvqAttribute:
                   case EvqVertexIn:
                     visitInfoList(sequence, mAttribs);
                     break;
                   case EvqFragmentOut:
@@ -360,34 +403,36 @@ bool CollectVariables::visitBinary(Visit
         // NOTE: we do not determine static use for individual blocks of an array
         TIntermTyped *blockNode = binaryNode->getLeft()->getAsTyped();
         ASSERT(blockNode);
 
         TIntermConstantUnion *constantUnion = binaryNode->getRight()->getAsConstantUnion();
         ASSERT(constantUnion);
 
         const TInterfaceBlock *interfaceBlock = blockNode->getType().getInterfaceBlock();
-        sh::InterfaceBlock *namedBlock = FindVariable(interfaceBlock->name(), mInterfaceBlocks);
+        InterfaceBlock *namedBlock = FindVariable(interfaceBlock->name(), mInterfaceBlocks);
         ASSERT(namedBlock);
         namedBlock->staticUse = true;
 
         unsigned int fieldIndex = constantUnion->getUConst(0);
         ASSERT(fieldIndex < namedBlock->fields.size());
         namedBlock->fields[fieldIndex].staticUse = true;
         return false;
     }
 
     return true;
 }
 
 template <typename VarT>
 void ExpandVariables(const std::vector<VarT> &compact,
-                     std::vector<sh::ShaderVariable> *expanded)
+                     std::vector<ShaderVariable> *expanded)
 {
     for (size_t variableIndex = 0; variableIndex < compact.size(); variableIndex++)
     {
-        const sh::ShaderVariable &variable = compact[variableIndex];
+        const ShaderVariable &variable = compact[variableIndex];
         ExpandVariable(variable, variable.name, variable.mappedName, variable.staticUse, expanded);
     }
 }
 
-template void ExpandVariables(const std::vector<sh::Uniform> &, std::vector<sh::ShaderVariable> *);
-template void ExpandVariables(const std::vector<sh::Varying> &, std::vector<sh::ShaderVariable> *);
+template void ExpandVariables(const std::vector<Uniform> &, std::vector<ShaderVariable> *);
+template void ExpandVariables(const std::vector<Varying> &, std::vector<ShaderVariable> *);
+
+}
--- a/gfx/angle/src/compiler/translator/VariableInfo.h
+++ b/gfx/angle/src/compiler/translator/VariableInfo.h
@@ -6,51 +6,56 @@
 
 #ifndef COMPILER_VARIABLE_INFO_H_
 #define COMPILER_VARIABLE_INFO_H_
 
 #include <GLSLANG/ShaderLang.h>
 
 #include "compiler/translator/IntermNode.h"
 
+namespace sh
+{
+
 // Traverses intermediate tree to collect all attributes, uniforms, varyings.
 class CollectVariables : public TIntermTraverser
 {
   public:
-    CollectVariables(std::vector<sh::Attribute> *attribs,
-                     std::vector<sh::Attribute> *outputVariables,
-                     std::vector<sh::Uniform> *uniforms,
-                     std::vector<sh::Varying> *varyings,
-                     std::vector<sh::InterfaceBlock> *interfaceBlocks,
+    CollectVariables(std::vector<Attribute> *attribs,
+                     std::vector<Attribute> *outputVariables,
+                     std::vector<Uniform> *uniforms,
+                     std::vector<Varying> *varyings,
+                     std::vector<InterfaceBlock> *interfaceBlocks,
                      ShHashFunction64 hashFunction);
 
     virtual void visitSymbol(TIntermSymbol *symbol);
     virtual bool visitAggregate(Visit, TIntermAggregate *node);
     virtual bool visitBinary(Visit visit, TIntermBinary *binaryNode);
 
   private:
     template <typename VarT>
     void visitVariable(const TIntermSymbol *variable, std::vector<VarT> *infoList) const;
 
     template <typename VarT>
     void visitInfoList(const TIntermSequence &sequence, std::vector<VarT> *infoList) const;
 
-    std::vector<sh::Attribute> *mAttribs;
-    std::vector<sh::Attribute> *mOutputVariables;
-    std::vector<sh::Uniform> *mUniforms;
-    std::vector<sh::Varying> *mVaryings;
-    std::vector<sh::InterfaceBlock> *mInterfaceBlocks;
+    std::vector<Attribute> *mAttribs;
+    std::vector<Attribute> *mOutputVariables;
+    std::vector<Uniform> *mUniforms;
+    std::vector<Varying> *mVaryings;
+    std::vector<InterfaceBlock> *mInterfaceBlocks;
 
-    std::map<std::string, sh::InterfaceBlockField *> mInterfaceBlockFields;
+    std::map<std::string, InterfaceBlockField *> mInterfaceBlockFields;
 
     bool mPointCoordAdded;
     bool mFrontFacingAdded;
     bool mFragCoordAdded;
 
     ShHashFunction64 mHashFunction;
 };
 
 // Expand struct variables to flattened lists of split variables
 template <typename VarT>
 void ExpandVariables(const std::vector<VarT> &compact,
-                     std::vector<sh::ShaderVariable> *expanded);
+                     std::vector<ShaderVariable> *expanded);
+
+}
 
 #endif  // COMPILER_VARIABLE_INFO_H_
--- a/gfx/angle/src/compiler/translator/util.cpp
+++ b/gfx/angle/src/compiler/translator/util.cpp
@@ -315,49 +315,9 @@ void GetVariableTraverser::traverse(cons
     ASSERT(output);
     output->push_back(variable);
 }
 
 template void GetVariableTraverser::traverse(const TType &, const TString &, std::vector<Uniform> *);
 template void GetVariableTraverser::traverse(const TType &, const TString &, std::vector<Varying> *);
 template void GetVariableTraverser::traverse(const TType &, const TString &, std::vector<InterfaceBlockField> *);
 
-BlockLayoutType GetBlockLayoutType(TLayoutBlockStorage blockStorage)
-{
-    switch (blockStorage)
-    {
-      case EbsPacked:         return BLOCKLAYOUT_PACKED;
-      case EbsShared:         return BLOCKLAYOUT_SHARED;
-      case EbsStd140:         return BLOCKLAYOUT_STANDARD;
-      default: UNREACHABLE(); return BLOCKLAYOUT_SHARED;
-    }
 }
-
-static TString InterfaceBlockFieldName(const TInterfaceBlock &interfaceBlock, const TField &field)
-{
-    if (interfaceBlock.hasInstanceName())
-    {
-        return interfaceBlock.name() + "." + field.name();
-    }
-    else
-    {
-        return field.name();
-    }
-}
-
-void GetInterfaceBlockFields(const TInterfaceBlock &interfaceBlock, std::vector<InterfaceBlockField> *fieldsOut)
-{
-    const TFieldList &fieldList = interfaceBlock.fields();
-
-    for (size_t fieldIndex = 0; fieldIndex < fieldList.size(); ++fieldIndex)
-    {
-        const TField &field = *fieldList[fieldIndex];
-        const TString &fullFieldName = InterfaceBlockFieldName(interfaceBlock, field);
-        const TType &fieldType = *field.type();
-
-        GetVariableTraverser traverser;
-        traverser.traverse(fieldType, fullFieldName, fieldsOut);
-
-        fieldsOut->back().isRowMajorLayout = (fieldType.getLayoutQualifier().matrixPacking == EmpRowMajor);
-    }
-}
-
-}
--- a/gfx/angle/src/compiler/translator/util.h
+++ b/gfx/angle/src/compiler/translator/util.h
@@ -28,17 +28,16 @@ namespace sh
 {
 
 GLenum GLVariableType(const TType &type);
 GLenum GLVariablePrecision(const TType &type);
 bool IsVaryingIn(TQualifier qualifier);
 bool IsVaryingOut(TQualifier qualifier);
 bool IsVarying(TQualifier qualifier);
 InterpolationType GetInterpolationType(TQualifier qualifier);
-BlockLayoutType GetBlockLayoutType(TLayoutBlockStorage blockStorage);
 TString ArrayString(const TType &type);
 
 class GetVariableTraverser
 {
   public:
     GetVariableTraverser() {}
 
     template <typename VarT>
@@ -47,13 +46,11 @@ class GetVariableTraverser
   protected:
     // May be overloaded
     virtual void visitVariable(ShaderVariable *newVar) {}
 
   private:
     DISALLOW_COPY_AND_ASSIGN(GetVariableTraverser);
 };
 
-void GetInterfaceBlockFields(const TInterfaceBlock &interfaceBlock, std::vector<InterfaceBlockField> *fieldsOut);
-
 }
 
 #endif // COMPILER_UTIL_H
--- a/gfx/angle/src/libEGL.gypi
+++ b/gfx/angle/src/libEGL.gypi
@@ -29,21 +29,23 @@
             'common/angleutils.h',
             'common/debug.cpp',
             'common/debug.h',
             'common/event_tracer.cpp',
             'common/event_tracer.h',
             'common/mathutil.cpp',
             'common/mathutil.h',
             'common/platform.h',
+            'common/NativeWindow.h',
             'common/tls.cpp',
             'common/tls.h',
             'common/utilities.cpp',
             'common/utilities.h',
             'common/version.h',
+            'common/win32/NativeWindow.cpp',
             'libEGL/Config.cpp',
             'libEGL/Config.h',
             'libEGL/Display.cpp',
             'libEGL/Display.h',
             'libEGL/Surface.cpp',
             'libEGL/Surface.h',
             'libEGL/libEGL.cpp',
             'libEGL/libEGL.def',
--- a/gfx/angle/src/libEGL/Display.cpp
+++ b/gfx/angle/src/libEGL/Display.cpp
@@ -188,17 +188,17 @@ bool Display::getConfigAttrib(EGLConfig 
         return false;
     }
 
     return true;
 }
 
 
 
-EGLSurface Display::createWindowSurface(HWND window, EGLConfig config, const EGLint *attribList)
+EGLSurface Display::createWindowSurface(EGLNativeWindowType window, EGLConfig config, const EGLint *attribList)
 {
     const Config *configuration = mConfigSet.get(config);
     EGLint postSubBufferSupported = EGL_FALSE;
 
     EGLint width = 0;
     EGLint height = 0;
     EGLint fixedSize = EGL_FALSE;
 
@@ -489,17 +489,17 @@ bool Display::isValidContext(gl::Context
     return mContextSet.find(context) != mContextSet.end();
 }
 
 bool Display::isValidSurface(egl::Surface *surface)
 {
     return mSurfaceSet.find(surface) != mSurfaceSet.end();
 }
 
-bool Display::hasExistingWindowSurface(HWND window)
+bool Display::hasExistingWindowSurface(EGLNativeWindowType window)
 {
     for (SurfaceSet::iterator surface = mSurfaceSet.begin(); surface != mSurfaceSet.end(); surface++)
     {
         if ((*surface)->getWindowHandle() == window)
         {
             return true;
         }
     }
@@ -547,18 +547,20 @@ void Display::initDisplayExtensionString
     extensions.push_back("EGL_ANGLE_query_surface_pointer");
     extensions.push_back("EGL_ANGLE_window_fixed_size");
 
     if (mRenderer->getPostSubBufferSupport())
     {
         extensions.push_back("EGL_NV_post_sub_buffer");
     }
 
+#if defined (ANGLE_TEST_CONFIG)
     // TODO: complete support for the EGL_KHR_create_context extension
     extensions.push_back("EGL_KHR_create_context");
+#endif
 
     std::ostringstream stream;
     std::copy(extensions.begin(), extensions.end(), std::ostream_iterator<std::string>(stream, " "));
     mDisplayExtensionString = stream.str();
 }
 
 const char *Display::getExtensionString(egl::Display *display)
 {
--- a/gfx/angle/src/libEGL/Display.h
+++ b/gfx/angle/src/libEGL/Display.h
@@ -38,28 +38,28 @@ class Display
     static const char *getExtensionString(egl::Display *display);
 
     static bool supportsPlatformD3D();
     static bool supportsPlatformOpenGL();
 
     bool getConfigs(EGLConfig *configs, const EGLint *attribList, EGLint configSize, EGLint *numConfig);
     bool getConfigAttrib(EGLConfig config, EGLint attribute, EGLint *value);
 
-    EGLSurface createWindowSurface(HWND window, EGLConfig config, const EGLint *attribList);
+    EGLSurface createWindowSurface(EGLNativeWindowType window, EGLConfig config, const EGLint *attribList);
     EGLSurface createOffscreenSurface(EGLConfig config, HANDLE shareHandle, const EGLint *attribList);
     EGLContext createContext(EGLConfig configHandle, EGLint clientVersion, const gl::Context *shareContext, bool notifyResets, bool robustAccess);
 
     void destroySurface(egl::Surface *surface);
     void destroyContext(gl::Context *context);
 
     bool isInitialized() const;
     bool isValidConfig(EGLConfig config);
     bool isValidContext(gl::Context *context);
     bool isValidSurface(egl::Surface *surface);
-    bool hasExistingWindowSurface(HWND window);
+    bool hasExistingWindowSurface(EGLNativeWindowType window);
 
     rx::Renderer *getRenderer() { return mRenderer; };
 
     // exported methods must be virtual
     virtual void notifyDeviceLost();
     virtual void recreateSwapChains();
 
     const char *getExtensionString() const;
--- a/gfx/angle/src/libEGL/Surface.cpp
+++ b/gfx/angle/src/libEGL/Surface.cpp
@@ -17,21 +17,23 @@
 #include "common/debug.h"
 #include "libGLESv2/Texture.h"
 #include "libGLESv2/renderer/SwapChain.h"
 #include "libGLESv2/main.h"
 
 #include "libEGL/main.h"
 #include "libEGL/Display.h"
 
+#include "common/NativeWindow.h"
+
 namespace egl
 {
 
-Surface::Surface(Display *display, const Config *config, HWND window, EGLint fixedSize, EGLint width, EGLint height, EGLint postSubBufferSupported) 
-    : mDisplay(display), mConfig(config), mWindow(window), mPostSubBufferSupported(postSubBufferSupported)
+Surface::Surface(Display *display, const Config *config, EGLNativeWindowType window, EGLint fixedSize, EGLint width, EGLint height, EGLint postSubBufferSupported) 
+    : mDisplay(display), mConfig(config), mNativeWindow(window), mPostSubBufferSupported(postSubBufferSupported)
 {
     mRenderer = mDisplay->getRenderer();
     mSwapChain = NULL;
     mShareHandle = NULL;
     mTexture = NULL;
     mTextureFormat = EGL_NO_TEXTURE;
     mTextureTarget = EGL_NO_TEXTURE;
 
@@ -43,17 +45,17 @@ Surface::Surface(Display *display, const
     mHeight = height;
     setSwapInterval(1);
     mFixedSize = fixedSize;
 
     subclassWindow();
 }
 
 Surface::Surface(Display *display, const Config *config, HANDLE shareHandle, EGLint width, EGLint height, EGLenum textureFormat, EGLenum textureType)
-    : mDisplay(display), mWindow(NULL), mConfig(config), mShareHandle(shareHandle), mWidth(width), mHeight(height), mPostSubBufferSupported(EGL_FALSE)
+    : mDisplay(display), mNativeWindow(NULL), mConfig(config), mShareHandle(shareHandle), mWidth(width), mHeight(height), mPostSubBufferSupported(EGL_FALSE)
 {
     mRenderer = mDisplay->getRenderer();
     mSwapChain = NULL;
     mWindowSubclassed = false;
     mTexture = NULL;
     mTextureFormat = textureFormat;
     mTextureTarget = textureType;
 
@@ -69,16 +71,24 @@ Surface::Surface(Display *display, const
 Surface::~Surface()
 {
     unsubclassWindow();
     release();
 }
 
 bool Surface::initialize()
 {
+    if (mNativeWindow.getNativeWindow())
+    {
+        if (!mNativeWindow.initialize())
+        {
+            return false;
+        }
+    }
+
     if (!resetSwapChain())
       return false;
 
     return true;
 }
 
 void Surface::release()
 {
@@ -97,17 +107,17 @@ bool Surface::resetSwapChain()
     ASSERT(!mSwapChain);
 
     int width;
     int height;
 
     if (!mFixedSize)
     {
         RECT windowRect;
-        if (!GetClientRect(getWindowHandle(), &windowRect))
+        if (!mNativeWindow.getClientRect(&windowRect))
         {
             ASSERT(false);
 
             ERR("Could not retrieve the window dimensions");
             return error(EGL_BAD_SURFACE, false);
         }
 
         width = windowRect.right - windowRect.left;
@@ -115,17 +125,17 @@ bool Surface::resetSwapChain()
     }
     else
     {
         // non-window surface - size is determined at creation
         width = mWidth;
         height = mHeight;
     }
 
-    mSwapChain = mRenderer->createSwapChain(mWindow, mShareHandle,
+    mSwapChain = mRenderer->createSwapChain(mNativeWindow, mShareHandle,
                                             mConfig->mRenderTargetFormat,
                                             mConfig->mDepthStencilFormat);
     if (!mSwapChain)
     {
         return error(EGL_BAD_ALLOC, false);
     }
 
     if (!resetSwapChain(width, height))
@@ -219,19 +229,19 @@ bool Surface::swapRect(EGLint x, EGLint 
         return error(status, false);
     }
 
     checkForOutOfDateSwapChain();
 
     return true;
 }
 
-HWND Surface::getWindowHandle()
+EGLNativeWindowType Surface::getWindowHandle()
 {
-    return mWindow;
+    return mNativeWindow.getNativeWindow();
 }
 
 
 #define kSurfaceProperty _TEXT("Egl::SurfaceOwner")
 #define kParentWndProc _TEXT("Egl::SurfaceParentWndProc")
 
 static LRESULT CALLBACK SurfaceWindowProc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam)
 {
@@ -244,79 +254,86 @@ static LRESULT CALLBACK SurfaceWindowPro
       }
   }
   WNDPROC prevWndFunc = reinterpret_cast<WNDPROC >(GetProp(hwnd, kParentWndProc));
   return CallWindowProc(prevWndFunc, hwnd, message, wparam, lparam);
 }
 
 void Surface::subclassWindow()
 {
-    if (!mWindow)
+    HWND window = mNativeWindow.getNativeWindow();
+    if (!window)
     {
         return;
     }
 
     DWORD processId;
-    DWORD threadId = GetWindowThreadProcessId(mWindow, &processId);
+    DWORD threadId = GetWindowThreadProcessId(window, &processId);
     if (processId != GetCurrentProcessId() || threadId != GetCurrentThreadId())
     {
         return;
     }
 
     SetLastError(0);
-    LONG_PTR oldWndProc = SetWindowLongPtr(mWindow, GWLP_WNDPROC, reinterpret_cast<LONG_PTR>(SurfaceWindowProc));
+    LONG_PTR oldWndProc = SetWindowLongPtr(window, GWLP_WNDPROC, reinterpret_cast<LONG_PTR>(SurfaceWindowProc));
     if(oldWndProc == 0 && GetLastError() != ERROR_SUCCESS)
     {
         mWindowSubclassed = false;
         return;
     }
 
-    SetProp(mWindow, kSurfaceProperty, reinterpret_cast<HANDLE>(this));
-    SetProp(mWindow, kParentWndProc, reinterpret_cast<HANDLE>(oldWndProc));
+    SetProp(window, kSurfaceProperty, reinterpret_cast<HANDLE>(this));
+    SetProp(window, kParentWndProc, reinterpret_cast<HANDLE>(oldWndProc));
     mWindowSubclassed = true;
 }
 
 void Surface::unsubclassWindow()
 {
     if(!mWindowSubclassed)
     {
         return;
     }
 
+    HWND window = mNativeWindow.getNativeWindow();
+    if (!window)
+    {
+        return;
+    }
+
     // un-subclass
-    LONG_PTR parentWndFunc = reinterpret_cast<LONG_PTR>(GetProp(mWindow, kParentWndProc));
+    LONG_PTR parentWndFunc = reinterpret_cast<LONG_PTR>(GetProp(window, kParentWndProc));
 
     // Check the windowproc is still SurfaceWindowProc.
     // If this assert fails, then it is likely the application has subclassed the
     // hwnd as well and did not unsubclass before destroying its EGL context. The
     // application should be modified to either subclass before initializing the
     // EGL context, or to unsubclass before destroying the EGL context.
     if(parentWndFunc)
     {
-        LONG_PTR prevWndFunc = SetWindowLongPtr(mWindow, GWLP_WNDPROC, parentWndFunc);
+        LONG_PTR prevWndFunc = SetWindowLongPtr(window, GWLP_WNDPROC, parentWndFunc);
         UNUSED_ASSERTION_VARIABLE(prevWndFunc);
         ASSERT(prevWndFunc == reinterpret_cast<LONG_PTR>(SurfaceWindowProc));
     }
 
-    RemoveProp(mWindow, kSurfaceProperty);
-    RemoveProp(mWindow, kParentWndProc);
+    RemoveProp(window, kSurfaceProperty);
+    RemoveProp(window, kParentWndProc);
     mWindowSubclassed = false;
 }
 
 bool Surface::checkForOutOfDateSwapChain()
 {
     RECT client;
     int clientWidth = getWidth();
     int clientHeight = getHeight();
     bool sizeDirty = false;
-    if (!mFixedSize && !IsIconic(getWindowHandle()))
+    if (!mFixedSize && !mNativeWindow.isIconic())
     {
         // The window is automatically resized to 150x22 when it's minimized, but the swapchain shouldn't be resized
         // because that's not a useful size to render to.
-        if (!GetClientRect(getWindowHandle(), &client))
+        if (!mNativeWindow.getClientRect(&client))
         {
             ASSERT(false);
             return false;
         }
 
         // Grow the buffer now, if the window has grown. We need to grow now to avoid losing information.
         clientWidth = client.right - client.left;
         clientHeight = client.bottom - client.top;
--- a/gfx/angle/src/libEGL/Surface.h
+++ b/gfx/angle/src/libEGL/Surface.h
@@ -9,16 +9,17 @@
 // Implements EGLSurface and related functionality. [EGL 1.4] section 2.2 page 3.
 
 #ifndef LIBEGL_SURFACE_H_
 #define LIBEGL_SURFACE_H_
 
 #include <EGL/egl.h>
 
 #include "common/angleutils.h"
+#include "common/NativeWindow.h"
 
 namespace gl
 {
 class Texture2D;
 }
 namespace rx
 {
 class Renderer;
@@ -28,26 +29,26 @@ class SwapChain;
 namespace egl
 {
 class Display;
 class Config;
 
 class Surface
 {
   public:
-    Surface(Display *display, const egl::Config *config, HWND window, EGLint fixedSize, EGLint width, EGLint height, EGLint postSubBufferSupported);
+    Surface(Display *display, const egl::Config *config, EGLNativeWindowType window, EGLint fixedSize, EGLint width, EGLint height, EGLint postSubBufferSupported);
     Surface(Display *display, const egl::Config *config, HANDLE shareHandle, EGLint width, EGLint height, EGLenum textureFormat, EGLenum textureTarget);
 
     virtual ~Surface();
 
     bool initialize();
     void release();
     bool resetSwapChain();
 
-    HWND getWindowHandle();
+    EGLNativeWindowType getWindowHandle();
     bool swap();
     bool postSubBuffer(EGLint x, EGLint y, EGLint width, EGLint height);
 
     virtual EGLint isPostSubBufferSupported() const;
 
     virtual rx::SwapChain *getSwapChain() const;
 
     void setSwapInterval(EGLint interval);
@@ -78,17 +79,17 @@ private:
     rx::SwapChain *mSwapChain;
 
     void subclassWindow();
     void unsubclassWindow();
     bool resizeSwapChain(int backbufferWidth, int backbufferHeight);
     bool resetSwapChain(int backbufferWidth, int backbufferHeight);
     bool swapRect(EGLint x, EGLint y, EGLint width, EGLint height);
 
-    const HWND mWindow;            // Window that the surface is created for.
+    rx::NativeWindow mNativeWindow;   // Handler for the Window that the surface is created for.
     bool mWindowSubclassed;        // Indicates whether we successfully subclassed mWindow for WM_RESIZE hooking
     const egl::Config *mConfig;    // EGL config surface was created with
     EGLint mHeight;                // Height of surface
     EGLint mWidth;                 // Width of surface
 //  EGLint horizontalResolution;   // Horizontal dot pitch
 //  EGLint verticalResolution;     // Vertical dot pitch
 //  EGLBoolean largestPBuffer;     // If true, create largest pbuffer possible
 //  EGLBoolean mipmapTexture;      // True if texture has mipmaps
--- a/gfx/angle/src/libEGL/libEGL.cpp
+++ b/gfx/angle/src/libEGL/libEGL.cpp
@@ -14,16 +14,18 @@
 #include "libGLESv2/Texture.h"
 #include "libGLESv2/main.h"
 #include "libGLESv2/renderer/SwapChain.h"
 
 #include "libEGL/main.h"
 #include "libEGL/Display.h"
 #include "libEGL/Surface.h"
 
+#include "common/NativeWindow.h"
+
 bool validateDisplay(egl::Display *display)
 {
     if (display == EGL_NO_DISPLAY)
     {
         return egl::error(EGL_BAD_DISPLAY, false);
     }
 
     if (!display->isInitialized())
@@ -319,24 +321,22 @@ EGLSurface __stdcall eglCreateWindowSurf
 
     egl::Display *display = static_cast<egl::Display*>(dpy);
 
     if (!validateConfig(display, config))
     {
         return EGL_NO_SURFACE;
     }
 
-    HWND window = (HWND)win;
-
-    if (!IsWindow(window))
+    if (!isValidEGLNativeWindowType(win))
     {
         return egl::error(EGL_BAD_NATIVE_WINDOW, EGL_NO_SURFACE);
     }
 
-    return display->createWindowSurface(window, config, attrib_list);
+    return display->createWindowSurface(win, config, attrib_list);
 }
 
 EGLSurface __stdcall eglCreatePbufferSurface(EGLDisplay dpy, EGLConfig config, const EGLint *attrib_list)
 {
     EVENT("(EGLDisplay dpy = 0x%0.8p, EGLConfig config = 0x%0.8p, const EGLint *attrib_list = 0x%0.8p)",
           dpy, config, attrib_list);
 
     egl::Display *display = static_cast<egl::Display*>(dpy);
--- a/gfx/angle/src/libEGL/moz.build
+++ b/gfx/angle/src/libEGL/moz.build
@@ -4,16 +4,17 @@
 UNIFIED_SOURCES += [
     '../common/angleutils.cpp',
     '../common/debug.cpp',
     '../common/event_tracer.cpp',
     '../common/mathutil.cpp',
     '../common/RefCountObject.cpp',
     '../common/tls.cpp',
     '../common/utilities.cpp',
+    '../common/win32/NativeWindow.cpp',
     'Config.cpp',
     'Display.cpp',
     'libEGL.cpp',
     'main.cpp',
     'Surface.cpp',
 ]
 
 
@@ -49,13 +50,14 @@ DEFINES['GL_GLEXT_PROTOTYPES'] = ""
 DEFINES['EGLAPI'] = ""
 
 # ANGLE uses the STL, so we can't use our derpy STL wrappers.
 DISABLE_STL_WRAPPING = True
 
 
 LOCAL_INCLUDES += [ '../../include', '../../src' ]
 USE_LIBS += [ 'libGLESv2' ]
+EXTRA_DSO_LDOPTS += [ '../libGLESv2/libGLESv2.lib' ]
 
 SharedLibrary('libEGL')
 
 RCFILE = SRCDIR + '/libEGL.rc'
 DEFFILE = SRCDIR + '/libEGL.def'
--- a/gfx/angle/src/libGLESv2.gypi
+++ b/gfx/angle/src/libGLESv2.gypi
@@ -3,17 +3,17 @@
 # found in the LICENSE file.
 
 {
     'variables':
     {
         'angle_enable_d3d9%': 1,
         'angle_enable_d3d11%': 1,
         # These file lists are shared with the GN build.
-        'angle_libglesv2_sources':
+        'angle_libangle_sources':
         [
             '../include/EGL/egl.h',
             '../include/EGL/eglext.h',
             '../include/EGL/eglplatform.h',
             '../include/GLES2/gl2.h',
             '../include/GLES2/gl2ext.h',
             '../include/GLES2/gl2platform.h',
             '../include/GLES3/gl3.h',
@@ -31,21 +31,23 @@
             'common/blocklayout.h',
             'common/debug.cpp',
             'common/debug.h',
             'common/event_tracer.cpp',
             'common/event_tracer.h',
             'common/mathutil.cpp',
             'common/mathutil.h',
             'common/platform.h',
+            'common/NativeWindow.h',
             'common/tls.cpp',
             'common/tls.h',
             'common/utilities.cpp',
             'common/utilities.h',
             'common/version.h',
+            'common/win32/NativeWindow.cpp',
             'libGLESv2/BinaryStream.h',
             'libGLESv2/Buffer.cpp',
             'libGLESv2/Buffer.h',
             'libGLESv2/Caps.cpp',
             'libGLESv2/Caps.h',
             'libGLESv2/Constants.h',
             'libGLESv2/Context.cpp',
             'libGLESv2/Context.h',
@@ -55,16 +57,18 @@
             'libGLESv2/Fence.h',
             'libGLESv2/Float16ToFloat32.cpp',
             'libGLESv2/Framebuffer.cpp',
             'libGLESv2/Framebuffer.h',
             'libGLESv2/FramebufferAttachment.cpp',
             'libGLESv2/FramebufferAttachment.h',
             'libGLESv2/HandleAllocator.cpp',
             'libGLESv2/HandleAllocator.h',
+            'libGLESv2/ImageIndex.h',
+            'libGLESv2/ImageIndex.cpp',
             'libGLESv2/Program.cpp',
             'libGLESv2/Program.h',
             'libGLESv2/ProgramBinary.cpp',
             'libGLESv2/ProgramBinary.h',
             'libGLESv2/Query.cpp',
             'libGLESv2/Query.h',
             'libGLESv2/Renderbuffer.cpp',
             'libGLESv2/Renderbuffer.h',
@@ -85,39 +89,38 @@
             'libGLESv2/VertexArray.cpp',
             'libGLESv2/VertexArray.h',
             'libGLESv2/VertexAttribute.cpp',
             'libGLESv2/VertexAttribute.h',
             'libGLESv2/angletypes.cpp',
             'libGLESv2/angletypes.h',
             'libGLESv2/formatutils.cpp',
             'libGLESv2/formatutils.h',
-            'libGLESv2/libGLESv2.cpp',
-            'libGLESv2/libGLESv2.def',
-            'libGLESv2/libGLESv2.rc',
             'libGLESv2/main.cpp',
             'libGLESv2/main.h',
             'libGLESv2/queryconversions.cpp',
             'libGLESv2/queryconversions.h',
             'libGLESv2/renderer/BufferImpl.h',
             'libGLESv2/renderer/FenceImpl.h',
             'libGLESv2/renderer/Image.cpp',
             'libGLESv2/renderer/Image.h',
             'libGLESv2/renderer/IndexRangeCache.cpp',
             'libGLESv2/renderer/IndexRangeCache.h',
+            'libGLESv2/renderer/ProgramImpl.h',
             'libGLESv2/renderer/QueryImpl.h',
             'libGLESv2/renderer/RenderTarget.h',
             'libGLESv2/renderer/Renderer.cpp',
             'libGLESv2/renderer/Renderer.h',
             'libGLESv2/renderer/ShaderExecutable.h',
             'libGLESv2/renderer/ShaderImpl.h',
             'libGLESv2/renderer/SwapChain.h',
             'libGLESv2/renderer/TextureImpl.h',
             'libGLESv2/renderer/TransformFeedbackImpl.h',
             'libGLESv2/renderer/VertexArrayImpl.h',
+            'libGLESv2/renderer/Workarounds.h',
             'libGLESv2/renderer/copyimage.cpp',
             'libGLESv2/renderer/copyimage.h',
             'libGLESv2/renderer/copyimage.inl',
             'libGLESv2/renderer/copyvertex.h',
             'libGLESv2/renderer/copyvertex.inl',
             'libGLESv2/renderer/generatemip.h',
             'libGLESv2/renderer/generatemip.inl',
             'libGLESv2/renderer/imageformats.h',
@@ -149,16 +152,18 @@
             'libGLESv2/renderer/d3d/ImageD3D.cpp',
             'libGLESv2/renderer/d3d/ImageD3D.h',
             'libGLESv2/renderer/d3d/IndexBuffer.cpp',
             'libGLESv2/renderer/d3d/IndexBuffer.h',
             'libGLESv2/renderer/d3d/IndexDataManager.cpp',
             'libGLESv2/renderer/d3d/IndexDataManager.h',
             'libGLESv2/renderer/d3d/MemoryBuffer.cpp',
             'libGLESv2/renderer/d3d/MemoryBuffer.h',
+            'libGLESv2/renderer/d3d/ProgramD3D.cpp',
+            'libGLESv2/renderer/d3d/ProgramD3D.h',
             'libGLESv2/renderer/d3d/ShaderD3D.cpp',
             'libGLESv2/renderer/d3d/ShaderD3D.h',
             'libGLESv2/renderer/d3d/TextureD3D.cpp',
             'libGLESv2/renderer/d3d/TextureD3D.h',
             'libGLESv2/renderer/d3d/TextureStorage.cpp',
             'libGLESv2/renderer/d3d/TextureStorage.h',
             'libGLESv2/renderer/d3d/TransformFeedbackD3D.cpp',
             'libGLESv2/renderer/d3d/TransformFeedbackD3D.h',
@@ -303,37 +308,54 @@
     # anything also change angle/BUILD.gn
     'conditions':
     [
         ['OS=="win"',
         {
             'targets':
             [
                 {
-                    'target_name': 'libGLESv2',
-                    'type': 'shared_library',
+                    'target_name': 'libANGLE',
+                    #TODO(jamdill/geofflang): support shared
+                    'type': 'static_library',
                     'dependencies': [ 'translator', 'commit_id', 'copy_compiler_dll' ],
                     'includes': [ '../build/common_defines.gypi', ],
                     'include_dirs':
                     [
                         '.',
                         '../include',
                         'libGLESv2',
                     ],
                     'sources':
                     [
-                        '<@(angle_libglesv2_sources)',
+                        '<@(angle_libangle_sources)',
                     ],
                     'defines':
                     [
                         'GL_APICALL=',
                         'GL_GLEXT_PROTOTYPES=',
                         'EGLAPI=',
                         'ANGLE_PRELOADED_D3DCOMPILER_MODULE_NAMES={ "d3dcompiler_46.dll", "d3dcompiler_43.dll" }',
                     ],
+                    'direct_dependent_settings':
+                    {
+                        'include_dirs':
+                        [
+                            '.',
+                            '../include',
+                            'libGLESv2',
+                        ],
+                        'defines':
+                        [
+                            'GL_APICALL=',
+                            'GL_GLEXT_PROTOTYPES=',
+                            'EGLAPI=',
+                            'ANGLE_PRELOADED_D3DCOMPILER_MODULE_NAMES={ "d3dcompiler_46.dll", "d3dcompiler_43.dll" }',
+                        ],
+                    },
                     'conditions':
                     [
                         ['angle_enable_d3d9==1 or angle_enable_d3d11==1',
                         {
                             'sources':
                             [
                                 '<@(angle_d3d_shared_sources)',
                             ],
@@ -343,45 +365,51 @@
                             'sources':
                             [
                                 '<@(angle_d3d9_sources)',
                             ],
                             'defines':
                             [
                                 'ANGLE_ENABLE_D3D9',
                             ],
-                            'msvs_settings':
+                            'link_settings':
                             {
-                                'VCLinkerTool':
+                                'msvs_settings':
                                 {
-                                    'AdditionalDependencies':
-                                    [
-                                        'd3d9.lib',
-                                    ]
-                                }
+                                    'VCLinkerTool':
+                                    {
+                                        'AdditionalDependencies':
+                                        [
+                                            'd3d9.lib',
+                                        ]
+                                    }
+                                },
                             },
                         }],
                         ['angle_enable_d3d11==1',
                         {
                             'sources':
                             [
                                 '<@(angle_d3d11_sources)',
                             ],
                             'defines':
                             [
                                 'ANGLE_ENABLE_D3D11',
                             ],
-                            'msvs_settings':
+                            'link_settings':
                             {
-                                'VCLinkerTool':
+                                'msvs_settings':
                                 {
-                                    'AdditionalDependencies':
-                                    [
-                                        'dxguid.lib',
-                                    ],
+                                    'VCLinkerTool':
+                                    {
+                                        'AdditionalDependencies':
+                                        [
+                                            'dxguid.lib',
+                                        ]
+                                    }
                                 },
                             },
                         }],
                     ],
 
                     'configurations':
                     {
                         'Debug':
@@ -399,39 +427,37 @@
                                         'd3d9.lib',
                                     ]
                                 }
                             },
                         },
                     },
                 },
                 {
-                    # This target supports angle_implementation_unittests.
-                    # It only executes cross-platform code and therefore
-                    # doesn't need any Direct3D DLLs.
+                    'target_name': 'libGLESv2',
+                    'type': 'shared_library',
+                    'dependencies': [ 'libANGLE' ],
+                    'includes': [ '../build/common_defines.gypi', ],
+                    'sources':
+                    [
+                        'libGLESv2/libGLESv2.cpp',
+                        'libGLESv2/libGLESv2.def',
+                        'libGLESv2/libGLESv2.rc',
+                    ],
+                },
+                {
                     'target_name': 'libGLESv2_static',
                     'type': 'static_library',
-                    'dependencies': [ 'translator', 'commit_id' ],
+                    # make sure we depend on commit_id as a hard dependency, otherwise
+                    # we will try to build the static_lib in parallel
+                    'dependencies': [ 'libANGLE', 'commit_id' ],
                     'includes': [ '../build/common_defines.gypi', ],
-                    'include_dirs':
-                    [
-                        '.',
-                        '../include',
-                        'libGLESv2',
-                    ],
                     'sources':
                     [
-                        '<@(angle_libglesv2_sources)',
-                    ],
-                    'defines':
-                    [
-                        'GL_APICALL=',
-                        'GL_GLEXT_PROTOTYPES=',
-                        'EGLAPI=',
-                        # Workaround for D3D-specific code in Renderer.h
-                        'ANGLE_COMPILE_OPTIMIZATION_LEVEL=0',
+                        'libGLESv2/libGLESv2.cpp',
+                        'libGLESv2/libGLESv2.rc',
                     ],
                 },
             ],
         },
         ],
     ],
 }
--- a/gfx/angle/src/libGLESv2/BinaryStream.h
+++ b/gfx/angle/src/libGLESv2/BinaryStream.h
@@ -10,28 +10,29 @@
 #define LIBGLESV2_BINARYSTREAM_H_
 
 #include "common/angleutils.h"
 #include "common/mathutil.h"
 
 #include <cstddef>
 #include <string>
 #include <vector>
+#include <stdint.h>
 
 namespace gl
 {
 
 class BinaryInputStream
 {
   public:
     BinaryInputStream(const void *data, size_t length)
     {
         mError = false;
         mOffset = 0;
-        mData = static_cast<const char*>(data);
+        mData = static_cast<const uint8_t*>(data);
         mLength = length;
     }
 
     // readInt will generate an error for bool types
     template <class IntT>
     IntT readInt()
     {
         int value;
@@ -80,17 +81,17 @@ class BinaryInputStream
         }
 
         if (mOffset + length > mLength)
         {
             mError = true;
             return;
         }
 
-        v->assign(mData + mOffset, length);
+        v->assign(reinterpret_cast<const char *>(mData) + mOffset, length);
         mOffset += length;
     }
 
     void skip(size_t length)
     {
         if (mOffset + length > mLength)
         {
             mError = true;
@@ -110,21 +111,26 @@ class BinaryInputStream
         return mError;
     }
 
     bool endOfStream() const
     {
         return mOffset == mLength;
     }
 
+    const uint8_t *data()
+    {
+        return mData;
+    }
+
   private:
     DISALLOW_COPY_AND_ASSIGN(BinaryInputStream);
     bool mError;
     size_t mOffset;
-    const char *mData;
+    const uint8_t *mData;
     size_t mLength;
 
     template <typename T>
     void read(T *v, size_t num)
     {
         META_ASSERT(std::is_fundamental<T>::value);
 
         size_t length = num * sizeof(T);
--- a/gfx/angle/src/libGLESv2/Buffer.cpp
+++ b/gfx/angle/src/libGLESv2/Buffer.cpp
@@ -28,68 +28,99 @@ Buffer::Buffer(rx::BufferImpl *impl, GLu
 {
 }
 
 Buffer::~Buffer()
 {
     SafeDelete(mBuffer);
 }
 
-void Buffer::bufferData(const void *data, GLsizeiptr size, GLenum usage)
+Error Buffer::bufferData(const void *data, GLsizeiptr size, GLenum usage)
 {
+    gl::Error error = mBuffer->setData(data, size, usage);
+    if (error.isError())
+    {
+        return error;
+    }
+
     mIndexRangeCache.clear();
     mUsage = usage;
     mSize = size;
-    mBuffer->setData(data, size, usage);
+
+    return error;
 }
 
-void Buffer::bufferSubData(const void *data, GLsizeiptr size, GLintptr offset)
+Error Buffer::bufferSubData(const void *data, GLsizeiptr size, GLintptr offset)
 {
+    gl::Error error = mBuffer->setSubData(data, size, offset);
+    if (error.isError())
+    {
+        return error;
+    }
+
     mIndexRangeCache.invalidateRange(offset, size);
-    mBuffer->setSubData(data, size, offset);
+
+    return error;
 }
 
-void Buffer::copyBufferSubData(Buffer* source, GLintptr sourceOffset, GLintptr destOffset, GLsizeiptr size)
+Error Buffer::copyBufferSubData(Buffer* source, GLintptr sourceOffset, GLintptr destOffset, GLsizeiptr size)
 {
+    gl::Error error = mBuffer->copySubData(source->getImplementation(), sourceOffset, destOffset, size);
+    if (error.isError())
+    {
+        return error;
+    }
+
     mIndexRangeCache.invalidateRange(destOffset, size);
-    mBuffer->copySubData(source->getImplementation(), sourceOffset, destOffset, size);
+
+    return error;
 }
 
-GLvoid *Buffer::mapRange(GLintptr offset, GLsizeiptr length, GLbitfield access)
+Error Buffer::mapRange(GLintptr offset, GLsizeiptr length, GLbitfield access)
 {
     ASSERT(!mMapped);
     ASSERT(offset + length <= mSize);
 
-    void *dataPointer = mBuffer->map(offset, length, access);
+    Error error = mBuffer->map(offset, length, access, &mMapPointer);
+    if (error.isError())
+    {
+        mMapPointer = NULL;
+        return error;
+    }
 
     mMapped = GL_TRUE;
-    mMapPointer = static_cast<GLvoid*>(static_cast<GLubyte*>(dataPointer));
     mMapOffset = static_cast<GLint64>(offset);
     mMapLength = static_cast<GLint64>(length);
     mAccessFlags = static_cast<GLint>(access);
 
     if ((access & GL_MAP_WRITE_BIT) > 0)
     {
         mIndexRangeCache.invalidateRange(offset, length);
     }
 
-    return mMapPointer;
+    return error;
 }
 
-void Buffer::unmap()
+Error Buffer::unmap()
 {
     ASSERT(mMapped);
 
-    mBuffer->unmap();
+    Error error = mBuffer->unmap();
+    if (error.isError())
+    {
+        return error;
+    }
 
     mMapped = GL_FALSE;
     mMapPointer = NULL;
     mMapOffset = 0;
     mMapLength = 0;
     mAccessFlags = 0;
+
+    return error;
 }
 
 void Buffer::markTransformFeedbackUsage()
 {
     // TODO: Only used by the DX11 backend. Refactor to a more appropriate place.
     mBuffer->markTransformFeedbackUsage();
     mIndexRangeCache.clear();
 }
--- a/gfx/angle/src/libGLESv2/Buffer.h
+++ b/gfx/angle/src/libGLESv2/Buffer.h
@@ -6,16 +6,18 @@
 
 // Buffer.h: Defines the gl::Buffer class, representing storage of vertex and/or
 // index data. Implements GL buffer objects and related functionality.
 // [OpenGL ES 2.0.24] section 2.9 page 21.
 
 #ifndef LIBGLESV2_BUFFER_H_
 #define LIBGLESV2_BUFFER_H_
 
+#include "libGLESv2/Error.h"
+
 #include "common/angleutils.h"
 #include "common/RefCountObject.h"
 #include "libGLESv2/renderer/IndexRangeCache.h"
 
 namespace rx
 {
 class Renderer;
 class BufferImpl;
@@ -26,23 +28,23 @@ namespace gl
 
 class Buffer : public RefCountObject
 {
   public:
     Buffer(rx::BufferImpl *impl, GLuint id);
 
     virtual ~Buffer();
 
-    void bufferData(const void *data, GLsizeiptr size, GLenum usage);
-    void bufferSubData(const void *data, GLsizeiptr size, GLintptr offset);
-    void copyBufferSubData(Buffer* source, GLintptr sourceOffset, GLintptr destOffset, GLsizeiptr size);
-    GLvoid *mapRange(GLintptr offset, GLsizeiptr length, GLbitfield access);
-    void unmap();
+    Error bufferData(const void *data, GLsizeiptr size, GLenum usage);
+    Error bufferSubData(const void *data, GLsizeiptr size, GLintptr offset);
+    Error copyBufferSubData(Buffer* source, GLintptr sourceOffset, GLintptr destOffset, GLsizeiptr size);
+    Error mapRange(GLintptr offset, GLsizeiptr length, GLbitfield access);
+    Error unmap();
 
-    GLenum  getUsage() const { return mUsage; }
+    GLenum getUsage() const { return mUsage; }
     GLint getAccessFlags() const {  return mAccessFlags; }
     GLboolean isMapped() const { return mMapped; }
     GLvoid *getMapPointer() const { return mMapPointer; }
     GLint64 getMapOffset() const { return mMapOffset; }
     GLint64 getMapLength() const { return mMapLength; }
     GLint64 getSize() const { return mSize; }
 
     rx::BufferImpl *getImplementation() const { return mBuffer; }
--- a/gfx/angle/src/libGLESv2/Caps.cpp
+++ b/gfx/angle/src/libGLESv2/Caps.cpp
@@ -177,22 +177,27 @@ std::vector<std::string> Extensions::get
     InsertExtensionString("GL_ANGLE_texture_usage",            textureUsage,             &extensionStrings);
     InsertExtensionString("GL_ANGLE_translated_shader_source", translatedShaderSource,   &extensionStrings);
     InsertExtensionString("GL_EXT_color_buffer_float",         colorBufferFloat,         &extensionStrings);
 
     return extensionStrings;
 }
 
 static bool GetFormatSupport(const TextureCapsMap &textureCaps, const std::vector<GLenum> &requiredFormats,
-                             bool requiresFiltering, bool requiresRendering)
+                             bool requiresTexturing, bool requiresFiltering, bool requiresRendering)
 {
     for (size_t i = 0; i < requiredFormats.size(); i++)
     {
         const TextureCaps &cap = textureCaps.get(requiredFormats[i]);
 
+        if (requiresTexturing && !cap.texturable)
+        {
+            return false;
+        }
+
         if (requiresFiltering && !cap.filterable)
         {
             return false;
         }
 
         if (requiresRendering && !cap.renderable)
         {
             return false;
@@ -204,66 +209,66 @@ static bool GetFormatSupport(const Textu
 
 // Checks for GL_OES_rgb8_rgba8 support
 static bool DetermineRGB8AndRGBA8TextureSupport(const TextureCapsMap &textureCaps)
 {
     std::vector<GLenum> requiredFormats;
     requiredFormats.push_back(GL_RGB8);
     requiredFormats.push_back(GL_RGBA8);
 
-    return GetFormatSupport(textureCaps, requiredFormats, true, true);
+    return GetFormatSupport(textureCaps, requiredFormats, true, true, true);
 }
 
 // Checks for GL_EXT_texture_format_BGRA8888 support
 static bool DetermineBGRA8TextureSupport(const TextureCapsMap &textureCaps)
 {
     std::vector<GLenum> requiredFormats;
     requiredFormats.push_back(GL_BGRA8_EXT);
 
-    return GetFormatSupport(textureCaps, requiredFormats, true, true);
+    return GetFormatSupport(textureCaps, requiredFormats, true, true, true);
 }
 
 // Checks for GL_OES_texture_half_float support
 static bool DetermineHalfFloatTextureSupport(const TextureCapsMap &textureCaps)
 {
     std::vector<GLenum> requiredFormats;
     requiredFormats.push_back(GL_RGB16F);
     requiredFormats.push_back(GL_RGBA16F);
 
-    return GetFormatSupport(textureCaps, requiredFormats, false, true);
+    return GetFormatSupport(textureCaps, requiredFormats, true, false, true);
 }
 
 // Checks for GL_OES_texture_half_float_linear support
 static bool DetermineHalfFloatTextureFilteringSupport(const TextureCapsMap &textureCaps)
 {
     std::vector<GLenum> requiredFormats;
     requiredFormats.push_back(GL_RGB16F);
     requiredFormats.push_back(GL_RGBA16F);
 
-    return GetFormatSupport(textureCaps, requiredFormats, true, false);
+    return GetFormatSupport(textureCaps, requiredFormats, true, true, false);
 }
 
 // Checks for GL_OES_texture_float support
 static bool DetermineFloatTextureSupport(const TextureCapsMap &textureCaps)
 {
     std::vector<GLenum> requiredFormats;
     requiredFormats.push_back(GL_RGB32F);
     requiredFormats.push_back(GL_RGBA32F);
 
-    return GetFormatSupport(textureCaps, requiredFormats, false, true);
+    return GetFormatSupport(textureCaps, requiredFormats, true, false, true);
 }
 
 // Checks for GL_OES_texture_float_linear support
 static bool DetermineFloatTextureFilteringSupport(const TextureCapsMap &textureCaps)
 {
     std::vector<GLenum> requiredFormats;
     requiredFormats.push_back(GL_RGB32F);
     requiredFormats.push_back(GL_RGBA32F);
 
-    return GetFormatSupport(textureCaps, requiredFormats, true, false);
+    return GetFormatSupport(textureCaps, requiredFormats, true, true, false);
 }
 
 // Checks for GL_EXT_texture_rg support
 static bool DetermineRGTextureSupport(const TextureCapsMap &textureCaps, bool checkHalfFloatFormats, bool checkFloatFormats)
 {
     std::vector<GLenum> requiredFormats;
     requiredFormats.push_back(GL_R8);
     requiredFormats.push_back(GL_RG8);
@@ -273,85 +278,85 @@ static bool DetermineRGTextureSupport(co
         requiredFormats.push_back(GL_RG16F);
     }
     if (checkFloatFormats)
     {
         requiredFormats.push_back(GL_R32F);
         requiredFormats.push_back(GL_RG32F);
     }
 
-    return GetFormatSupport(textureCaps, requiredFormats, true, false);
+    return GetFormatSupport(textureCaps, requiredFormats, true, true, false);
 }
 
 // Check for GL_EXT_texture_compression_dxt1
 static bool DetermineDXT1TextureSupport(const TextureCapsMap &textureCaps)
 {
     std::vector<GLenum> requiredFormats;
     requiredFormats.push_back(GL_COMPRESSED_RGB_S3TC_DXT1_EXT);
     requiredFormats.push_back(GL_COMPRESSED_RGBA_S3TC_DXT1_EXT);
 
-    return GetFormatSupport(textureCaps, requiredFormats, true, false);
+    return GetFormatSupport(textureCaps, requiredFormats, true, true, false);
 }
 
 // Check for GL_ANGLE_texture_compression_dxt3
 static bool DetermineDXT3TextureSupport(const TextureCapsMap &textureCaps)
 {
     std::vector<GLenum> requiredFormats;
     requiredFormats.push_back(GL_COMPRESSED_RGBA_S3TC_DXT3_ANGLE);
 
-    return GetFormatSupport(textureCaps, requiredFormats, true, false);
+    return GetFormatSupport(textureCaps, requiredFormats, true, true, false);
 }
 
 // Check for GL_ANGLE_texture_compression_dxt5
 static bool DetermineDXT5TextureSupport(const TextureCapsMap &textureCaps)
 {
     std::vector<GLenum> requiredFormats;
     requiredFormats.push_back(GL_COMPRESSED_RGBA_S3TC_DXT5_ANGLE);
 
-    return GetFormatSupport(textureCaps, requiredFormats, true, false);
+    return GetFormatSupport(textureCaps, requiredFormats, true, true, false);
 }
 
 // Check for GL_ANGLE_texture_compression_dxt5
 static bool DetermineSRGBTextureSupport(const TextureCapsMap &textureCaps)
 {
     std::vector<GLenum> requiredFilterFormats;
     requiredFilterFormats.push_back(GL_SRGB8);
     requiredFilterFormats.push_back(GL_SRGB8_ALPHA8);
 
     std::vector<GLenum> requiredRenderFormats;
     requiredRenderFormats.push_back(GL_SRGB8_ALPHA8);
 
-    return GetFormatSupport(textureCaps, requiredFilterFormats, true, false) &&
-           GetFormatSupport(textureCaps, requiredRenderFormats, false, true);
+    return GetFormatSupport(textureCaps, requiredFilterFormats, true, true, false) &&
+           GetFormatSupport(textureCaps, requiredRenderFormats, true, false, true);
 }
 
 // Check for GL_ANGLE_depth_texture
 static bool DetermineDepthTextureSupport(const TextureCapsMap &textureCaps)
 {
     std::vector<GLenum> requiredFormats;
     requiredFormats.push_back(GL_DEPTH_COMPONENT16);
     requiredFormats.push_back(GL_DEPTH_COMPONENT32_OES);
     requiredFormats.push_back(GL_DEPTH24_STENCIL8_OES);
 
-    return GetFormatSupport(textureCaps, requiredFormats, true, true);
+    return GetFormatSupport(textureCaps, requiredFormats, true, true, true);
 }
 
 // Check for GL_EXT_color_buffer_float
 static bool DetermineColorBufferFloatSupport(const TextureCapsMap &textureCaps)
 {
     std::vector<GLenum> requiredFormats;
     requiredFormats.push_back(GL_R16F);
     requiredFormats.push_back(GL_RG16F);
     requiredFormats.push_back(GL_RGBA16F);
     requiredFormats.push_back(GL_R32F);
     requiredFormats.push_back(GL_RG32F);
     requiredFormats.push_back(GL_RGBA32F);
     requiredFormats.push_back(GL_R11F_G11F_B10F);
 
-    return GetFormatSupport(textureCaps, requiredFormats, false, true);
+    return GetFormatSupport(textureCaps, requiredFormats, true, false, true);
 }
 
 void Extensions::setTextureExtensionSupport(const TextureCapsMap &textureCaps)
 {
     rgb8rgba8 = DetermineRGB8AndRGBA8TextureSupport(textureCaps);
     textureFormatBGRA8888 = DetermineBGRA8TextureSupport(textureCaps);
     textureHalfFloat = DetermineHalfFloatTextureSupport(textureCaps);
     textureHalfFloatLinear = DetermineHalfFloatTextureFilteringSupport(textureCaps);
--- a/gfx/angle/src/libGLESv2/Constants.h
+++ b/gfx/angle/src/libGLESv2/Constants.h
@@ -10,22 +10,18 @@
 #define LIBGLESV2_CONSTANTS_H_
 
 namespace gl
 {
 
 enum
 {
     MAX_VERTEX_ATTRIBS = 16,
-    MAX_TEXTURE_IMAGE_UNITS = 16,
 
     // Implementation upper limits, real maximums depend on the hardware
-    IMPLEMENTATION_MAX_VERTEX_TEXTURE_IMAGE_UNITS = 16,
-    IMPLEMENTATION_MAX_COMBINED_TEXTURE_IMAGE_UNITS = MAX_TEXTURE_IMAGE_UNITS + IMPLEMENTATION_MAX_VERTEX_TEXTURE_IMAGE_UNITS,    
-
     IMPLEMENTATION_MAX_VARYING_VECTORS = 32,
     IMPLEMENTATION_MAX_DRAW_BUFFERS = 8,
     IMPLEMENTATION_MAX_FRAMEBUFFER_ATTACHMENTS = IMPLEMENTATION_MAX_DRAW_BUFFERS + 2, // 2 extra for depth and/or stencil buffers
 
     IMPLEMENTATION_MAX_VERTEX_SHADER_UNIFORM_BUFFERS = 16,
     IMPLEMENTATION_MAX_FRAGMENT_SHADER_UNIFORM_BUFFERS = 16,
     IMPLEMENTATION_MAX_COMBINED_SHADER_UNIFORM_BUFFERS = IMPLEMENTATION_MAX_VERTEX_SHADER_UNIFORM_BUFFERS +
                                                          IMPLEMENTATION_MAX_FRAGMENT_SHADER_UNIFORM_BUFFERS,
--- a/gfx/angle/src/libGLESv2/Context.cpp
+++ b/gfx/angle/src/libGLESv2/Context.cpp
@@ -39,16 +39,17 @@ namespace gl
 {
 
 Context::Context(int clientVersion, const gl::Context *shareContext, rx::Renderer *renderer, bool notifyResets, bool robustAccess)
     : mRenderer(renderer)
 {
     ASSERT(robustAccess == false);   // Unimplemented
 
     initCaps(clientVersion);
+    mState.initialize(mCaps, clientVersion);
 
     mClientVersion = clientVersion;
 
     mFenceNVHandleAllocator.setBaseHandle(0);
 
     if (shareContext != NULL)
     {
         mResourceManager = shareContext->mResourceManager;
@@ -60,26 +61,36 @@ Context::Context(int clientVersion, cons
     }
 
     // [OpenGL ES 2.0.24] section 3.7 page 83:
     // In the initial state, TEXTURE_2D and TEXTURE_CUBE_MAP have twodimensional
     // and cube map texture state vectors respectively associated with them.
     // In order that access to these initial textures not be lost, they are treated as texture
     // objects all of whose names are 0.
 
-    mTexture2DZero.set(new Texture2D(mRenderer->createTexture(GL_TEXTURE_2D), 0));
-    mTextureCubeMapZero.set(new TextureCubeMap(mRenderer->createTexture(GL_TEXTURE_CUBE_MAP), 0));
-    mTexture3DZero.set(new Texture3D(mRenderer->createTexture(GL_TEXTURE_3D), 0));
-    mTexture2DArrayZero.set(new Texture2DArray(mRenderer->createTexture(GL_TEXTURE_2D_ARRAY), 0));
+    mZeroTextures[GL_TEXTURE_2D].set(new Texture2D(mRenderer->createTexture(GL_TEXTURE_2D), 0));
+    bindTexture(GL_TEXTURE_2D, 0);
+
+    mZeroTextures[GL_TEXTURE_CUBE_MAP].set(new TextureCubeMap(mRenderer->createTexture(GL_TEXTURE_CUBE_MAP), 0));
+    bindTexture(GL_TEXTURE_CUBE_MAP, 0);
+
+    if (mClientVersion >= 3)
+    {
+        // TODO: These could also be enabled via extension
+        mZeroTextures[GL_TEXTURE_3D].set(new Texture3D(mRenderer->createTexture(GL_TEXTURE_3D), 0));
+        bindTexture(GL_TEXTURE_3D, 0);
+
+        mZeroTextures[GL_TEXTURE_2D_ARRAY].set(new Texture2DArray(mRenderer->createTexture(GL_TEXTURE_2D_ARRAY), 0));
+        bindTexture(GL_TEXTURE_2D_ARRAY, 0);
+    }
 
     bindVertexArray(0);
     bindArrayBuffer(0);
     bindElementArrayBuffer(0);
-    bindTextureCubeMap(0);
-    bindTexture2D(0);
+
     bindReadFramebuffer(0);
     bindDrawFramebuffer(0);
     bindRenderbuffer(0);
 
     bindGenericUniformBuffer(0);
     for (int i = 0; i < IMPLEMENTATION_MAX_COMBINED_SHADER_UNIFORM_BUFFERS; i++)
     {
         bindIndexedUniformBuffer(0, i, 0, -1);
@@ -147,25 +158,27 @@ Context::~Context()
     }
 
     mTransformFeedbackZero.set(NULL);
     while (!mTransformFeedbackMap.empty())
     {
         deleteTransformFeedback(mTransformFeedbackMap.begin()->first);
     }
 
-    for (int type = 0; type < TEXTURE_TYPE_COUNT; type++)
+    for (TextureMap::iterator i = mIncompleteTextures.begin(); i != mIncompleteTextures.end(); i++)
     {
-        mIncompleteTextures[type].set(NULL);
+        i->second.set(NULL);
     }
+    mIncompleteTextures.clear();
 
-    mTexture2DZero.set(NULL);
-    mTextureCubeMapZero.set(NULL);
-    mTexture3DZero.set(NULL);
-    mTexture2DArrayZero.set(NULL);
+    for (TextureMap::iterator i = mZeroTextures.begin(); i != mZeroTextures.end(); i++)
+    {
+        i->second.set(NULL);
+    }
+    mZeroTextures.clear();
 
     mResourceManager->release();
 }
 
 void Context::makeCurrent(egl::Surface *surface)
 {
     if (!mHasBeenCurrent)
     {
@@ -497,42 +510,21 @@ void Context::bindArrayBuffer(unsigned i
 
 void Context::bindElementArrayBuffer(unsigned int buffer)
 {
     mResourceManager->checkBufferAllocation(buffer);
 
     mState.getVertexArray()->setElementArrayBuffer(getBuffer(buffer));
 }
 
-void Context::bindTexture2D(GLuint texture)
+void Context::bindTexture(GLenum target, GLuint texture)
 {
-    mResourceManager->checkTextureAllocation(texture, TEXTURE_2D);
-
-    mState.setSamplerTexture(TEXTURE_2D, getTexture(texture));
-}
-
-void Context::bindTextureCubeMap(GLuint texture)
-{
-    mResourceManager->checkTextureAllocation(texture, TEXTURE_CUBE);
+    mResourceManager->checkTextureAllocation(texture, target);
 
-    mState.setSamplerTexture(TEXTURE_CUBE, getTexture(texture));
-}
-
-void Context::bindTexture3D(GLuint texture)
-{
-    mResourceManager->checkTextureAllocation(texture, TEXTURE_3D);
-
-    mState.setSamplerTexture(TEXTURE_3D, getTexture(texture));
-}
-
-void Context::bindTexture2DArray(GLuint texture)
-{
-    mResourceManager->checkTextureAllocation(texture, TEXTURE_2D_ARRAY);
-
-    mState.setSamplerTexture(TEXTURE_2D_ARRAY, getTexture(texture));
+    mState.setSamplerTexture(target, getTexture(texture));
 }
 
 void Context::bindReadFramebuffer(GLuint framebuffer)
 {
     if (!getFramebuffer(framebuffer))
     {
         mFramebufferMap[framebuffer] = new Framebuffer(mRenderer, framebuffer);
     }
@@ -565,17 +557,17 @@ void Context::bindVertexArray(GLuint ver
         mVertexArrayMap[vertexArray] = vertexArrayObject;
     }
 
     mState.setVertexArrayBinding(getVertexArray(vertexArray));
 }
 
 void Context::bindSampler(GLuint textureUnit, GLuint sampler)
 {
-    ASSERT(textureUnit < IMPLEMENTATION_MAX_COMBINED_TEXTURE_IMAGE_UNITS); // TODO: Update for backend-determined array size
+    ASSERT(textureUnit < mCaps.maxCombinedTextureImageUnits);
     mResourceManager->checkSamplerAllocation(sampler);
 
     mState.setSamplerBinding(textureUnit, getSampler(sampler));
 }
 
 void Context::bindGenericUniformBuffer(GLuint buffer)
 {
     mResourceManager->checkBufferAllocation(buffer);
@@ -677,36 +669,45 @@ void Context::setProgramBinary(GLuint pr
 
 }
 
 void Context::bindTransformFeedback(GLuint transformFeedback)
 {
     mState.setTransformFeedbackBinding(getTransformFeedback(transformFeedback));
 }
 
-void Context::beginQuery(GLenum target, GLuint query)
+Error Context::beginQuery(GLenum target, GLuint query)
 {
     Query *queryObject = getQuery(query, true, target);
     ASSERT(queryObject);
 
-    // set query as active for specified target
+    // begin query
+    Error error = queryObject->begin();
+    if (error.isError())
+    {
+        return error;
+    }
+
+    // set query as active for specified target only if begin succeeded
     mState.setActiveQuery(target, queryObject);
 
-    // begin query
-    queryObject->begin();
+    return Error(GL_NO_ERROR);
 }
 
-void Context::endQuery(GLenum target)
+Error Context::endQuery(GLenum target)
 {
     Query *queryObject = mState.getActiveQuery(target);
     ASSERT(queryObject);
 
-    queryObject->end();
+    gl::Error error = queryObject->end();
 
+    // Always unbind the query, even if there was an error. This may delete the query object.
     mState.setActiveQuery(target, NULL);
+
+    return error;
 }
 
 void Context::setFramebufferZero(Framebuffer *buffer)
 {
     // First, check to see if the old default framebuffer
     // was set for draw or read framebuffer, and change
     // the bindings to point to the new one before deleting it.
     if (mState.getDrawFramebuffer()->id() == 0)
@@ -811,46 +812,39 @@ Texture *Context::getTargetTexture(GLenu
       case GL_TEXTURE_3D:       return getTexture3D();
       case GL_TEXTURE_2D_ARRAY: return getTexture2DArray();
       default:                  return NULL;
     }
 }
 
 Texture2D *Context::getTexture2D() const
 {
-    return static_cast<Texture2D*>(getSamplerTexture(mState.getActiveSampler(), TEXTURE_2D));
+    return static_cast<Texture2D*>(getSamplerTexture(mState.getActiveSampler(), GL_TEXTURE_2D));
 }
 
 TextureCubeMap *Context::getTextureCubeMap() const
 {
-    return static_cast<TextureCubeMap*>(getSamplerTexture(mState.getActiveSampler(), TEXTURE_CUBE));
+    return static_cast<TextureCubeMap*>(getSamplerTexture(mState.getActiveSampler(), GL_TEXTURE_CUBE_MAP));
 }
 
 Texture3D *Context::getTexture3D() const
 {
-    return static_cast<Texture3D*>(getSamplerTexture(mState.getActiveSampler(), TEXTURE_3D));
+    return static_cast<Texture3D*>(getSamplerTexture(mState.getActiveSampler(), GL_TEXTURE_3D));
 }
 
 Texture2DArray *Context::getTexture2DArray() const
 {
-    return static_cast<Texture2DArray*>(getSamplerTexture(mState.getActiveSampler(), TEXTURE_2D_ARRAY));
+    return static_cast<Texture2DArray*>(getSamplerTexture(mState.getActiveSampler(), GL_TEXTURE_2D_ARRAY));
 }
 
-Texture *Context::getSamplerTexture(unsigned int sampler, TextureType type) const
+Texture *Context::getSamplerTexture(unsigned int sampler, GLenum type) const
 {
     if (mState.getSamplerTextureId(sampler, type) == 0)
     {
-        switch (type)
-        {
-          default: UNREACHABLE();
-          case TEXTURE_2D:       return mTexture2DZero.get();
-          case TEXTURE_CUBE:     return mTextureCubeMapZero.get();
-          case TEXTURE_3D:       return mTexture3DZero.get();
-          case TEXTURE_2D_ARRAY: return mTexture2DArrayZero.get();
-        }
+        return mZeroTextures.at(type).get();
     }
     else
     {
         return mState.getSamplerTexture(sampler, type);
     }
 }
 
 void Context::getBooleanv(GLenum pname, GLboolean *params)
@@ -1312,57 +1306,61 @@ bool Context::getIndexedQueryParameterIn
         }
     }
 
     return false;
 }
 
 // Applies the render target surface, depth stencil surface, viewport rectangle and
 // scissor rectangle to the renderer
-bool Context::applyRenderTarget(GLenum drawMode, bool ignoreViewport)
+Error Context::applyRenderTarget(GLenum drawMode, bool ignoreViewport)
 {
     Framebuffer *framebufferObject = mState.getDrawFramebuffer();
     ASSERT(framebufferObject && framebufferObject->completeness() == GL_FRAMEBUFFER_COMPLETE);
 
-    mRenderer->applyRenderTarget(framebufferObject);
+    gl::Error error = mRenderer->applyRenderTarget(framebufferObject);
+    if (error.isError())
+    {
+        return error;
+    }
 
     float nearZ, farZ;
     mState.getDepthRange(&nearZ, &farZ);
-    if (!mRenderer->setViewport(mState.getViewport(), nearZ, farZ, drawMode, mState.getRasterizerState().frontFace,
-                                ignoreViewport))
-    {
-        return false;
-    }
+    mRenderer->setViewport(mState.getViewport(), nearZ, farZ, drawMode, mState.getRasterizerState().frontFace,
+                           ignoreViewport);
 
     mRenderer->setScissorRectangle(mState.getScissor(), mState.isScissorTestEnabled());
 
-    return true;
+    return gl::Error(GL_NO_ERROR);
 }
 
 // Applies the fixed-function state (culling, depth test, alpha blending, stenciling, etc) to the Direct3D 9 device
-void Context::applyState(GLenum drawMode)
+Error Context::applyState(GLenum drawMode)
 {
     Framebuffer *framebufferObject = mState.getDrawFramebuffer();
     int samples = framebufferObject->getSamples();
 
     RasterizerState rasterizer = mState.getRasterizerState();
     rasterizer.pointDrawMode = (drawMode == GL_POINTS);
     rasterizer.multiSample = (samples != 0);
 
-    mRenderer->setRasterizerState(rasterizer);
+    Error error = mRenderer->setRasterizerState(rasterizer);
+    if (error.isError())
+    {
+        return error;
+    }
 
     unsigned int mask = 0;
     if (mState.isSampleCoverageEnabled())
     {
         GLclampf coverageValue;
         bool coverageInvert = false;
         mState.getSampleCoverageParams(&coverageValue, &coverageInvert);
         if (coverageValue != 0)
         {
-
             float threshold = 0.5f;
 
             for (int i = 0; i < samples; ++i)
             {
                 mask <<= 1;
 
                 if ((i + 1) * coverageValue >= threshold)
                 {
@@ -1376,134 +1374,204 @@ void Context::applyState(GLenum drawMode
         {
             mask = ~mask;
         }
     }
     else
     {
         mask = 0xFFFFFFFF;
     }
-    mRenderer->setBlendState(framebufferObject, mState.getBlendState(), mState.getBlendColor(), mask);
+    error = mRenderer->setBlendState(framebufferObject, mState.getBlendState(), mState.getBlendColor(), mask);
+    if (error.isError())
+    {
+        return error;
+    }
 
-    mRenderer->setDepthStencilState(mState.getDepthStencilState(), mState.getStencilRef(), mState.getStencilBackRef(),
-                                    rasterizer.frontFace == GL_CCW);
+    error = mRenderer->setDepthStencilState(mState.getDepthStencilState(), mState.getStencilRef(), mState.getStencilBackRef(),
+                                            rasterizer.frontFace == GL_CCW);
+    if (error.isError())
+    {
+        return error;
+    }
+
+    return Error(GL_NO_ERROR);
 }
 
 // Applies the shaders and shader constants to the Direct3D 9 device
-void Context::applyShaders(ProgramBinary *programBinary, bool transformFeedbackActive)
+Error Context::applyShaders(ProgramBinary *programBinary, bool transformFeedbackActive)
 {
     const VertexAttribute *vertexAttributes = mState.getVertexArray()->getVertexAttributes();
 
-    VertexFormat inputLayout[gl::MAX_VERTEX_ATTRIBS];
+    VertexFormat inputLayout[MAX_VERTEX_ATTRIBS];
     VertexFormat::GetInputLayout(inputLayout, programBinary, vertexAttributes, mState.getVertexAttribCurrentValues());
 
     const Framebuffer *fbo = mState.getDrawFramebuffer();
 
-    mRenderer->applyShaders(programBinary, inputLayout, fbo, mState.getRasterizerState().rasterizerDiscard, transformFeedbackActive);
+    Error error = mRenderer->applyShaders(programBinary, inputLayout, fbo, mState.getRasterizerState().rasterizerDiscard, transformFeedbackActive);
+    if (error.isError())
+    {
+        return error;
+    }
 
-    programBinary->applyUniforms();
+    return programBinary->applyUniforms();
 }
 
-size_t Context::getCurrentTexturesAndSamplerStates(ProgramBinary *programBinary, SamplerType type, Texture **outTextures,
-                                                   TextureType *outTextureTypes, SamplerState *outSamplers)
+Error Context::generateSwizzles(ProgramBinary *programBinary, SamplerType type)
 {
     size_t samplerRange = programBinary->getUsedSamplerRange(type);
+
     for (size_t i = 0; i < samplerRange; i++)
     {
-        outTextureTypes[i] = programBinary->getSamplerTextureType(type, i);
-        GLint textureUnit = programBinary->getSamplerMapping(type, i, getCaps());   // OpenGL texture image unit index
+        GLenum textureType = programBinary->getSamplerTextureType(type, i);
+        GLint textureUnit = programBinary->getSamplerMapping(type, i, getCaps());
         if (textureUnit != -1)
         {
-            outTextures[i] = getSamplerTexture(textureUnit, outTextureTypes[i]);
-            outTextures[i]->getSamplerStateWithNativeOffset(&outSamplers[i]);
-            Sampler *samplerObject = mState.getSampler(textureUnit);
-            if (samplerObject)
+            Texture* texture = getSamplerTexture(textureUnit, textureType);
+            if (texture->getSamplerState().swizzleRequired())
             {
-                samplerObject->getState(&outSamplers[i]);
+                Error error = mRenderer->generateSwizzle(texture);
+                if (error.isError())
+                {
+                    return error;
+                }
             }
         }
-        else
-        {
-            outTextures[i] = NULL;
-        }
     }
 
-    return samplerRange;
+    return Error(GL_NO_ERROR);
 }
 
-void Context::generateSwizzles(Texture *textures[], size_t count)
+Error Context::generateSwizzles(ProgramBinary *programBinary)
 {
-    for (size_t i = 0; i < count; i++)
+    Error error = generateSwizzles(programBinary, SAMPLER_VERTEX);
+    if (error.isError())
     {
-        if (textures[i] && textures[i]->getSamplerState().swizzleRequired())
-        {
-            mRenderer->generateSwizzle(textures[i]);
-        }
+        return error;
     }
+
+    error = generateSwizzles(programBinary, SAMPLER_PIXEL);
+    if (error.isError())
+    {
+        return error;
+    }
+
+    return Error(GL_NO_ERROR);
 }
 
 // For each Direct3D sampler of either the pixel or vertex stage,
 // looks up the corresponding OpenGL texture image unit and texture type,
 // and sets the texture and its addressing/filtering state (or NULL when inactive).
-void Context::applyTextures(SamplerType shaderType, Texture *textures[], TextureType *textureTypes, SamplerState *samplers,
-                            size_t textureCount, const FramebufferTextureSerialArray& framebufferSerials,
-                            size_t framebufferSerialCount)
+Error Context::applyTextures(ProgramBinary *programBinary, SamplerType shaderType,
+                             const FramebufferTextureSerialArray &framebufferSerials, size_t framebufferSerialCount)
 {
-    // Range of Direct3D samplers of given sampler type
-    size_t samplerCount = (shaderType == SAMPLER_PIXEL) ? mCaps.maxTextureImageUnits
-                                                        : mCaps.maxVertexTextureImageUnits;
+    size_t samplerRange = programBinary->getUsedSamplerRange(shaderType);
+    for (size_t samplerIndex = 0; samplerIndex < samplerRange; samplerIndex++)
+    {
+        GLenum textureType = programBinary->getSamplerTextureType(shaderType, samplerIndex);
+        GLint textureUnit = programBinary->getSamplerMapping(shaderType, samplerIndex, getCaps());
+        if (textureUnit != -1)
+        {
+            SamplerState sampler;
+            Texture* texture = getSamplerTexture(textureUnit, textureType);
+            texture->getSamplerStateWithNativeOffset(&sampler);
 
-    for (size_t samplerIndex = 0; samplerIndex < textureCount; samplerIndex++)
-    {
-        Texture *texture = textures[samplerIndex];
-        const SamplerState &sampler = samplers[samplerIndex];
-        TextureType textureType = textureTypes[samplerIndex];
+            Sampler *samplerObject = mState.getSampler(textureUnit);
+            if (samplerObject)
+            {
+                samplerObject->getState(&sampler);
+            }
 
-        if (texture)
-        {
             // TODO: std::binary_search may become unavailable using older versions of GCC
             if (texture->isSamplerComplete(sampler, mTextureCaps, mExtensions, mClientVersion) &&
                 !std::binary_search(framebufferSerials.begin(), framebufferSerials.begin() + framebufferSerialCount, texture->getTextureSerial()))
             {
-                mRenderer->setSamplerState(shaderType, samplerIndex, sampler);
-                mRenderer->setTexture(shaderType, samplerIndex, texture);
+                Error error = mRenderer->setSamplerState(shaderType, samplerIndex, sampler);
+                if (error.isError())
+                {
+                    return error;
+                }
+
+                error = mRenderer->setTexture(shaderType, samplerIndex, texture);
+                if (error.isError())
+                {
+                    return error;
+                }
             }
             else
             {
+                // Texture is not sampler complete or it is in use by the framebuffer.  Bind the incomplete texture.
                 Texture *incompleteTexture = getIncompleteTexture(textureType);
-                mRenderer->setTexture(shaderType, samplerIndex, incompleteTexture);
+                gl::Error error = mRenderer->setTexture(shaderType, samplerIndex, incompleteTexture);
+                if (error.isError())
+                {
+                    return error;
+                }
             }
         }
         else
         {
-            mRenderer->setTexture(shaderType, samplerIndex, NULL);
+            // No texture bound to this slot even though it is used by the shader, bind a NULL texture
+            Error error = mRenderer->setTexture(shaderType, samplerIndex, NULL);
+            if (error.isError())
+            {
+                return error;
+            }
+        }
+    }
+
+    // Set all the remaining textures to NULL
+    size_t samplerCount = (shaderType == SAMPLER_PIXEL) ? mCaps.maxTextureImageUnits
+                                                        : mCaps.maxVertexTextureImageUnits;
+    for (size_t samplerIndex = samplerRange; samplerIndex < samplerCount; samplerIndex++)
+    {
+        Error error = mRenderer->setTexture(shaderType, samplerIndex, NULL);
+        if (error.isError())
+        {
+            return error;
         }
     }
 
-    for (size_t samplerIndex = textureCount; samplerIndex < samplerCount; samplerIndex++)
-    {
-        mRenderer->setTexture(shaderType, samplerIndex, NULL);
-    }
+    return Error(GL_NO_ERROR);
 }
 
-bool Context::applyUniformBuffers()
+Error Context::applyTextures(ProgramBinary *programBinary)
+{
+    FramebufferTextureSerialArray framebufferSerials;
+    size_t framebufferSerialCount = getBoundFramebufferTextureSerials(&framebufferSerials);
+
+    Error error = applyTextures(programBinary, SAMPLER_VERTEX, framebufferSerials, framebufferSerialCount);
+    if (error.isError())
+    {
+        return error;
+    }
+
+    error = applyTextures(programBinary, SAMPLER_PIXEL, framebufferSerials, framebufferSerialCount);
+    if (error.isError())
+    {
+        return error;
+    }
+
+    return Error(GL_NO_ERROR);
+}
+
+Error Context::applyUniformBuffers()
 {
     Program *programObject = getProgram(mState.getCurrentProgramId());
     ProgramBinary *programBinary = programObject->getProgramBinary();
 
-    std::vector<gl::Buffer*> boundBuffers;
+    std::vector<Buffer*> boundBuffers;
 
     for (unsigned int uniformBlockIndex = 0; uniformBlockIndex < programBinary->getActiveUniformBlockCount(); uniformBlockIndex++)
     {
         GLuint blockBinding = programObject->getUniformBlockBinding(uniformBlockIndex);
 
         if (mState.getIndexedUniformBuffer(blockBinding)->id() == 0)
         {
             // undefined behaviour
-            return false;
+            return gl::Error(GL_INVALID_OPERATION, "It is undefined behaviour to have a used but unbound uniform buffer.");
         }
         else
         {
             Buffer *uniformBuffer = mState.getIndexedUniformBuffer(blockBinding);
             ASSERT(uniformBuffer);
             boundBuffers.push_back(uniformBuffer);
         }
     }
@@ -1539,38 +1607,35 @@ void Context::markTransformFeedbackUsage
         Buffer *buffer = mState.getIndexedTransformFeedbackBuffer(i);
         if (buffer)
         {
             buffer->markTransformFeedbackUsage();
         }
     }
 }
 
-void Context::clear(GLbitfield mask)
+Error Context::clear(GLbitfield mask)
 {
     if (mState.isRasterizerDiscardEnabled())
     {
-        return;
+        return Error(GL_NO_ERROR);
     }
 
     ClearParameters clearParams = mState.getClearParameters(mask);
 
-    if (!applyRenderTarget(GL_TRIANGLES, true))   // Clips the clear to the scissor rectangle but not the viewport
-    {
-        return;
-    }
+    applyRenderTarget(GL_TRIANGLES, true);   // Clips the clear to the scissor rectangle but not the viewport
 
-    mRenderer->clear(clearParams, mState.getDrawFramebuffer());
+    return mRenderer->clear(clearParams, mState.getDrawFramebuffer());
 }
 
-void Context::clearBufferfv(GLenum buffer, int drawbuffer, const float *values)
+Error Context::clearBufferfv(GLenum buffer, int drawbuffer, const float *values)
 {
     if (mState.isRasterizerDiscardEnabled())
     {
-        return;
+        return Error(GL_NO_ERROR);
     }
 
     // glClearBufferfv can be called to clear the color buffer or depth buffer
     ClearParameters clearParams = mState.getClearParameters(0);
 
     if (buffer == GL_COLOR)
     {
         for (unsigned int i = 0; i < ArraySize(clearParams.clearColor); i++)
@@ -1582,53 +1647,47 @@ void Context::clearBufferfv(GLenum buffe
     }
 
     if (buffer == GL_DEPTH)
     {
         clearParams.clearDepth = true;
         clearParams.depthClearValue = values[0];
     }
 
-    if (!applyRenderTarget(GL_TRIANGLES, true))   // Clips the clear to the scissor rectangle but not the viewport
-    {
-        return;
-    }
+    applyRenderTarget(GL_TRIANGLES, true);   // Clips the clear to the scissor rectangle but not the viewport
 
-    mRenderer->clear(clearParams, mState.getDrawFramebuffer());
+    return mRenderer->clear(clearParams, mState.getDrawFramebuffer());
 }
 
-void Context::clearBufferuiv(GLenum buffer, int drawbuffer, const unsigned int *values)
+Error Context::clearBufferuiv(GLenum buffer, int drawbuffer, const unsigned int *values)
 {
     if (mState.isRasterizerDiscardEnabled())
     {
-        return;
+        return Error(GL_NO_ERROR);
     }
 
     // glClearBufferuv can only be called to clear a color buffer
     ClearParameters clearParams = mState.getClearParameters(0);
     for (unsigned int i = 0; i < ArraySize(clearParams.clearColor); i++)
     {
         clearParams.clearColor[i] = (drawbuffer == static_cast<int>(i));
     }
     clearParams.colorUIClearValue = ColorUI(values[0], values[1], values[2], values[3]);
     clearParams.colorClearType = GL_UNSIGNED_INT;
 
-    if (!applyRenderTarget(GL_TRIANGLES, true))   // Clips the clear to the scissor rectangle but not the viewport
-    {
-        return;
-    }
+    applyRenderTarget(GL_TRIANGLES, true);   // Clips the clear to the scissor rectangle but not the viewport
 
-    mRenderer->clear(clearParams, mState.getDrawFramebuffer());
+    return mRenderer->clear(clearParams, mState.getDrawFramebuffer());
 }
 
-void Context::clearBufferiv(GLenum buffer, int drawbuffer, const int *values)
+Error Context::clearBufferiv(GLenum buffer, int drawbuffer, const int *values)
 {
     if (mState.isRasterizerDiscardEnabled())
     {
-        return;
+        return Error(GL_NO_ERROR);
     }
 
     // glClearBufferfv can be called to clear the color buffer or stencil buffer
     ClearParameters clearParams = mState.getClearParameters(0);
 
     if (buffer == GL_COLOR)
     {
         for (unsigned int i = 0; i < ArraySize(clearParams.clearColor); i++)
@@ -1640,197 +1699,209 @@ void Context::clearBufferiv(GLenum buffe
     }
 
     if (buffer == GL_STENCIL)
     {
         clearParams.clearStencil = true;
         clearParams.stencilClearValue = values[1];
     }
 
-    if (!applyRenderTarget(GL_TRIANGLES, true))   // Clips the clear to the scissor rectangle but not the viewport
-    {
-        return;
-    }
+    applyRenderTarget(GL_TRIANGLES, true);   // Clips the clear to the scissor rectangle but not the viewport
 
-    mRenderer->clear(clearParams, mState.getDrawFramebuffer());
+    return mRenderer->clear(clearParams, mState.getDrawFramebuffer());
 }
 
-void Context::clearBufferfi(GLenum buffer, int drawbuffer, float depth, int stencil)
+Error Context::clearBufferfi(GLenum buffer, int drawbuffer, float depth, int stencil)
 {
     if (mState.isRasterizerDiscardEnabled())
     {
-        return;
+        return Error(GL_NO_ERROR);
     }
 
     // glClearBufferfi can only be called to clear a depth stencil buffer
     ClearParameters clearParams = mState.getClearParameters(0);
     clearParams.clearDepth = true;
     clearParams.depthClearValue = depth;
     clearParams.clearStencil = true;
     clearParams.stencilClearValue = stencil;
 
-    if (!applyRenderTarget(GL_TRIANGLES, true))   // Clips the clear to the scissor rectangle but not the viewport
-    {
-        return;
-    }
+    applyRenderTarget(GL_TRIANGLES, true);   // Clips the clear to the scissor rectangle but not the viewport
 
-    mRenderer->clear(clearParams, mState.getDrawFramebuffer());
+    return mRenderer->clear(clearParams, mState.getDrawFramebuffer());
 }
 
-void Context::readPixels(GLint x, GLint y, GLsizei width, GLsizei height,
-                         GLenum format, GLenum type, GLsizei *bufSize, void* pixels)
+Error Context::readPixels(GLint x, GLint y, GLsizei width, GLsizei height,
+                          GLenum format, GLenum type, GLsizei *bufSize, void* pixels)
 {
-    gl::Framebuffer *framebuffer = mState.getReadFramebuffer();
+    Framebuffer *framebuffer = mState.getReadFramebuffer();
 
     GLenum sizedInternalFormat = GetSizedInternalFormat(format, type);
     const InternalFormat &sizedFormatInfo = GetInternalFormatInfo(sizedInternalFormat);
     GLuint outputPitch = sizedFormatInfo.computeRowPitch(type, width, mState.getPackAlignment());
 
-    mRenderer->readPixels(framebuffer, x, y, width, height, format, type, outputPitch, mState.getPackState(),
-                          reinterpret_cast<uint8_t*>(pixels));
+    return mRenderer->readPixels(framebuffer, x, y, width, height, format, type, outputPitch, mState.getPackState(),
+                                 reinterpret_cast<uint8_t*>(pixels));
 }
 
-void Context::drawArrays(GLenum mode, GLint first, GLsizei count, GLsizei instances)
+Error Context::drawArrays(GLenum mode, GLint first, GLsizei count, GLsizei instances)
 {
     ASSERT(mState.getCurrentProgramId() != 0);
 
     ProgramBinary *programBinary = mState.getCurrentProgramBinary();
     programBinary->updateSamplerMapping();
 
-    Texture *vsTextures[IMPLEMENTATION_MAX_VERTEX_TEXTURE_IMAGE_UNITS];
-    TextureType vsTextureTypes[IMPLEMENTATION_MAX_VERTEX_TEXTURE_IMAGE_UNITS];
-    SamplerState vsSamplers[IMPLEMENTATION_MAX_VERTEX_TEXTURE_IMAGE_UNITS];
-    size_t vsTextureCount = getCurrentTexturesAndSamplerStates(programBinary, SAMPLER_VERTEX, vsTextures, vsTextureTypes, vsSamplers);
-
-    Texture *psTextures[MAX_TEXTURE_IMAGE_UNITS];
-    TextureType psTextureTypes[MAX_TEXTURE_IMAGE_UNITS];
-    SamplerState psSamplers[MAX_TEXTURE_IMAGE_UNITS];
-    size_t psTextureCount = getCurrentTexturesAndSamplerStates(programBinary, SAMPLER_PIXEL, psTextures, psTextureTypes, psSamplers);
-
-    generateSwizzles(vsTextures, vsTextureCount);
-    generateSwizzles(psTextures, psTextureCount);
+    Error error = generateSwizzles(programBinary);
+    if (error.isError())
+    {
+        return error;
+    }
 
     if (!mRenderer->applyPrimitiveType(mode, count))
     {
-        return;
+        return Error(GL_NO_ERROR);
+    }
+
+    error = applyRenderTarget(mode, false);
+    if (error.isError())
+    {
+        return error;
     }
 
-    if (!applyRenderTarget(mode, false))
+    error = applyState(mode);
+    if (error.isError())
     {
-        return;
+        return error;
     }
 
-    applyState(mode);
-
-    GLenum err = mRenderer->applyVertexBuffer(programBinary, mState.getVertexArray()->getVertexAttributes(), mState.getVertexAttribCurrentValues(), first, count, instances);
-    if (err != GL_NO_ERROR)
+    error = mRenderer->applyVertexBuffer(programBinary, mState.getVertexArray()->getVertexAttributes(), mState.getVertexAttribCurrentValues(), first, count, instances);
+    if (error.isError())
     {
-        return gl::error(err);
+        return error;
     }
 
     bool transformFeedbackActive = applyTransformFeedbackBuffers();
 
-    applyShaders(programBinary, transformFeedbackActive);
-
-    FramebufferTextureSerialArray frameBufferSerials;
-    size_t framebufferSerialCount = getBoundFramebufferTextureSerials(&frameBufferSerials);
+    error = applyShaders(programBinary, transformFeedbackActive);
+    if (error.isError())
+    {
+        return error;
+    }
 
-    applyTextures(SAMPLER_VERTEX, vsTextures, vsTextureTypes, vsSamplers, vsTextureCount, frameBufferSerials, framebufferSerialCount);
-    applyTextures(SAMPLER_PIXEL, psTextures, psTextureTypes, psSamplers, psTextureCount, frameBufferSerials, framebufferSerialCount);
+    error = applyTextures(programBinary);
+    if (error.isError())
+    {
+        return error;
+    }
 
-    if (!applyUniformBuffers())
+    error = applyUniformBuffers();
+    if (error.isError())
     {
-        return;
+        return error;
     }
 
     if (!skipDraw(mode))
     {
-        mRenderer->drawArrays(mode, count, instances, transformFeedbackActive);
+        error = mRenderer->drawArrays(mode, count, instances, transformFeedbackActive);
+        if (error.isError())
+        {
+            return error;
+        }
 
         if (transformFeedbackActive)
         {
             markTransformFeedbackUsage();
         }
     }
+
+    return gl::Error(GL_NO_ERROR);
 }
 
-void Context::drawElements(GLenum mode, GLsizei count, GLenum type,
-                           const GLvoid *indices, GLsizei instances,
-                           const rx::RangeUI &indexRange)
+Error Context::drawElements(GLenum mode, GLsizei count, GLenum type,
+                            const GLvoid *indices, GLsizei instances,
+                            const rx::RangeUI &indexRange)
 {
     ASSERT(mState.getCurrentProgramId() != 0);
 
     ProgramBinary *programBinary = mState.getCurrentProgramBinary();
     programBinary->updateSamplerMapping();
 
-    Texture *vsTextures[IMPLEMENTATION_MAX_VERTEX_TEXTURE_IMAGE_UNITS];
-    TextureType vsTextureTypes[IMPLEMENTATION_MAX_VERTEX_TEXTURE_IMAGE_UNITS];
-    SamplerState vsSamplers[IMPLEMENTATION_MAX_VERTEX_TEXTURE_IMAGE_UNITS];
-    size_t vsTextureCount = getCurrentTexturesAndSamplerStates(programBinary, SAMPLER_VERTEX, vsTextures, vsTextureTypes, vsSamplers);
-
-    Texture *psTextures[MAX_TEXTURE_IMAGE_UNITS];
-    TextureType psTextureTypes[MAX_TEXTURE_IMAGE_UNITS];
-    SamplerState psSamplers[MAX_TEXTURE_IMAGE_UNITS];
-    size_t psTextureCount = getCurrentTexturesAndSamplerStates(programBinary, SAMPLER_PIXEL, psTextures, psTextureTypes, psSamplers);
-
-    generateSwizzles(vsTextures, vsTextureCount);
-    generateSwizzles(psTextures, psTextureCount);
+    Error error = generateSwizzles(programBinary);
+    if (error.isError())
+    {
+        return error;
+    }
 
     if (!mRenderer->applyPrimitiveType(mode, count))
     {
-        return;
+        return Error(GL_NO_ERROR);
     }
 
-    if (!applyRenderTarget(mode, false))
+    error = applyRenderTarget(mode, false);
+    if (error.isError())
     {
-        return;
+        return error;
     }
 
-    applyState(mode);
+    error = applyState(mode);
+    if (error.isError())
+    {
+        return error;
+    }
 
     VertexArray *vao = mState.getVertexArray();
     rx::TranslatedIndexData indexInfo;
     indexInfo.indexRange = indexRange;
-    GLenum err = mRenderer->applyIndexBuffer(indices, vao->getElementArrayBuffer(), count, mode, type, &indexInfo);
-    if (err != GL_NO_ERROR)
+    error = mRenderer->applyIndexBuffer(indices, vao->getElementArrayBuffer(), count, mode, type, &indexInfo);
+    if (error.isError())
     {
-        return gl::error(err);
+        return error;
     }
 
     GLsizei vertexCount = indexInfo.indexRange.length() + 1;
-    err = mRenderer->applyVertexBuffer(programBinary, vao->getVertexAttributes(),
-                                       mState.getVertexAttribCurrentValues(),
-                                       indexInfo.indexRange.start, vertexCount, instances);
-    if (err != GL_NO_ERROR)
+    error = mRenderer->applyVertexBuffer(programBinary, vao->getVertexAttributes(),
+                                         mState.getVertexAttribCurrentValues(),
+                                         indexInfo.indexRange.start, vertexCount, instances);
+    if (error.isError())
     {
-        return gl::error(err);
+        return error;
     }
 
     bool transformFeedbackActive = applyTransformFeedbackBuffers();
     // Transform feedback is not allowed for DrawElements, this error should have been caught at the API validation
     // layer.
     ASSERT(!transformFeedbackActive);
 
-    applyShaders(programBinary, transformFeedbackActive);
-
-    FramebufferTextureSerialArray frameBufferSerials;
-    size_t framebufferSerialCount = getBoundFramebufferTextureSerials(&frameBufferSerials);
+    error = applyShaders(programBinary, transformFeedbackActive);
+    if (error.isError())
+    {
+        return error;
+    }
 
-    applyTextures(SAMPLER_VERTEX, vsTextures, vsTextureTypes, vsSamplers, vsTextureCount, frameBufferSerials, framebufferSerialCount);
-    applyTextures(SAMPLER_PIXEL, psTextures, psTextureTypes, psSamplers, psTextureCount, frameBufferSerials, framebufferSerialCount);
+    error = applyTextures(programBinary);
+    if (error.isError())
+    {
+        return error;
+    }
 
-    if (!applyUniformBuffers())
+    error = applyUniformBuffers();
+    if (error.isError())
     {
-        return;
+        return error;
     }
 
     if (!skipDraw(mode))
     {
-        mRenderer->drawElements(mode, count, type, indices, vao->getElementArrayBuffer(), indexInfo, instances);
+        error = mRenderer->drawElements(mode, count, type, indices, vao->getElementArrayBuffer(), indexInfo, instances);
+        if (error.isError())
+        {
+            return error;
+        }
     }
+
+    return Error(GL_NO_ERROR);
 }
 
 // Implements glFlush when block is false, glFinish when block is true
 void Context::sync(bool block)
 {
     mRenderer->sync(block);
 }
 
@@ -1996,77 +2067,76 @@ void Context::detachTransformFeedback(GL
     mState.detachTransformFeedback(transformFeedback);
 }
 
 void Context::detachSampler(GLuint sampler)
 {
     mState.detachSampler(sampler);
 }
 
-Texture *Context::getIncompleteTexture(TextureType type)
+Texture *Context::getIncompleteTexture(GLenum type)
 {
-    Texture *t = mIncompleteTextures[type].get();
-
-    if (t == NULL)
+    if (mIncompleteTextures.find(type) == mIncompleteTextures.end())
     {
         const GLubyte color[] = { 0, 0, 0, 255 };
         const PixelUnpackState incompleteUnpackState(1);
 
+        Texture* t = NULL;
         switch (type)
         {
           default:
             UNREACHABLE();
             // default falls through to TEXTURE_2D
 
-          case TEXTURE_2D:
+          case GL_TEXTURE_2D:
             {
                 Texture2D *incomplete2d = new Texture2D(mRenderer->createTexture(GL_TEXTURE_2D), Texture::INCOMPLETE_TEXTURE_ID);
                 incomplete2d->setImage(0, 1, 1, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, incompleteUnpackState, color);
                 t = incomplete2d;
             }
             break;
 
-          case TEXTURE_CUBE:
+          case GL_TEXTURE_CUBE_MAP:
             {
               TextureCubeMap *incompleteCube = new TextureCubeMap(mRenderer->createTexture(GL_TEXTURE_CUBE_MAP), Texture::INCOMPLETE_TEXTURE_ID);
 
-              incompleteCube->setImagePosX(0, 1, 1, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, incompleteUnpackState, color);
-              incompleteCube->setImageNegX(0, 1, 1, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, incompleteUnpackState, color);
-              incompleteCube->setImagePosY(0, 1, 1, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, incompleteUnpackState, color);
-              incompleteCube->setImageNegY(0, 1, 1, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, incompleteUnpackState, color);
-              incompleteCube->setImagePosZ(0, 1, 1, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, incompleteUnpackState, color);
-              incompleteCube->setImageNegZ(0, 1, 1, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, incompleteUnpackState, color);
+              incompleteCube->setImage(GL_TEXTURE_CUBE_MAP_POSITIVE_X, 0, 1, 1, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, incompleteUnpackState, color);
+              incompleteCube->setImage(GL_TEXTURE_CUBE_MAP_NEGATIVE_X, 0, 1, 1, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, incompleteUnpackState, color);
+              incompleteCube->setImage(GL_TEXTURE_CUBE_MAP_POSITIVE_Y, 0, 1, 1, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, incompleteUnpackState, color);
+              incompleteCube->setImage(GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, 0, 1, 1, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, incompleteUnpackState, color);
+              incompleteCube->setImage(GL_TEXTURE_CUBE_MAP_POSITIVE_Z, 0, 1, 1, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, incompleteUnpackState, color);
+              incompleteCube->setImage(GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, 0, 1, 1, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, incompleteUnpackState, color);
 
               t = incompleteCube;
             }
             break;
 
-          case TEXTURE_3D:
+          case GL_TEXTURE_3D:
             {
                 Texture3D *incomplete3d = new Texture3D(mRenderer->createTexture(GL_TEXTURE_3D), Texture::INCOMPLETE_TEXTURE_ID);
                 incomplete3d->setImage(0, 1, 1, 1, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, incompleteUnpackState, color);
 
                 t = incomplete3d;
             }
             break;
 
-          case TEXTURE_2D_ARRAY:
+          case GL_TEXTURE_2D_ARRAY:
             {
                 Texture2DArray *incomplete2darray = new Texture2DArray(mRenderer->createTexture(GL_TEXTURE_2D_ARRAY), Texture::INCOMPLETE_TEXTURE_ID);
                 incomplete2darray->setImage(0, 1, 1, 1, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, incompleteUnpackState, color);
 
                 t = incomplete2darray;
             }
             break;
         }
 
         mIncompleteTextures[type].set(t);
     }
 
-    return t;
+    return mIncompleteTextures[type].get();
 }
 
 bool Context::skipDraw(GLenum drawMode)
 {
     if (drawMode == GL_POINTS)
     {
         // ProgramBinary assumes non-point rendering if gl_PointSize isn't written,
         // which affects varying interpolation. Since the value of gl_PointSize is
@@ -2228,33 +2298,35 @@ size_t Context::getBoundFramebufferTextu
     size_t serialCount = 0;
 
     Framebuffer *drawFramebuffer = mState.getDrawFramebuffer();
     for (unsigned int i = 0; i < IMPLEMENTATION_MAX_DRAW_BUFFERS; i++)
     {
         FramebufferAttachment *attachment = drawFramebuffer->getColorbuffer(i);
         if (attachment && attachment->isTexture())
         {
-            (*outSerialArray)[serialCount++] = attachment->getTextureSerial();
+            Texture *texture = attachment->getTexture();
+            (*outSerialArray)[serialCount++] = texture->getTextureSerial();
         }
     }
 
     FramebufferAttachment *depthStencilAttachment = drawFramebuffer->getDepthOrStencilbuffer();
     if (depthStencilAttachment && depthStencilAttachment->isTexture())
     {
-        (*outSerialArray)[serialCount++] = depthStencilAttachment->getTextureSerial();
+        Texture *depthStencilTexture = depthStencilAttachment->getTexture();
+        (*outSerialArray)[serialCount++] = depthStencilTexture->getTextureSerial();
     }
 
     std::sort(outSerialArray->begin(), outSerialArray->begin() + serialCount);
 
     return serialCount;
 }
 
-void Context::blitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1,
-                              GLbitfield mask, GLenum filter)
+Error Context::blitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1,
+                               GLbitfield mask, GLenum filter)
 {
     Framebuffer *readFramebuffer = mState.getReadFramebuffer();
     Framebuffer *drawFramebuffer = mState.getDrawFramebuffer();
 
     bool blitRenderTarget = false;
     bool blitDepth = false;
     bool blitStencil = false;
     if ((mask & GL_COLOR_BUFFER_BIT) && readFramebuffer->getReadColorbuffer() && drawFramebuffer->getFirstColorbuffer())
@@ -2265,24 +2337,30 @@ void Context::blitFramebuffer(GLint srcX
     {
         blitStencil = true;
     }
     if ((mask & GL_DEPTH_BUFFER_BIT) && readFramebuffer->getDepthbuffer() && drawFramebuffer->getDepthbuffer())
     {
         blitDepth = true;
     }
 
-    gl::Rectangle srcRect(srcX0, srcY0, srcX1 - srcX0, srcY1 - srcY0);
-    gl::Rectangle dstRect(dstX0, dstY0, dstX1 - dstX0, dstY1 - dstY0);
+    Rectangle srcRect(srcX0, srcY0, srcX1 - srcX0, srcY1 - srcY0);
+    Rectangle dstRect(dstX0, dstY0, dstX1 - dstX0, dstY1 - dstY0);
     if (blitRenderTarget || blitDepth || blitStencil)
     {
         const gl::Rectangle *scissor = mState.isScissorTestEnabled() ? &mState.getScissor() : NULL;
-        mRenderer->blitRect(readFramebuffer, srcRect, drawFramebuffer, dstRect, scissor,
-                            blitRenderTarget, blitDepth, blitStencil, filter);
+        gl::Error error = mRenderer->blitRect(readFramebuffer, srcRect, drawFramebuffer, dstRect, scissor,
+                                              blitRenderTarget, blitDepth, blitStencil, filter);
+        if (error.isError())
+        {
+            return error;
+        }
     }
+
+    return gl::Error(GL_NO_ERROR);
 }
 
 void Context::releaseShaderCompiler()
 {
     mRenderer->releaseShaderCompiler();
 }
 
 void Context::initCaps(GLuint clientVersion)
@@ -2300,55 +2378,50 @@ void Context::initCaps(GLuint clientVers
     if (clientVersion > 2)
     {
         // FIXME(geofflang): Don't support EXT_sRGB in non-ES2 contexts
         //mExtensions.sRGB = false;
     }
 
     // Apply implementation limits
     mCaps.maxVertexAttributes = std::min<GLuint>(mCaps.maxVertexAttributes, MAX_VERTEX_ATTRIBS);
-    mCaps.maxVertexTextureImageUnits = std::min<GLuint>(mCaps.maxVertexTextureImageUnits, IMPLEMENTATION_MAX_VERTEX_TEXTURE_IMAGE_UNITS);
     mCaps.maxVertexUniformBlocks = std::min<GLuint>(mCaps.maxVertexUniformBlocks, IMPLEMENTATION_MAX_VERTEX_SHADER_UNIFORM_BUFFERS);
     mCaps.maxVertexOutputComponents = std::min<GLuint>(mCaps.maxVertexOutputComponents, IMPLEMENTATION_MAX_VARYING_VECTORS * 4);
 
     mCaps.maxFragmentInputComponents = std::min<GLuint>(mCaps.maxFragmentInputComponents, IMPLEMENTATION_MAX_VARYING_VECTORS * 4);
-    mCaps.maxTextureImageUnits = std::min<GLuint>(mCaps.maxTextureImageUnits, MAX_TEXTURE_IMAGE_UNITS);
-
-    mCaps.maxCombinedTextureImageUnits = std::min<GLuint>(mCaps.maxCombinedTextureImageUnits, IMPLEMENTATION_MAX_COMBINED_TEXTURE_IMAGE_UNITS);
 
     GLuint maxSamples = 0;
     mCaps.compressedTextureFormats.clear();
 
     const TextureCapsMap &rendererFormats = mRenderer->getRendererTextureCaps();
     for (TextureCapsMap::const_iterator i = rendererFormats.begin(); i != rendererFormats.end(); i++)
     {
         GLenum format = i->first;
         TextureCaps formatCaps = i->second;
 
         const InternalFormat &formatInfo = GetInternalFormatInfo(format);
-        if (formatCaps.texturable && formatInfo.textureSupport(clientVersion, mExtensions))
-        {
-            // Update the format caps based on the client version and extensions
-            formatCaps.renderable = formatInfo.renderSupport(clientVersion, mExtensions);
-            formatCaps.filterable = formatInfo.filterSupport(clientVersion, mExtensions);
+
+        // Update the format caps based on the client version and extensions
+        formatCaps.texturable = formatInfo.textureSupport(clientVersion, mExtensions);
+        formatCaps.renderable = formatInfo.renderSupport(clientVersion, mExtensions);
+        formatCaps.filterable = formatInfo.filterSupport(clientVersion, mExtensions);
 
-            // OpenGL ES does not support multisampling with integer formats
-            if (formatInfo.componentType == GL_INT || formatInfo.componentType == GL_UNSIGNED_INT)
-            {
-                formatCaps.sampleCounts.clear();
-            }
-            maxSamples = std::max(maxSamples, formatCaps.getMaxSamples());
+        // OpenGL ES does not support multisampling with integer formats
+        if (!formatInfo.renderSupport || formatInfo.componentType == GL_INT || formatInfo.componentType == GL_UNSIGNED_INT)
+        {
+            formatCaps.sampleCounts.clear();
+        }
+        maxSamples = std::max(maxSamples, formatCaps.getMaxSamples());
 
-            if (formatInfo.compressed)
-            {
-                mCaps.compressedTextureFormats.push_back(format);
-            }
+        if (formatCaps.texturable && formatInfo.compressed)
+        {
+            mCaps.compressedTextureFormats.push_back(format);
+        }
 
-            mTextureCaps.insert(format, formatCaps);
-        }
+        mTextureCaps.insert(format, formatCaps);
     }
 
     mExtensions.maxSamples = maxSamples;
 }
 
 }
 
 extern "C"
--- a/gfx/angle/src/libGLESv2/Context.h
+++ b/gfx/angle/src/libGLESv2/Context.h
@@ -110,20 +110,17 @@ class Context
     void deleteQuery(GLuint query);
 
     // Vertex arrays are owned by the Context
     GLuint createVertexArray();
     void deleteVertexArray(GLuint vertexArray);
 
     void bindArrayBuffer(GLuint buffer);
     void bindElementArrayBuffer(GLuint buffer);
-    void bindTexture2D(GLuint texture);
-    void bindTextureCubeMap(GLuint texture);
-    void bindTexture3D(GLuint texture);
-    void bindTexture2DArray(GLuint texture);
+    void bindTexture(GLenum target, GLuint texture);
     void bindReadFramebuffer(GLuint framebuffer);
     void bindDrawFramebuffer(GLuint framebuffer);
     void bindRenderbuffer(GLuint renderbuffer);
     void bindVertexArray(GLuint vertexArray);
     void bindSampler(GLuint textureUnit, GLuint sampler);
     void bindGenericUniformBuffer(GLuint buffer);
     void bindIndexedUniformBuffer(GLuint buffer, GLuint index, GLintptr offset, GLsizeiptr size);
     void bindGenericTransformFeedbackBuffer(GLuint buffer);
@@ -132,18 +129,18 @@ class Context
     void bindCopyWriteBuffer(GLuint buffer);
     void bindPixelPackBuffer(GLuint buffer);
     void bindPixelUnpackBuffer(GLuint buffer);
     void useProgram(GLuint program);
     void linkProgram(GLuint program);
     void setProgramBinary(GLuint program, GLenum binaryFormat, const void *binary, GLint length);
     void bindTransformFeedback(GLuint transformFeedback);
 
-    void beginQuery(GLenum target, GLuint query);
-    void endQuery(GLenum target);
+    Error beginQuery(GLenum target, GLuint query);
+    Error endQuery(GLenum target);
 
     void setFramebufferZero(Framebuffer *framebuffer);
 
     void setRenderbufferStorage(GLsizei width, GLsizei height, GLenum internalformat, GLsizei samples);
 
     void setVertexAttribDivisor(GLuint index, GLuint divisor);
 
     void samplerParameteri(GLuint sampler, GLenum pname, GLint param);
@@ -165,42 +162,42 @@ class Context
     TransformFeedback *getTransformFeedback(GLuint handle) const;
 
     Texture *getTargetTexture(GLenum target) const;
     Texture2D *getTexture2D() const;
     TextureCubeMap *getTextureCubeMap() const;
     Texture3D *getTexture3D() const;
     Texture2DArray *getTexture2DArray() const;
 
-    Texture *getSamplerTexture(unsigned int sampler, TextureType type) const;
+    Texture *getSamplerTexture(unsigned int sampler, GLenum type) const;
 
     bool isSampler(GLuint samplerName) const;
 
     void getBooleanv(GLenum pname, GLboolean *params);
     void getFloatv(GLenum pname, GLfloat *params);
     void getIntegerv(GLenum pname, GLint *params);
     void getInteger64v(GLenum pname, GLint64 *params);
 
     bool getIndexedIntegerv(GLenum target, GLuint index, GLint *data);
     bool getIndexedInteger64v(GLenum target, GLuint index, GLint64 *data);
 
     bool getQueryParameterInfo(GLenum pname, GLenum *type, unsigned int *numParams);
     bool getIndexedQueryParameterInfo(GLenum target, GLenum *type, unsigned int *numParams);
 
-    void clear(GLbitfield mask);
-    void clearBufferfv(GLenum buffer, int drawbuffer, const float *values);
-    void clearBufferuiv(GLenum buffer, int drawbuffer, const unsigned int *values);
-    void clearBufferiv(GLenum buffer, int drawbuffer, const int *values);
-    void clearBufferfi(GLenum buffer, int drawbuffer, float depth, int stencil);
+    Error clear(GLbitfield mask);
+    Error clearBufferfv(GLenum buffer, int drawbuffer, const float *values);
+    Error clearBufferuiv(GLenum buffer, int drawbuffer, const unsigned int *values);
+    Error clearBufferiv(GLenum buffer, int drawbuffer, const int *values);
+    Error clearBufferfi(GLenum buffer, int drawbuffer, float depth, int stencil);
 
-    void readPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLsizei *bufSize, void* pixels);
-    void drawArrays(GLenum mode, GLint first, GLsizei count, GLsizei instances);
-    void drawElements(GLenum mode, GLsizei count, GLenum type,
-                      const GLvoid *indices, GLsizei instances,
-                      const rx::RangeUI &indexRange);
+    Error readPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLsizei *bufSize, void* pixels);
+    Error drawArrays(GLenum mode, GLint first, GLsizei count, GLsizei instances);
+    Error drawElements(GLenum mode, GLsizei count, GLenum type,
+                       const GLvoid *indices, GLsizei instances,
+                       const rx::RangeUI &indexRange);
     void sync(bool block);   // flush/finish
 
     void recordError(const Error &error);
 
     GLenum getError();
     GLenum getResetStatus();
     virtual bool isResetNotificationEnabled();
 
@@ -213,54 +210,54 @@ class Context
     const std::string &getRendererString() const;
 
     const std::string &getExtensionString() const;
     const std::string &getExtensionString(size_t idx) const;
     size_t getExtensionStringCount() const;
 
     void getCurrentReadFormatType(GLenum *internalFormat, GLenum *format, GLenum *type);
 
-    void blitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1,
-                         GLbitfield mask, GLenum filter);
+    Error blitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1,
+                          GLbitfield mask, GLenum filter);
 
     rx::Renderer *getRenderer() { return mRenderer; }
 
     State &getState() { return mState; }
     const State &getState() const { return mState; }
 
     void releaseShaderCompiler();
 
   private:
     DISALLOW_COPY_AND_ASSIGN(Context);
 
     // TODO: std::array may become unavailable using older versions of GCC
     typedef std::array<unsigned int, IMPLEMENTATION_MAX_FRAMEBUFFER_ATTACHMENTS> FramebufferTextureSerialArray;
 
-    bool applyRenderTarget(GLenum drawMode, bool ignoreViewport);
-    void applyState(GLenum drawMode);
-    void applyShaders(ProgramBinary *programBinary, bool transformFeedbackActive);
-    void applyTextures(SamplerType shaderType, Texture *textures[], TextureType *textureTypes, SamplerState *samplers,
-                       size_t textureCount, const FramebufferTextureSerialArray& framebufferSerials,
-                       size_t framebufferSerialCount);
-    bool applyUniformBuffers();
+    Error applyRenderTarget(GLenum drawMode, bool ignoreViewport);
+    Error applyState(GLenum drawMode);
+    Error applyShaders(ProgramBinary *programBinary, bool transformFeedbackActive);
+    Error applyTextures(ProgramBinary *programBinary, SamplerType shaderType, const FramebufferTextureSerialArray &framebufferSerials,
+                        size_t framebufferSerialCount);
+    Error applyTextures(ProgramBinary *programBinary);
+    Error applyUniformBuffers();
     bool applyTransformFeedbackBuffers();
     void markTransformFeedbackUsage();
 
     void detachBuffer(GLuint buffer);
     void detachTexture(GLuint texture);
     void detachFramebuffer(GLuint framebuffer);
     void detachRenderbuffer(GLuint renderbuffer);
     void detachVertexArray(GLuint vertexArray);
     void detachTransformFeedback(GLuint transformFeedback);
     void detachSampler(GLuint sampler);
 
-    void generateSwizzles(Texture *textures[], size_t count);
-    size_t getCurrentTexturesAndSamplerStates(ProgramBinary *programBinary, SamplerType type, Texture **outTextures,
-                                              TextureType *outTextureTypes, SamplerState *outSamplers);
-    Texture *getIncompleteTexture(TextureType type);
+    Error generateSwizzles(ProgramBinary *programBinary, SamplerType type);
+    Error generateSwizzles(ProgramBinary *programBinary);
+
+    Texture *getIncompleteTexture(GLenum type);
 
     bool skipDraw(GLenum drawMode);
 
     void initRendererString();
     void initExtensionStrings();
 
     size_t getBoundFramebufferTextureSerials(FramebufferTextureSerialArray *outSerialArray);
 
@@ -271,20 +268,19 @@ class Context
     TextureCapsMap mTextureCaps;
     Extensions mExtensions;
 
     rx::Renderer *const mRenderer;
     State mState;
 
     int mClientVersion;
 
-    BindingPointer<Texture2D> mTexture2DZero;
-    BindingPointer<TextureCubeMap> mTextureCubeMapZero;
-    BindingPointer<Texture3D> mTexture3DZero;
-    BindingPointer<Texture2DArray> mTexture2DArrayZero;
+    typedef std::map< GLenum, BindingPointer<Texture> > TextureMap;
+    TextureMap mZeroTextures;
+    TextureMap mIncompleteTextures;
 
     typedef std::unordered_map<GLuint, Framebuffer*> FramebufferMap;
     FramebufferMap mFramebufferMap;
     HandleAllocator mFramebufferHandleAllocator;
 
     typedef std::unordered_map<GLuint, FenceNV*> FenceNVMap;
     FenceNVMap mFenceNVMap;
     HandleAllocator mFenceNVHandleAllocator;
@@ -301,18 +297,16 @@ class Context
     typedef std::unordered_map<GLuint, TransformFeedback*> TransformFeedbackMap;
     TransformFeedbackMap mTransformFeedbackMap;
     HandleAllocator mTransformFeedbackAllocator;
 
     std::string mRendererString;
     std::string mExtensionString;
     std::vector<std::string> mExtensionStrings;
 
-    BindingPointer<Texture> mIncompleteTextures[TEXTURE_TYPE_COUNT];
-
     // Recorded errors
     typedef std::set<GLenum> ErrorSet;
     ErrorSet mErrors;
 
     // Current/lost context flags
     bool mHasBeenCurrent;
     bool mContextLost;
     GLenum mResetStatus;
--- a/gfx/angle/src/libGLESv2/Framebuffer.cpp
+++ b/gfx/angle/src/libGLESv2/Framebuffer.cpp
@@ -11,19 +11,64 @@
 #include "libGLESv2/main.h"
 #include "libGLESv2/formatutils.h"
 #include "libGLESv2/Texture.h"
 #include "libGLESv2/Context.h"
 #include "libGLESv2/Renderbuffer.h"
 #include "libGLESv2/FramebufferAttachment.h"
 #include "libGLESv2/renderer/Renderer.h"
 #include "libGLESv2/renderer/RenderTarget.h"
+#include "libGLESv2/renderer/Workarounds.h"
+#include "libGLESv2/renderer/d3d/TextureD3D.h"
 
 #include "common/utilities.h"
 
+namespace rx
+{
+RenderTarget *GetAttachmentRenderTarget(gl::FramebufferAttachment *attachment)
+{
+    if (attachment->isTexture())
+    {
+        gl::Texture *texture = attachment->getTexture();
+        ASSERT(texture);
+        TextureD3D *textureD3D = TextureD3D::makeTextureD3D(texture->getImplementation());
+        const gl::ImageIndex *index = attachment->getTextureImageIndex();
+        ASSERT(index);
+        return textureD3D->getRenderTarget(*index);
+    }
+
+    gl::Renderbuffer *renderbuffer = attachment->getRenderbuffer();
+    ASSERT(renderbuffer);
+
+    // TODO: cast to RenderbufferD3D
+    return renderbuffer->getStorage()->getRenderTarget();
+}
+
+// Note: RenderTarget serials should ideally be in the RenderTargets themselves.
+unsigned int GetAttachmentSerial(gl::FramebufferAttachment *attachment)
+{
+    if (attachment->isTexture())
+    {
+        gl::Texture *texture = attachment->getTexture();
+        ASSERT(texture);
+        TextureD3D *textureD3D = TextureD3D::makeTextureD3D(texture->getImplementation());
+        const gl::ImageIndex *index = attachment->getTextureImageIndex();
+        ASSERT(index);
+        return textureD3D->getRenderTargetSerial(*index);
+    }
+
+    gl::Renderbuffer *renderbuffer = attachment->getRenderbuffer();
+    ASSERT(renderbuffer);
+
+    // TODO: cast to RenderbufferD3D
+    return renderbuffer->getStorage()->getSerial();
+}
+
+}
+
 namespace gl
 {
 
 Framebuffer::Framebuffer(rx::Renderer *renderer, GLuint id)
     : mRenderer(renderer),
       mId(id),
       mReadBufferState(GL_COLOR_ATTACHMENT0_EXT),
       mDepthbuffer(NULL),
@@ -42,40 +87,39 @@ Framebuffer::~Framebuffer()
     for (unsigned int colorAttachment = 0; colorAttachment < IMPLEMENTATION_MAX_DRAW_BUFFERS; colorAttachment++)
     {
         SafeDelete(mColorbuffers[colorAttachment]);
     }
     SafeDelete(mDepthbuffer);
     SafeDelete(mStencilbuffer);
 }
 
-FramebufferAttachment *Framebuffer::createAttachment(GLenum type, GLuint handle, GLint level, GLint layer) const
+FramebufferAttachment *Framebuffer::createAttachment(GLenum binding, GLenum type, GLuint handle, GLint level, GLint layer) const
 {
     if (handle == 0)
     {
         return NULL;
     }
 
     gl::Context *context = gl::getContext();
 
     switch (type)
     {
       case GL_NONE:
         return NULL;
 
       case GL_RENDERBUFFER:
-        return new RenderbufferAttachment(context->getRenderbuffer(handle));
+        return new RenderbufferAttachment(binding, context->getRenderbuffer(handle));
 
       case GL_TEXTURE_2D:
         {
             Texture *texture = context->getTexture(handle);
             if (texture && texture->getTarget() == GL_TEXTURE_2D)
             {
-                Texture2D *tex2D = static_cast<Texture2D*>(texture);
-                return new Texture2DAttachment(tex2D, level);
+                return new TextureAttachment(binding, texture, ImageIndex::Make2D(level));
             }
             else
             {
                 return NULL;
             }
         }
 
       case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
@@ -83,46 +127,43 @@ FramebufferAttachment *Framebuffer::crea
       case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
       case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
       case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
       case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
         {
             Texture *texture = context->getTexture(handle);
             if (texture && texture->getTarget() == GL_TEXTURE_CUBE_MAP)
             {
-                TextureCubeMap *texCube = static_cast<TextureCubeMap*>(texture);
-                return new TextureCubeMapAttachment(texCube, type, level);
+                return new TextureAttachment(binding, texture, ImageIndex::MakeCube(type, level));
             }
             else
             {
                 return NULL;
             }
         }
 
       case GL_TEXTURE_3D:
         {
             Texture *texture = context->getTexture(handle);
             if (texture && texture->getTarget() == GL_TEXTURE_3D)
             {
-                Texture3D *tex3D = static_cast<Texture3D*>(texture);
-                return new Texture3DAttachment(tex3D, level, layer);
+                return new TextureAttachment(binding, texture, ImageIndex::Make3D(level, layer));
             }
             else
             {
                 return NULL;
             }
         }
 
       case GL_TEXTURE_2D_ARRAY:
         {
             Texture *texture = context->getTexture(handle);
             if (texture && texture->getTarget() == GL_TEXTURE_2D_ARRAY)
             {
-                Texture2DArray *tex2DArray = static_cast<Texture2DArray*>(texture);
-                return new Texture2DArrayAttachment(tex2DArray, level, layer);
+                return new TextureAttachment(binding, texture, ImageIndex::Make2DArray(level, layer));
             }
             else
             {
                 return NULL;
             }
         }
 
       default:
@@ -130,46 +171,47 @@ FramebufferAttachment *Framebuffer::crea
         return NULL;
     }
 }
 
 void Framebuffer::setColorbuffer(unsigned int colorAttachment, GLenum type, GLuint colorbuffer, GLint level, GLint layer)
 {
     ASSERT(colorAttachment < IMPLEMENTATION_MAX_DRAW_BUFFERS);
     SafeDelete(mColorbuffers[colorAttachment]);
-    mColorbuffers[colorAttachment] = createAttachment(type, colorbuffer, level, layer);
+    GLenum binding = colorAttachment + GL_COLOR_ATTACHMENT0;
+    mColorbuffers[colorAttachment] = createAttachment(binding, type, colorbuffer, level, layer);
 }
 
 void Framebuffer::setDepthbuffer(GLenum type, GLuint depthbuffer, GLint level, GLint layer)
 {
     SafeDelete(mDepthbuffer);
-    mDepthbuffer = createAttachment(type, depthbuffer, level, layer);
+    mDepthbuffer = createAttachment(GL_DEPTH_ATTACHMENT, type, depthbuffer, level, layer);
 }
 
 void Framebuffer::setStencilbuffer(GLenum type, GLuint stencilbuffer, GLint level, GLint layer)
 {
     SafeDelete(mStencilbuffer);
-    mStencilbuffer = createAttachment(type, stencilbuffer, level, layer);
+    mStencilbuffer = createAttachment(GL_STENCIL_ATTACHMENT, type, stencilbuffer, level, layer);
 }
 
 void Framebuffer::setDepthStencilBuffer(GLenum type, GLuint depthStencilBuffer, GLint level, GLint layer)
 {
-    FramebufferAttachment *attachment = createAttachment(type, depthStencilBuffer, level, layer);
+    FramebufferAttachment *attachment = createAttachment(GL_DEPTH_STENCIL_ATTACHMENT, type, depthStencilBuffer, level, layer);
 
     SafeDelete(mDepthbuffer);
     SafeDelete(mStencilbuffer);
 
     // ensure this is a legitimate depth+stencil format
     if (attachment && attachment->getDepthSize() > 0 && attachment->getStencilSize() > 0)
     {
         mDepthbuffer = attachment;
 
         // Make a new attachment object to ensure we do not double-delete
         // See angle issue 686
-        mStencilbuffer = createAttachment(type, depthStencilBuffer, level, layer);
+        mStencilbuffer = createAttachment(GL_DEPTH_STENCIL_ATTACHMENT, type, depthStencilBuffer, level, layer);
     }
 }
 
 void Framebuffer::detachTexture(GLuint textureId)
 {
     for (unsigned int colorAttachment = 0; colorAttachment < IMPLEMENTATION_MAX_DRAW_BUFFERS; colorAttachment++)
     {
         FramebufferAttachment *attachment = mColorbuffers[colorAttachment];
@@ -566,81 +608,47 @@ void Framebuffer::invalidate(const Caps 
     GLuint maxDimension = caps.maxRenderbufferSize;
     invalidateSub(caps, numAttachments, attachments, 0, 0, maxDimension, maxDimension);
 }
 
 void Framebuffer::invalidateSub(const Caps &caps, GLsizei numAttachments, const GLenum *attachments,
                                 GLint x, GLint y, GLsizei width, GLsizei height)
 {
     ASSERT(completeness() == GL_FRAMEBUFFER_COMPLETE);
-    for (int i = 0; i < numAttachments; ++i)
+    for (GLsizei attachIndex = 0; attachIndex < numAttachments; ++attachIndex)
     {
-        rx::RenderTarget *renderTarget = NULL;
+        GLenum attachmentTarget = attachments[attachIndex];
 
-        if (attachments[i] >= GL_COLOR_ATTACHMENT0 && attachments[i] <= GL_COLOR_ATTACHMENT15)
+        gl::FramebufferAttachment *attachment =
+            (attachmentTarget == GL_DEPTH_STENCIL_ATTACHMENT) ? getDepthOrStencilbuffer() :
+                                                                getAttachment(attachmentTarget);
+
+        if (attachment)
         {
-            gl::FramebufferAttachment *attachment = getColorbuffer(attachments[i] - GL_COLOR_ATTACHMENT0);
-            if (attachment)
-            {
-                renderTarget = attachment->getRenderTarget();
-            }
-        }
-        else if (attachments[i] == GL_COLOR)
-        {
-            gl::FramebufferAttachment *attachment = getColorbuffer(0);
-            if (attachment)
-            {
-                renderTarget = attachment->getRenderTarget();
-            }
-        }
-        else
-        {
-            gl::FramebufferAttachment *attachment = NULL;
-            switch (attachments[i])
+            rx::RenderTarget *renderTarget = rx::GetAttachmentRenderTarget(attachment);
+            if (renderTarget)
             {
-              case GL_DEPTH_ATTACHMENT:
-              case GL_DEPTH:
-                attachment = mDepthbuffer;
-                break;
-              case GL_STENCIL_ATTACHMENT:
-              case GL_STENCIL:
-                attachment = mStencilbuffer;
-                break;
-              case GL_DEPTH_STENCIL_ATTACHMENT:
-                attachment = getDepthOrStencilbuffer();
-                break;
-              default:
-                UNREACHABLE();
+                renderTarget->invalidate(x, y, width, height);
             }
-
-            if (attachment)
-            {
-                renderTarget = attachment->getRenderTarget();
-            }
-        }
-
-        if (renderTarget)
-        {
-            renderTarget->invalidate(x, y, width, height);
         }
     }
 }
 
 DefaultFramebuffer::DefaultFramebuffer(rx::Renderer *renderer, Colorbuffer *colorbuffer, DepthStencilbuffer *depthStencil)
     : Framebuffer(renderer, 0)
 {
     Renderbuffer *colorRenderbuffer = new Renderbuffer(0, colorbuffer);
-    mColorbuffers[0] = new RenderbufferAttachment(colorRenderbuffer);
+    mColorbuffers[0] = new RenderbufferAttachment(GL_BACK, colorRenderbuffer);
 
     Renderbuffer *depthStencilBuffer = new Renderbuffer(0, depthStencil);
 
     // Make a new attachment objects to ensure we do not double-delete
     // See angle issue 686
-    mDepthbuffer = (depthStencilBuffer->getDepthSize() != 0 ? new RenderbufferAttachment(depthStencilBuffer) : NULL);
-    mStencilbuffer = (depthStencilBuffer->getStencilSize() != 0 ? new RenderbufferAttachment(depthStencilBuffer) : NULL);
+    mDepthbuffer = (depthStencilBuffer->getDepthSize() != 0 ? new RenderbufferAttachment(GL_DEPTH_ATTACHMENT, depthStencilBuffer) : NULL);
+    mStencilbuffer = (depthStencilBuffer->getStencilSize() != 0 ? new RenderbufferAttachment(GL_STENCIL_ATTACHMENT, depthStencilBuffer) : NULL);
 
     mDrawBufferStates[0] = GL_BACK;
     mReadBufferState = GL_BACK;
 }
 
 int Framebuffer::getSamples() const
 {
     if (completeness() == GL_FRAMEBUFFER_COMPLETE)
@@ -663,27 +671,51 @@ bool Framebuffer::hasValidDepthStencil()
 {
     // A valid depth-stencil attachment has the same resource bound to both the
     // depth and stencil attachment points.
     return (mDepthbuffer && mStencilbuffer &&
             mDepthbuffer->type() == mStencilbuffer->type() &&
             mDepthbuffer->id() == mStencilbuffer->id());
 }
 
+ColorbufferInfo Framebuffer::getColorbuffersForRender() const
+{
+    ColorbufferInfo colorbuffersForRender;
+
+    for (size_t colorAttachment = 0; colorAttachment < IMPLEMENTATION_MAX_DRAW_BUFFERS; ++colorAttachment)
+    {
+        GLenum drawBufferState = mDrawBufferStates[colorAttachment];
+        FramebufferAttachment *colorbuffer = mColorbuffers[colorAttachment];
+
+        if (colorbuffer != NULL && drawBufferState != GL_NONE)
+        {
+            ASSERT(drawBufferState == GL_BACK || drawBufferState == (GL_COLOR_ATTACHMENT0_EXT + colorAttachment));
+            colorbuffersForRender.push_back(colorbuffer);
+        }
+        else if (!mRenderer->getWorkarounds().mrtPerfWorkaround)
+        {
+            colorbuffersForRender.push_back(NULL);
+        }
+    }
+
+    return colorbuffersForRender;
+}
+
 GLenum DefaultFramebuffer::completeness() const
 {
     // The default framebuffer *must* always be complete, though it may not be
     // subject to the same rules as application FBOs. ie, it could have 0x0 size.
     return GL_FRAMEBUFFER_COMPLETE;
 }
 
 FramebufferAttachment *DefaultFramebuffer::getAttachment(GLenum attachment) const
 {
     switch (attachment)
     {
+      case GL_COLOR:
       case GL_BACK:
         return getColorbuffer(0);
       case GL_DEPTH:
         return getDepthbuffer();
       case GL_STENCIL:
         return getStencilbuffer();
       case GL_DEPTH_STENCIL:
         return getDepthStencilBuffer();
--- a/gfx/angle/src/libGLESv2/Framebuffer.h
+++ b/gfx/angle/src/libGLESv2/Framebuffer.h
@@ -5,16 +5,18 @@
 //
 
 // Framebuffer.h: Defines the gl::Framebuffer class. Implements GL framebuffer
 // objects and related functionality. [OpenGL ES 2.0.24] section 4.4 page 105.
 
 #ifndef LIBGLESV2_FRAMEBUFFER_H_
 #define LIBGLESV2_FRAMEBUFFER_H_
 
+#include <vector>
+
 #include "common/angleutils.h"
 #include "common/RefCountObject.h"
 #include "Constants.h"
 
 namespace rx
 {
 class Renderer;
 }
@@ -23,16 +25,18 @@ namespace gl
 {
 class FramebufferAttachment;
 class Colorbuffer;
 class Depthbuffer;
 class Stencilbuffer;
 class DepthStencilbuffer;
 struct Caps;
 
+typedef std::vector<FramebufferAttachment *> ColorbufferInfo;
+
 class Framebuffer
 {
   public:
     Framebuffer(rx::Renderer *renderer, GLuint id);
 
     virtual ~Framebuffer();
 
     GLuint id() const { return mId; }
@@ -67,41 +71,56 @@ class Framebuffer
 
     virtual GLenum completeness() const;
     bool hasValidDepthStencil() const;
 
     void invalidate(const Caps &caps, GLsizei numAttachments, const GLenum *attachments);
     void invalidateSub(const Caps &caps, GLsizei numAttachments, const GLenum *attachments,
                        GLint x, GLint y, GLsizei width, GLsizei height);
 
+    // Use this method to retrieve the color buffer map when doing rendering.
+    // It will apply a workaround for poor shader performance on some systems
+    // by compacting the list to skip NULL values.
+    ColorbufferInfo getColorbuffersForRender() const;
+
   protected:
     rx::Renderer *mRenderer;
 
     GLuint mId;
 
     FramebufferAttachment *mColorbuffers[IMPLEMENTATION_MAX_DRAW_BUFFERS];
     GLenum mDrawBufferStates[IMPLEMENTATION_MAX_DRAW_BUFFERS];
     GLenum mReadBufferState;
 
     FramebufferAttachment *mDepthbuffer;
     FramebufferAttachment *mStencilbuffer;
 
-private:
+  private:
     DISALLOW_COPY_AND_ASSIGN(Framebuffer);
 
-    FramebufferAttachment *createAttachment(GLenum type, GLuint handle, GLint level, GLint layer) const;
+    FramebufferAttachment *createAttachment(GLenum binding, GLenum type, GLuint handle, GLint level, GLint layer) const;
 };
 
 class DefaultFramebuffer : public Framebuffer
 {
   public:
     DefaultFramebuffer(rx::Renderer *Renderer, Colorbuffer *colorbuffer, DepthStencilbuffer *depthStencil);
 
     virtual GLenum completeness() const;
     virtual FramebufferAttachment *getAttachment(GLenum attachment) const;
 
   private:
     DISALLOW_COPY_AND_ASSIGN(DefaultFramebuffer);
 };
 
 }
 
+namespace rx
+{
+class RenderTarget;
+
+// TODO: place this in FramebufferD3D.h
+RenderTarget *GetAttachmentRenderTarget(gl::FramebufferAttachment *attachment);
+unsigned int GetAttachmentSerial(gl::FramebufferAttachment *attachment);
+
+}
+
 #endif   // LIBGLESV2_FRAMEBUFFER_H_
--- a/gfx/angle/src/libGLESv2/FramebufferAttachment.cpp
+++ b/gfx/angle/src/libGLESv2/FramebufferAttachment.cpp
@@ -17,17 +17,18 @@
 
 #include "common/utilities.h"
 
 namespace gl
 {
 
 ////// FramebufferAttachment Implementation //////
 
-FramebufferAttachment::FramebufferAttachment()
+FramebufferAttachment::FramebufferAttachment(GLenum binding)
+    : mBinding(binding)
 {
 }
 
 FramebufferAttachment::~FramebufferAttachment()
 {
 }
 
 GLuint FramebufferAttachment::getRedSize() const
@@ -70,351 +71,105 @@ GLenum FramebufferAttachment::getColorEn
     return GetInternalFormatInfo(getActualFormat()).colorEncoding;
 }
 
 bool FramebufferAttachment::isTexture() const
 {
     return (type() != GL_RENDERBUFFER);
 }
 
-///// Texture2DAttachment Implementation ////////
-
-Texture2DAttachment::Texture2DAttachment(Texture2D *texture, GLint level) : mLevel(level)
-{
-    mTexture2D.set(texture);
-}
-
-Texture2DAttachment::~Texture2DAttachment()
-{
-    mTexture2D.set(NULL);
-}
-
-rx::RenderTarget *Texture2DAttachment::getRenderTarget()
-{
-    return mTexture2D->getRenderTarget(mLevel);
-}
+///// TextureAttachment Implementation ////////
 
-rx::TextureStorage *Texture2DAttachment::getTextureStorage()
-{
-    return mTexture2D->getNativeTexture()->getStorageInstance();
-}
-
-GLsizei Texture2DAttachment::getWidth() const
-{
-    return mTexture2D->getWidth(mLevel);
-}
-
-GLsizei Texture2DAttachment::getHeight() const
-{
-    return mTexture2D->getHeight(mLevel);
-}
-
-GLenum Texture2DAttachment::getInternalFormat() const
+TextureAttachment::TextureAttachment(GLenum binding, Texture *texture, const ImageIndex &index)
+    : FramebufferAttachment(binding),
+      mIndex(index)
 {
-    return mTexture2D->getInternalFormat(mLevel);
-}
-
-GLenum Texture2DAttachment::getActualFormat() const
-{
-    return mTexture2D->getActualFormat(mLevel);
-}
-
-GLsizei Texture2DAttachment::getSamples() const
-{
-    return 0;
-}
-
-unsigned int Texture2DAttachment::getSerial() const
-{
-    return mTexture2D->getRenderTargetSerial(mLevel);
-}
-
-GLuint Texture2DAttachment::id() const
-{
-    return mTexture2D->id();
-}
-
-GLenum Texture2DAttachment::type() const
-{
-    return GL_TEXTURE_2D;
-}
-
-GLint Texture2DAttachment::mipLevel() const
-{
-    return mLevel;
-}
-
-GLint Texture2DAttachment::layer() const
-{
-    return 0;
+    mTexture.set(texture);
 }
 
-unsigned int Texture2DAttachment::getTextureSerial() const
-{
-    return mTexture2D->getTextureSerial();
-}
-
-///// TextureCubeMapAttachment Implementation ////////
-
-TextureCubeMapAttachment::TextureCubeMapAttachment(TextureCubeMap *texture, GLenum faceTarget, GLint level)
-    : mFaceTarget(faceTarget), mLevel(level)
-{
-    mTextureCubeMap.set(texture);
-}
-
-TextureCubeMapAttachment::~TextureCubeMapAttachment()
+TextureAttachment::~TextureAttachment()
 {
-    mTextureCubeMap.set(NULL);
-}
-
-rx::RenderTarget *TextureCubeMapAttachment::getRenderTarget()
-{
-    return mTextureCubeMap->getRenderTarget(mFaceTarget, mLevel);
-}
-
-rx::TextureStorage *TextureCubeMapAttachment::getTextureStorage()
-{
-    return mTextureCubeMap->getNativeTexture()->getStorageInstance();
-}
-
-GLsizei TextureCubeMapAttachment::getWidth() const
-{
-    return mTextureCubeMap->getWidth(mFaceTarget, mLevel);
+    mTexture.set(NULL);
 }
 
-GLsizei TextureCubeMapAttachment::getHeight() const
-{
-    return mTextureCubeMap->getHeight(mFaceTarget, mLevel);
-}
-
-GLenum TextureCubeMapAttachment::getInternalFormat() const
-{
-    return mTextureCubeMap->getInternalFormat(mFaceTarget, mLevel);
-}
-
-GLenum TextureCubeMapAttachment::getActualFormat() const
-{
-    return mTextureCubeMap->getActualFormat(mFaceTarget, mLevel);
-}
-
-GLsizei TextureCubeMapAttachment::getSamples() const
-{
-    return 0;
-}
-
-unsigned int TextureCubeMapAttachment::getSerial() const
-{
-    return mTextureCubeMap->getRenderTargetSerial(mFaceTarget, mLevel);
-}
-
-GLuint TextureCubeMapAttachment::id() const
-{
-    return mTextureCubeMap->id();
-}
-
-GLenum TextureCubeMapAttachment::type() const
-{
-    return mFaceTarget;
-}
-
-GLint TextureCubeMapAttachment::mipLevel() const
-{
-    return mLevel;
-}
-
-GLint TextureCubeMapAttachment::layer() const
+GLsizei TextureAttachment::getSamples() const
 {
     return 0;
 }
 
-unsigned int TextureCubeMapAttachment::getTextureSerial() const
+GLuint TextureAttachment::id() const
 {
-    return mTextureCubeMap->getTextureSerial();
-}
-
-///// Texture3DAttachment Implementation ////////
-
-Texture3DAttachment::Texture3DAttachment(Texture3D *texture, GLint level, GLint layer)
-    : mLevel(level), mLayer(layer)
-{
-    mTexture3D.set(texture);
-}
-
-Texture3DAttachment::~Texture3DAttachment()
-{
-    mTexture3D.set(NULL);
+    return mTexture->id();
 }
 
-rx::RenderTarget *Texture3DAttachment::getRenderTarget()
-{
-    return mTexture3D->getRenderTarget(mLevel, mLayer);
-}
-
-rx::TextureStorage *Texture3DAttachment::getTextureStorage()
+GLsizei TextureAttachment::getWidth() const
 {
-    return mTexture3D->getNativeTexture()->getStorageInstance();
-}
-
-GLsizei Texture3DAttachment::getWidth() const
-{
-    return mTexture3D->getWidth(mLevel);
-}
-
-GLsizei Texture3DAttachment::getHeight() const
-{
-    return mTexture3D->getHeight(mLevel);
+    return mTexture->getWidth(mIndex);
 }
 
-GLenum Texture3DAttachment::getInternalFormat() const
-{
-    return mTexture3D->getInternalFormat(mLevel);
-}
-
-GLenum Texture3DAttachment::getActualFormat() const
+GLsizei TextureAttachment::getHeight() const
 {
-    return mTexture3D->getActualFormat(mLevel);
-}
-
-GLsizei Texture3DAttachment::getSamples() const
-{
-    return 0;
+    return mTexture->getHeight(mIndex);
 }
 
-unsigned int Texture3DAttachment::getSerial() const
+GLenum TextureAttachment::getInternalFormat() const
 {
-    return mTexture3D->getRenderTargetSerial(mLevel, mLayer);
-}
-
-GLuint Texture3DAttachment::id() const
-{
-    return mTexture3D->id();
+    return mTexture->getInternalFormat(mIndex);
 }
 
-GLenum Texture3DAttachment::type() const
-{
-    return GL_TEXTURE_3D;
-}
-
-GLint Texture3DAttachment::mipLevel() const
+GLenum TextureAttachment::getActualFormat() const
 {
-    return mLevel;
-}
-
-GLint Texture3DAttachment::layer() const
-{
-    return mLayer;
+    return mTexture->getActualFormat(mIndex);
 }
 
-unsigned int Texture3DAttachment::getTextureSerial() const
+GLenum TextureAttachment::type() const
 {
-    return mTexture3D->getTextureSerial();
-}
-
-////// Texture2DArrayAttachment Implementation //////
-
-Texture2DArrayAttachment::Texture2DArrayAttachment(Texture2DArray *texture, GLint level, GLint layer)
-    : mLevel(level), mLayer(layer)
-{
-    mTexture2DArray.set(texture);
-}
-
-Texture2DArrayAttachment::~Texture2DArrayAttachment()
-{
-    mTexture2DArray.set(NULL);
+    return mIndex.type;
 }
 
-rx::RenderTarget *Texture2DArrayAttachment::getRenderTarget()
+GLint TextureAttachment::mipLevel() const
 {
-    return mTexture2DArray->getRenderTarget(mLevel, mLayer);
+    return mIndex.mipIndex;
 }
 
-rx::TextureStorage *Texture2DArrayAttachment::getTextureStorage()
-{
-    return mTexture2DArray->getNativeTexture()->getStorageInstance();
-}
-
-GLsizei Texture2DArrayAttachment::getWidth() const
+GLint TextureAttachment::layer() const
 {
-    return mTexture2DArray->getWidth(mLevel);
-}
-
-GLsizei Texture2DArrayAttachment::getHeight() const
-{
-    return mTexture2DArray->getHeight(mLevel);
+    return mIndex.layerIndex;
 }
 
-GLenum Texture2DArrayAttachment::getInternalFormat() const
-{
-    return mTexture2DArray->getInternalFormat(mLevel);
-}
-
-GLenum Texture2DArrayAttachment::getActualFormat() const
+Texture *TextureAttachment::getTexture()
 {
-    return mTexture2DArray->getActualFormat(mLevel);
-}
-
-GLsizei Texture2DArrayAttachment::getSamples() const
-{
-    return 0;
-}
-
-unsigned int Texture2DArrayAttachment::getSerial() const
-{
-    return mTexture2DArray->getRenderTargetSerial(mLevel, mLayer);
+    return mTexture.get();
 }
 
-GLuint Texture2DArrayAttachment::id() const
+const ImageIndex *TextureAttachment::getTextureImageIndex() const
 {
-    return mTexture2DArray->id();
-}
-
-GLenum Texture2DArrayAttachment::type() const
-{
-    return GL_TEXTURE_2D_ARRAY;
+    return &mIndex;
 }
 
-GLint Texture2DArrayAttachment::mipLevel() const
-{
-    return mLevel;
-}
-
-GLint Texture2DArrayAttachment::layer() const
+Renderbuffer *TextureAttachment::getRenderbuffer()
 {
-    return mLayer;
-}
-
-unsigned int Texture2DArrayAttachment::getTextureSerial() const
-{
-    return mTexture2DArray->getTextureSerial();
+    UNREACHABLE();
+    return NULL;
 }
 
 ////// RenderbufferAttachment Implementation //////
 
-RenderbufferAttachment::RenderbufferAttachment(Renderbuffer *renderbuffer)
+RenderbufferAttachment::RenderbufferAttachment(GLenum binding, Renderbuffer *renderbuffer)
+    : FramebufferAttachment(binding)
 {
     ASSERT(renderbuffer);
     mRenderbuffer.set(renderbuffer);
 }
 
 RenderbufferAttachment::~RenderbufferAttachment()
 {
     mRenderbuffer.set(NULL);
 }
 
-rx::RenderTarget *RenderbufferAttachment::getRenderTarget()
-{
-    return mRenderbuffer->getStorage()->getRenderTarget();
-}
-
-rx::TextureStorage *RenderbufferAttachment::getTextureStorage()
-{
-    UNREACHABLE();
-    return NULL;
-}
-
 GLsizei RenderbufferAttachment::getWidth() const
 {
     return mRenderbuffer->getWidth();
 }
 
 GLsizei RenderbufferAttachment::getHeight() const
 {
     return mRenderbuffer->getHeight();
@@ -430,21 +185,16 @@ GLenum RenderbufferAttachment::getActual
     return mRenderbuffer->getActualFormat();
 }
 
 GLsizei RenderbufferAttachment::getSamples() const
 {
     return mRenderbuffer->getStorage()->getSamples();
 }
 
-unsigned int RenderbufferAttachment::getSerial() const
-{
-    return mRenderbuffer->getStorage()->getSerial();
-}
-
 GLuint RenderbufferAttachment::id() const
 {
     return mRenderbuffer->id();
 }
 
 GLenum RenderbufferAttachment::type() const
 {
     return GL_RENDERBUFFER;
@@ -455,15 +205,26 @@ GLint RenderbufferAttachment::mipLevel()
     return 0;
 }
 
 GLint RenderbufferAttachment::layer() const
 {
     return 0;
 }
 
-unsigned int RenderbufferAttachment::getTextureSerial() const
+Texture *RenderbufferAttachment::getTexture()
 {
     UNREACHABLE();
-    return 0;
+    return NULL;
+}
+
+const ImageIndex *RenderbufferAttachment::getTextureImageIndex() const
+{
+    UNREACHABLE();
+    return NULL;
+}
+
+Renderbuffer *RenderbufferAttachment::get