Merge mozilla-central to mozilla-inbound
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Thu, 21 Jul 2016 16:26:18 +0200
changeset 346204 ab54bfc55266aaadc00567341bf494e907b0e4d7
parent 346203 0055f23a1374324df40c85c27b4ac417de6e2cc2 (current diff)
parent 346117 6b180266ac16e3226be33319ff710ddfa85f5836 (diff)
child 346205 3faf6a4932c13c6bef7b8834014650d9e237dc7f
push id6389
push userraliiev@mozilla.com
push dateMon, 19 Sep 2016 13:38:22 +0000
treeherdermozilla-beta@01d67bfe6c81 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone50.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 mozilla-central to mozilla-inbound
browser/themes/linux/customizableui/panelUIOverlay.css
browser/themes/osx/customizableui/panelUIOverlay.css
browser/themes/shared/customizableui/panelUIOverlay.inc.css
browser/themes/windows/customizableui/panelUIOverlay.css
devtools/client/framework/source-location.js
devtools/client/framework/test/browser_source-location-01.js
devtools/client/framework/test/browser_source-location-02.js
devtools/server/actors/utils/ScriptStore.js
devtools/server/tests/unit/test_ScriptStore.js
dom/html/HTMLMediaElement.cpp
dom/media/MediaCallbackID.cpp
dom/media/MediaCallbackID.h
toolkit/components/microformats/Microformats.js
toolkit/components/microformats/tests/.eslintrc
toolkit/components/microformats/tests/geo.html
toolkit/components/microformats/tests/mochitest.ini
toolkit/components/microformats/tests/test_Microformats.html
toolkit/components/microformats/tests/test_Microformats_add.html
toolkit/components/microformats/tests/test_Microformats_adr.html
toolkit/components/microformats/tests/test_Microformats_count.html
toolkit/components/microformats/tests/test_Microformats_geo.html
toolkit/components/microformats/tests/test_Microformats_getters.html
toolkit/components/microformats/tests/test_Microformats_hCalendar.html
toolkit/components/microformats/tests/test_Microformats_hCard.html
toolkit/components/microformats/tests/test_Microformats_negative.html
toolkit/components/microformats/tests/test_framerecursion.html
--- a/b2g/chrome/content/devtools/hud.js
+++ b/b2g/chrome/content/devtools/hud.js
@@ -20,17 +20,17 @@ XPCOMUtils.defineLazyGetter(this, 'Debug
   return devtools.require('devtools/shared/client/main').DebuggerClient;
 });
 
 XPCOMUtils.defineLazyGetter(this, 'WebConsoleUtils', function() {
   return devtools.require('devtools/shared/webconsole/utils').Utils;
 });
 
 XPCOMUtils.defineLazyGetter(this, 'EventLoopLagFront', function() {
-  return devtools.require('devtools/server/actors/eventlooplag').EventLoopLagFront;
+  return devtools.require('devtools/shared/fronts/eventlooplag').EventLoopLagFront;
 });
 
 XPCOMUtils.defineLazyGetter(this, 'PerformanceEntriesFront', function() {
   return devtools.require('devtools/server/actors/performance-entries').PerformanceEntriesFront;
 });
 
 XPCOMUtils.defineLazyGetter(this, 'MemoryFront', function() {
   return devtools.require('devtools/server/actors/memory').MemoryFront;
--- a/browser/base/content/browser.xul
+++ b/browser/base/content/browser.xul
@@ -6,17 +6,17 @@
 # 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/.
 
 <?xml-stylesheet href="chrome://browser/content/browser.css" type="text/css"?>
 <?xml-stylesheet href="chrome://browser/content/places/places.css" type="text/css"?>
 <?xml-stylesheet href="chrome://browser/content/usercontext/usercontext.css" type="text/css"?>
 <?xml-stylesheet href="chrome://devtools/skin/devtools-browser.css" type="text/css"?>
 <?xml-stylesheet href="chrome://browser/skin/controlcenter/panel.css" type="text/css"?>
-<?xml-stylesheet href="chrome://browser/skin/customizableui/panelUIOverlay.css" type="text/css"?>
+<?xml-stylesheet href="chrome://browser/skin/customizableui/panelUI.css" type="text/css"?>
 <?xml-stylesheet href="chrome://browser/skin/" type="text/css"?>
 <?xml-stylesheet href="chrome://browser/skin/browser-lightweightTheme.css" type="text/css"?>
 
 <?xul-overlay href="chrome://global/content/editMenuOverlay.xul"?>
 <?xul-overlay href="chrome://browser/content/baseMenuOverlay.xul"?>
 <?xul-overlay href="chrome://browser/content/places/placesOverlay.xul"?>
 
 # All DTD information is stored in a separate file so that it can be shared by
--- a/browser/base/content/test/general/browser.ini
+++ b/browser/base/content/test/general/browser.ini
@@ -275,16 +275,17 @@ tags = mcb
 skip-if = os == 'win'
 [browser_bug1064280_changeUrlInPinnedTab.js]
 [browser_accesskeys.js]
 [browser_clipboard.js]
 subsuite = clipboard
 [browser_clipboard_pastefile.js]
 [browser_contentAreaClick.js]
 [browser_contextmenu.js]
+subsuite = clipboard
 tags = fullscreen
 skip-if = toolkit == "gtk2" || toolkit == "gtk3" # disabled on Linux due to bug 513558
 [browser_contextmenu_input.js]
 skip-if = toolkit == "gtk2" || toolkit == "gtk3" # disabled on Linux due to bug 513558
 [browser_ctrlTab.js]
 [browser_datachoices_notification.js]
 skip-if = !datareporting
 [browser_decoderDoctor.js]
--- a/browser/components/extensions/ext-history.js
+++ b/browser/components/extensions/ext-history.js
@@ -94,27 +94,25 @@ var _observer;
 
 function getObserver() {
   if (!_observer) {
     _observer = {
       onDeleteURI: function(uri, guid, reason) {
         this.emit("visitRemoved", {allHistory: false, urls: [uri.spec]});
       },
       onVisit: function(uri, visitId, time, sessionId, referringId, transitionType, guid, hidden, visitCount, typed) {
-        PlacesUtils.promisePlaceInfo(guid).then(placeInfo => {
-          let data = {
-            id: guid,
-            url: uri.spec,
-            title: placeInfo.title,
-            lastVisitTime: time / 1000,  // time from Places is microseconds,
-            visitCount,
-            typedCount: typed,
-          };
-          this.emit("visited", data);
-        });
+        let data = {
+          id: guid,
+          url: uri.spec,
+          title: "",
+          lastVisitTime: time / 1000,  // time from Places is microseconds,
+          visitCount,
+          typedCount: typed,
+        };
+        this.emit("visited", data);
       },
       onBeginUpdateBatch: function() {},
       onEndUpdateBatch: function() {},
       onTitleChanged: function() {},
       onClearHistory: function() {
         this.emit("visitRemoved", {allHistory: true});
       },
       onPageChanged: function() {},
--- a/browser/components/extensions/test/browser/browser_ext_currentWindow.js
+++ b/browser/components/extensions/test/browser/browser_ext_currentWindow.js
@@ -1,18 +1,18 @@
 /* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set sts=2 sw=2 et tw=80: */
 "use strict";
 
 function genericChecker() {
   let kind = "background";
   let path = window.location.pathname;
-  if (path.indexOf("popup") != -1) {
+  if (path.includes("/popup.html")) {
     kind = "popup";
-  } else if (path.indexOf("page") != -1) {
+  } else if (path.includes("/page.html")) {
     kind = "page";
   }
 
   browser.test.onMessage.addListener((msg, ...args) => {
     if (msg == kind + "-check-current1") {
       browser.tabs.query({
         currentWindow: true,
       }, function(tabs) {
@@ -29,17 +29,17 @@ function genericChecker() {
         browser.test.sendMessage("result", window.id);
       });
     } else if (msg == kind + "-open-page") {
       browser.tabs.create({windowId: args[0], url: browser.runtime.getURL("page.html")});
     } else if (msg == kind + "-close-page") {
       browser.tabs.query({
         windowId: args[0],
       }, tabs => {
-        let tab = tabs.find(tab => tab.url.indexOf("page.html") != -1);
+        let tab = tabs.find(tab => tab.url.includes("/page.html"));
         browser.tabs.remove(tab.id, () => {
           browser.test.sendMessage("closed");
         });
       });
     }
   });
   browser.test.sendMessage(kind + "-ready");
 }
--- a/browser/components/extensions/test/browser/browser_ext_history.js
+++ b/browser/components/extensions/test/browser/browser_ext_history.js
@@ -454,17 +454,19 @@ add_task(function* test_on_visited() {
   yield PlacesUtils.history.insertMany(PAGE_INFOS);
 
   let onVisitedData = yield extension.awaitMessage("on-visited-data");
 
   function checkOnVisitedData(index, expected) {
     let onVisited = onVisitedData[index];
     ok(PlacesUtils.isValidGuid(onVisited.id), "onVisited received a valid id");
     is(onVisited.url, expected.url, "onVisited received the expected url");
-    is(onVisited.title, expected.title, "onVisited received the expected title");
+    // Title will be blank until bug 1287928 lands
+    // https://bugzilla.mozilla.org/show_bug.cgi?id=1287928
+    is(onVisited.title, "", "onVisited received a blank title");
     is(onVisited.lastVisitTime, expected.time, "onVisited received the expected time");
     is(onVisited.visitCount, expected.visitCount, "onVisited received the expected visitCount");
   }
 
   let expected = {
     url: PAGE_INFOS[0].url,
     title: PAGE_INFOS[0].title,
     time: PAGE_INFOS[0].visits[0].date.getTime(),
--- a/browser/installer/package-manifest.in
+++ b/browser/installer/package-manifest.in
@@ -143,16 +143,17 @@
 #endif
 
 ; [Components]
 @RESPATH@/browser/components/components.manifest
 @RESPATH@/components/alerts.xpt
 #ifdef ACCESSIBILITY
 #ifdef XP_WIN32
 @BINPATH@/AccessibleMarshal.dll
+@BINPATH@/IA2Marshal.dll
 #endif
 @RESPATH@/components/accessibility.xpt
 #endif
 @RESPATH@/components/appshell.xpt
 @RESPATH@/components/appstartup.xpt
 @RESPATH@/components/autocomplete.xpt
 @RESPATH@/components/autoconfig.xpt
 @RESPATH@/components/browser-element.xpt
--- a/browser/installer/windows/nsis/shared.nsh
+++ b/browser/installer/windows/nsis/shared.nsh
@@ -1365,16 +1365,17 @@
 ; directory.
 !macro PushFilesToCheck
   ; The first string to be pushed onto the stack MUST be "end" to indicate
   ; that there are no more files to check in $INSTDIR and the last string
   ; should be ${FileMainEXE} so if it is in use the CheckForFilesInUse macro
   ; returns after the first check.
   Push "end"
   Push "AccessibleMarshal.dll"
+  Push "IA2Marshal.dll"
   Push "freebl3.dll"
   Push "nssckbi.dll"
   Push "nspr4.dll"
   Push "nssdbm3.dll"
   Push "mozsqlite3.dll"
   Push "xpcom.dll"
   Push "crashreporter.exe"
   Push "updater.exe"
rename from browser/themes/linux/customizableui/panelUIOverlay.css
rename to browser/themes/linux/customizableui/panelUI.css
--- a/browser/themes/linux/customizableui/panelUIOverlay.css
+++ b/browser/themes/linux/customizableui/panelUI.css
@@ -1,13 +1,13 @@
 /* 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/customizableui/panelUIOverlay.inc.css
+%include ../../shared/customizableui/panelUI.inc.css
 
 .panel-subviews {
   background-color: -moz-dialog;
 }
 
 #BMB_bookmarksPopup > menuitem[type="checkbox"] {
   -moz-appearance: none !important; /* important, to override toolkit rule */
 }
--- a/browser/themes/linux/jar.mn
+++ b/browser/themes/linux/jar.mn
@@ -46,17 +46,17 @@ browser.jar:
   skin/classic/browser/Toolbar-inverted@2x.png
   skin/classic/browser/Toolbar-small.png
   skin/classic/browser/webRTC-indicator.css
 * skin/classic/browser/controlcenter/panel.css        (controlcenter/panel.css)
   skin/classic/browser/customizableui/background-noise-toolbar.png  (customizableui/background-noise-toolbar.png)
   skin/classic/browser/customizableui/customizeMode-gridTexture.png  (customizableui/customizeMode-gridTexture.png)
   skin/classic/browser/customizableui/customizeMode-separatorHorizontal.png  (customizableui/customizeMode-separatorHorizontal.png)
   skin/classic/browser/customizableui/customizeMode-separatorVertical.png  (customizableui/customizeMode-separatorVertical.png)
-* skin/classic/browser/customizableui/panelUIOverlay.css (customizableui/panelUIOverlay.css)
+* skin/classic/browser/customizableui/panelUI.css (customizableui/panelUI.css)
 * skin/classic/browser/downloads/allDownloadsViewOverlay.css   (downloads/allDownloadsViewOverlay.css)
   skin/classic/browser/downloads/buttons.png          (downloads/buttons.png)
   skin/classic/browser/downloads/download-glow-menuPanel.png (downloads/download-glow-menuPanel.png)
   skin/classic/browser/downloads/download-notification-finish.png (downloads/download-notification-finish.png)
   skin/classic/browser/downloads/download-notification-start.png (downloads/download-notification-start.png)
   skin/classic/browser/downloads/download-summary.png (downloads/download-summary.png)
 * skin/classic/browser/downloads/downloads.css        (downloads/downloads.css)
   skin/classic/browser/feeds/feedIcon.png             (feeds/feedIcon.png)
rename from browser/themes/osx/customizableui/panelUIOverlay.css
rename to browser/themes/osx/customizableui/panelUI.css
--- a/browser/themes/osx/customizableui/panelUIOverlay.css
+++ b/browser/themes/osx/customizableui/panelUI.css
@@ -1,13 +1,13 @@
 /* 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/customizableui/panelUIOverlay.inc.css
+%include ../../shared/customizableui/panelUI.inc.css
 
 .panel-subviews {
   background-color: hsla(0,0%,100%,.97);
 }
 
 .panelUI-grid .toolbarbutton-1 {
   margin-right: 0;
   margin-left: 0;
--- a/browser/themes/osx/jar.mn
+++ b/browser/themes/osx/jar.mn
@@ -67,17 +67,17 @@ browser.jar:
   skin/classic/browser/webRTC-indicator.css
 * skin/classic/browser/controlcenter/panel.css        (controlcenter/panel.css)
   skin/classic/browser/customizableui/background-noise-toolbar.png  (customizableui/background-noise-toolbar.png)
   skin/classic/browser/customizableui/customize-titleBar-toggle.png  (customizableui/customize-titleBar-toggle.png)
   skin/classic/browser/customizableui/customize-titleBar-toggle@2x.png  (customizableui/customize-titleBar-toggle@2x.png)
   skin/classic/browser/customizableui/customizeMode-gridTexture.png  (customizableui/customizeMode-gridTexture.png)
   skin/classic/browser/customizableui/customizeMode-separatorHorizontal.png  (customizableui/customizeMode-separatorHorizontal.png)
   skin/classic/browser/customizableui/customizeMode-separatorVertical.png  (customizableui/customizeMode-separatorVertical.png)
-* skin/classic/browser/customizableui/panelUIOverlay.css    (customizableui/panelUIOverlay.css)
+* skin/classic/browser/customizableui/panelUI.css    (customizableui/panelUI.css)
 * skin/classic/browser/downloads/allDownloadsViewOverlay.css (downloads/allDownloadsViewOverlay.css)
   skin/classic/browser/downloads/buttons.png                (downloads/buttons.png)
   skin/classic/browser/downloads/buttons@2x.png             (downloads/buttons@2x.png)
   skin/classic/browser/downloads/download-glow-menuPanel.png (downloads/download-glow-menuPanel.png)
   skin/classic/browser/downloads/download-glow-menuPanel@2x.png (downloads/download-glow-menuPanel@2x.png)
   skin/classic/browser/downloads/download-notification-finish.png  (downloads/download-notification-finish.png)
   skin/classic/browser/downloads/download-notification-finish@2x.png  (downloads/download-notification-finish@2x.png)
   skin/classic/browser/downloads/download-notification-start.png  (downloads/download-notification-start.png)
rename from browser/themes/shared/customizableui/panelUIOverlay.inc.css
rename to browser/themes/shared/customizableui/panelUI.inc.css
rename from browser/themes/windows/customizableui/panelUIOverlay.css
rename to browser/themes/windows/customizableui/panelUI.css
--- a/browser/themes/windows/customizableui/panelUIOverlay.css
+++ b/browser/themes/windows/customizableui/panelUI.css
@@ -1,13 +1,13 @@
 /* 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/customizableui/panelUIOverlay.inc.css
+%include ../../shared/customizableui/panelUI.inc.css
 
 .panel-subviews {
   background-color: -moz-field;
 }
 
 #PanelUI-contents #zoom-out-btn {
   padding-left: 12px;
   padding-right: 12px;
--- a/browser/themes/windows/jar.mn
+++ b/browser/themes/windows/jar.mn
@@ -81,17 +81,17 @@ browser.jar:
 * skin/classic/browser/controlcenter/panel.css                 (controlcenter/panel.css)
   skin/classic/browser/customizableui/background-noise-toolbar.png  (customizableui/background-noise-toolbar.png)
   skin/classic/browser/customizableui/customize-titleBar-toggle.png  (customizableui/customize-titleBar-toggle.png)
   skin/classic/browser/customizableui/customize-titleBar-toggle@2x.png  (customizableui/customize-titleBar-toggle@2x.png)
   skin/classic/browser/customizableui/customizeMode-gridTexture.png  (customizableui/customizeMode-gridTexture.png)
   skin/classic/browser/customizableui/customizeMode-separatorHorizontal.png  (customizableui/customizeMode-separatorHorizontal.png)
   skin/classic/browser/customizableui/customizeMode-separatorVertical.png  (customizableui/customizeMode-separatorVertical.png)
   skin/classic/browser/customizableui/menu-arrow.svg           (customizableui/menu-arrow.svg)
-* skin/classic/browser/customizableui/panelUIOverlay.css       (customizableui/panelUIOverlay.css)
+* skin/classic/browser/customizableui/panelUI.css       (customizableui/panelUI.css)
 * skin/classic/browser/downloads/allDownloadsViewOverlay.css   (downloads/allDownloadsViewOverlay.css)
   skin/classic/browser/downloads/buttons.png                   (downloads/buttons.png)
   skin/classic/browser/downloads/buttons-XP.png                (downloads/buttons-XP.png)
   skin/classic/browser/downloads/download-glow-menuPanel.png   (downloads/download-glow-menuPanel.png)
   skin/classic/browser/downloads/download-glow-menuPanel-XPVista7.png   (downloads/download-glow-menuPanel-XPVista7.png)
   skin/classic/browser/downloads/download-notification-finish.png (downloads/download-notification-finish.png)
   skin/classic/browser/downloads/download-notification-start.png (downloads/download-notification-start.png)
   skin/classic/browser/downloads/download-summary.png          (downloads/download-summary.png)
--- a/caps/nsIAddonPolicyService.idl
+++ b/caps/nsIAddonPolicyService.idl
@@ -29,16 +29,23 @@ interface nsIAddonPolicyService : nsISup
   /**
    * Returns the content security policy which applies to documents belonging
    * to the extension with the given ID. This may be either a custom policy,
    * if one was supplied, or the default policy if one was not.
    */
   AString getAddonCSP(in AString aAddonId);
 
   /**
+   * Returns the generated background page as a data-URI, if any. If the addon
+   * does not have an auto-generated background page, an empty string is
+   * returned.
+   */
+  ACString getGeneratedBackgroundPageUrl(in ACString aAddonId);
+
+  /**
    * Returns true if unprivileged code associated with the given addon may load
    * data from |aURI|.
    */
   boolean addonMayLoadURI(in AString aAddonId, in nsIURI aURI);
 
   /**
    * Returns true if a given extension:// URI is web-accessible.
    */
new file mode 100644
--- /dev/null
+++ b/devtools/client/framework/location-store.js
@@ -0,0 +1,103 @@
+/* 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";
+
+const SOURCE_TOKEN = "<:>";
+
+function LocationStore (store) {
+  this._store = store || new Map();
+}
+
+/**
+ * Method to get a promised location from the Store.
+ * @param location
+ * @returns Promise<Object>
+ */
+LocationStore.prototype.get = function (location) {
+  this._safeAccessInit(location.url);
+  return this._store.get(location.url).get(location);
+};
+
+/**
+ * Method to set a promised location to the Store
+ * @param location
+ * @param promisedLocation
+ */
+LocationStore.prototype.set = function (location, promisedLocation = null) {
+  this._safeAccessInit(location.url);
+  this._store.get(location.url).set(serialize(location), promisedLocation);
+};
+
+/**
+ * Utility method to verify if key exists in Store before accessing it.
+ * If not, initializing it.
+ * @param url
+ * @private
+ */
+LocationStore.prototype._safeAccessInit = function (url) {
+  if (!this._store.has(url)) {
+    this._store.set(url, new Map());
+  }
+};
+
+/**
+ * Utility proxy method to Map.clear() method
+ */
+LocationStore.prototype.clear = function () {
+  this._store.clear();
+};
+
+/**
+ * Retrieves an object containing all locations to be resolved when `source-updated`
+ * event is triggered.
+ * @param url
+ * @returns {Array<String>}
+ */
+LocationStore.prototype.getByURL = function (url){
+  if (this._store.has(url)) {
+    return [...this._store.get(url).keys()];
+  }
+  return [];
+};
+
+/**
+ * Invalidates the stale location promises from the store when `source-updated`
+ * event is triggered, and when FrameView unsubscribes from a location.
+ * @param url
+ */
+LocationStore.prototype.clearByURL = function (url) {
+  this._safeAccessInit(url);
+  this._store.set(url, new Map());
+};
+
+exports.LocationStore = LocationStore;
+exports.serialize = serialize;
+exports.deserialize = deserialize;
+
+/**
+ * Utility method to serialize the source
+ * @param source
+ * @returns {string}
+ */
+function serialize(source) {
+  let { url, line, column } = source;
+  line = line || 0;
+  column = column || 0;
+  return `${url}${SOURCE_TOKEN}${line}${SOURCE_TOKEN}${column}`;
+};
+
+/**
+ * Utility method to serialize the source
+ * @param source
+ * @returns Object
+ */
+function deserialize(source) {
+  let [ url, line, column ] = source.split(SOURCE_TOKEN);
+  line = parseInt(line);
+  column = parseInt(column);
+  if (column === 0) {
+    return { url, line };
+  }
+  return { url, line, column };
+};
--- a/devtools/client/framework/moz.build
+++ b/devtools/client/framework/moz.build
@@ -11,21 +11,22 @@ TEST_HARNESS_FILES.xpcshell.devtools.cli
 
 DevToolsModules(
     'about-devtools-toolbox.js',
     'attach-thread.js',
     'browser-menus.js',
     'devtools-browser.js',
     'devtools.js',
     'gDevTools.jsm',
+    'location-store.js',
     'menu-item.js',
     'menu.js',
     'selection.js',
     'sidebar.js',
-    'source-location.js',
+    'source-map-service.js',
     'target-from-url.js',
     'target.js',
     'toolbox-highlighter-utils.js',
     'toolbox-hosts.js',
     'toolbox-options.js',
     'toolbox.js',
     'ToolboxProcess.jsm',
 )
rename from devtools/client/framework/source-location.js
rename to devtools/client/framework/source-map-service.js
--- a/devtools/client/framework/source-location.js
+++ b/devtools/client/framework/source-map-service.js
@@ -1,98 +1,171 @@
 /* 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";
 
 const { Task } = require("devtools/shared/task");
-const { assert } = require("devtools/shared/DevToolsUtils");
+const EventEmitter = require("devtools/shared/event-emitter");
+const { LocationStore, serialize, deserialize } = require("./location-store");
 
 /**
  * A manager class that wraps a TabTarget and listens to source changes
  * from source maps and resolves non-source mapped locations to the source mapped
  * versions and back and forth, and creating smart elements with a location that
  * auto-update when the source changes (from pretty printing, source maps loading, etc)
  *
  * @param {TabTarget} target
  */
-function SourceLocationController(target) {
-  this.target = target;
-  this.locations = new Set();
+
+function SourceMapService(target) {
+  this._target = target;
+  this._locationStore = new LocationStore();
+  this._isInitialResolve = true;
+
+  EventEmitter.decorate(this);
 
   this._onSourceUpdated = this._onSourceUpdated.bind(this);
+  this._resolveLocation = this._resolveLocation.bind(this);
+  this._resolveAndUpdate = this._resolveAndUpdate.bind(this);
+  this.subscribe = this.subscribe.bind(this);
+  this.unsubscribe = this.unsubscribe.bind(this);
   this.reset = this.reset.bind(this);
   this.destroy = this.destroy.bind(this);
 
   target.on("source-updated", this._onSourceUpdated);
   target.on("navigate", this.reset);
   target.on("will-navigate", this.reset);
   target.on("close", this.destroy);
 }
 
-SourceLocationController.prototype.reset = function () {
-  this.locations.clear();
+/**
+ * Clears the store containing the cached resolved locations and promises
+ */
+SourceMapService.prototype.reset = function () {
+  this._isInitialResolve = true;
+  this._locationStore.clear();
+};
+
+SourceMapService.prototype.destroy = function () {
+  this.reset();
+  this._target.off("source-updated", this._onSourceUpdated);
+  this._target.off("navigate", this.reset);
+  this._target.off("will-navigate", this.reset);
+  this._target.off("close", this.destroy);
+  this._isInitialResolve = null;
+  this._target = this._locationStore = null;
 };
 
-SourceLocationController.prototype.destroy = function () {
-  this.locations.clear();
-  this.target.off("source-updated", this._onSourceUpdated);
-  this.target.off("navigate", this.reset);
-  this.target.off("will-navigate", this.reset);
-  this.target.off("close", this.destroy);
-  this.target = this.locations = null;
+/**
+ * Sets up listener for the callback to update the FrameView and tries to resolve location
+ * @param location
+ * @param callback
+ */
+SourceMapService.prototype.subscribe = function (location, callback) {
+  this.on(serialize(location), callback);
+  this._locationStore.set(location);
+  if (this._isInitialResolve) {
+    this._resolveAndUpdate(location);
+    this._isInitialResolve = false;
+  }
+};
+
+/**
+ * Removes the listener for the location and clears cached locations
+ * @param location
+ * @param callback
+ */
+SourceMapService.prototype.unsubscribe = function (location, callback) {
+  this.off(serialize(location), callback);
+  this._locationStore.clearByURL(location.url);
 };
 
 /**
- * Add this `location` to be observed and register a callback
- * whenever the underlying source is updated.
- *
- * @param {Object} location
- *        An object with a {String} url, {Number} line, and optionally
- *        a {Number} column.
- * @param {Function} callback
+ * Tries to resolve the location and if successful,
+ * emits the resolved location and caches it
+ * @param location
+ * @private
  */
-SourceLocationController.prototype.bindLocation = function (location, callback) {
-  assert(location.url, "Location must have a url.");
-  assert(location.line, "Location must have a line.");
-  this.locations.add({ location, callback });
+SourceMapService.prototype._resolveAndUpdate = function (location) {
+  this._resolveLocation(location).then(resolvedLocation => {
+    // We try to source map the first console log to initiate the source-updated event from
+    // target. The isSameLocation check is to make sure we don't update the frame, if the
+    // location is not source-mapped.
+    if (resolvedLocation) {
+      if (this._isInitialResolve) {
+        if (!isSameLocation(location, resolvedLocation)) {
+          this.emit(serialize(location), location, resolvedLocation);
+          return;
+        }
+      }
+      this.emit(serialize(location), location, resolvedLocation);
+    }
+  });
 };
 
 /**
- * Called when a new source occurs (a normal source, source maps) or an updated
- * source (pretty print) occurs.
- *
- * @param {String} eventName
- * @param {Object} sourceEvent
+ * Validates the location model,
+ * checks if there is existing promise to resolve location, if so returns cached promise
+ * if not promised to resolve,
+ * tries to resolve location and returns a promised location
+ * @param location
+ * @return Promise<Object>
+ * @private
  */
-SourceLocationController.prototype._onSourceUpdated = function (_, sourceEvent) {
+SourceMapService.prototype._resolveLocation = Task.async(function* (location) {
+  // Location must have a url and a line
+  if (!location.url || !location.line) {
+    return null;
+  }
+  const cachedLocation = this._locationStore.get(location);
+  if (cachedLocation) {
+    return cachedLocation;
+  } else {
+    const promisedLocation = resolveLocation(this._target, location);
+    if (promisedLocation) {
+      this._locationStore.set(location, promisedLocation);
+      return promisedLocation;
+    }
+  }
+});
+
+/**
+ * Checks if the `source-updated` event is fired from the target.
+ * Checks to see if location store has the source url in its cache,
+ * if so, tries to update each stale location in the store.
+ * @param _
+ * @param sourceEvent
+ * @private
+ */
+SourceMapService.prototype._onSourceUpdated = function (_, sourceEvent) {
   let { type, source } = sourceEvent;
   // If we get a new source, and it's not a source map, abort;
-  // we can ahve no actionable updates as this is just a new normal source.
+  // we can have no actionable updates as this is just a new normal source.
   // Also abort if there's no `url`, which means it's unsourcemappable anyway,
   // like an eval script.
   if (!source.url || type === "newSource" && !source.isSourceMapped) {
     return;
   }
-
-  for (let locationItem of this.locations) {
-    if (isSourceRelated(locationItem.location, source)) {
-      this._updateSource(locationItem);
+  let sourceUrl = null;
+  if (source.generatedUrl && source.isSourceMapped) {
+    sourceUrl = source.generatedUrl;
+  } else if (source.url && source.isPrettyPrinted) {
+    sourceUrl = source.url;
+  }
+  const locationsToResolve = this._locationStore.getByURL(sourceUrl);
+  if (locationsToResolve.length) {
+    this._locationStore.clearByURL(sourceUrl);
+    for (let location of locationsToResolve) {
+      this._resolveAndUpdate(deserialize(location));
     }
   }
 };
 
-SourceLocationController.prototype._updateSource = Task.async(function* (locationItem) {
-  let newLocation = yield resolveLocation(this.target, locationItem.location);
-  if (newLocation) {
-    let previousLocation = Object.assign({}, locationItem.location);
-    Object.assign(locationItem.location, newLocation);
-    locationItem.callback(previousLocation, newLocation);
-  }
-});
+exports.SourceMapService = SourceMapService;
 
 /**
  * Take a TabTarget and a location, containing a `url`, `line`, and `column`, resolve
  * the location to the latest location (so a source mapped location, or if pretty print
  * status has been updated)
  *
  * @param {TabTarget} target
  * @param {Object} location
@@ -100,38 +173,28 @@ SourceLocationController.prototype._upda
  */
 function resolveLocation(target, location) {
   return Task.spawn(function* () {
     let newLocation = yield target.resolveLocation({
       url: location.url,
       line: location.line,
       column: location.column || Infinity
     });
-
     // Source or mapping not found, so don't do anything
     if (newLocation.error) {
       return null;
     }
 
     return newLocation;
   });
 }
 
 /**
- * Takes a serialized SourceActor form and returns a boolean indicating
- * if this source is related to this location, like if a location is a generated source,
- * and the source map is loaded subsequently, the new source mapped SourceActor
- * will be considered related to this location. Same with pretty printing new sources.
- *
- * @param {Object} location
- * @param {Object} source
- * @return {Boolean}
+ * Returns if the original location and resolved location are the same
+ * @param location
+ * @param resolvedLocation
+ * @returns {boolean}
  */
-function isSourceRelated(location, source) {
-         // Mapping location to subsequently loaded source map
-  return source.generatedUrl === location.url ||
-         // Mapping source map loc to source map
-         source.url === location.url;
-}
-
-exports.SourceLocationController = SourceLocationController;
-exports.resolveLocation = resolveLocation;
-exports.isSourceRelated = isSourceRelated;
+function isSameLocation(location, resolvedLocation) {
+  return location.url === resolvedLocation.url &&
+    location.line === resolvedLocation.line &&
+    location.column === resolvedLocation.column;
+};
\ No newline at end of file
--- a/devtools/client/framework/test/browser.ini
+++ b/devtools/client/framework/test/browser.ini
@@ -3,18 +3,22 @@ tags = devtools
 subsuite = devtools
 support-files =
   browser_toolbox_options_disable_js.html
   browser_toolbox_options_disable_js_iframe.html
   browser_toolbox_options_disable_cache.sjs
   browser_toolbox_sidebar_tool.xul
   browser_toolbox_window_title_changes_page.html
   browser_toolbox_window_title_frame_select_page.html
+  code_binary_search.coffee
+  code_binary_search.js
+  code_binary_search.map
   code_math.js
   code_ugly.js
+  doc_empty-tab-01.html
   head.js
   shared-head.js
   shared-redux-head.js
   helper_disable_cache.js
   doc_theme.css
   doc_viewsource.html
   browser_toolbox_options_enable_serviceworkers_testing_frame_script.js
   browser_toolbox_options_enable_serviceworkers_testing.html
@@ -26,18 +30,18 @@ support-files =
 [browser_devtools_api_destroy.js]
 [browser_dynamic_tool_enabling.js]
 [browser_ignore_toolbox_network_requests.js]
 [browser_keybindings_01.js]
 [browser_keybindings_02.js]
 [browser_keybindings_03.js]
 [browser_menu_api.js]
 [browser_new_activation_workflow.js]
-[browser_source-location-01.js]
-[browser_source-location-02.js]
+[browser_source_map-01.js]
+[browser_source_map-02.js]
 [browser_target_from_url.js]
 [browser_target_events.js]
 [browser_target_remote.js]
 [browser_target_support.js]
 [browser_toolbox_custom_host.js]
 [browser_toolbox_dynamic_registration.js]
 [browser_toolbox_getpanelwhenready.js]
 [browser_toolbox_highlight.js]
rename from devtools/client/framework/test/browser_source-location-01.js
rename to devtools/client/framework/test/browser_source_map-01.js
--- a/devtools/client/framework/test/browser_source-location-01.js
+++ b/devtools/client/framework/test/browser_source_map-01.js
@@ -1,66 +1,65 @@
 /* Any copyright is dedicated to the Public Domain.
-   http://creativecommons.org/publicdomain/zero/1.0/ */
+ http://creativecommons.org/publicdomain/zero/1.0/ */
 
 // Whitelisting this test.
 // As part of bug 1077403, the leaking uncaught rejections should be fixed.
 thisTestLeaksUncaughtRejectionsAndShouldBeFixed("[object Object]");
 thisTestLeaksUncaughtRejectionsAndShouldBeFixed(
   "TypeError: this.transport is null");
 
 /**
- * Tests the SourceMapController updates generated sources when source maps
+ * Tests the SourceMapService updates generated sources when source maps
  * are subsequently found. Also checks when no column is provided, and
  * when tagging an already source mapped location initially.
  */
 
 const DEBUGGER_ROOT = "http://example.com/browser/devtools/client/debugger/test/mochitest/";
 // Empty page
 const PAGE_URL = `${DEBUGGER_ROOT}doc_empty-tab-01.html`;
-const JS_URL = `${DEBUGGER_ROOT}code_binary_search.js`;
-const COFFEE_URL = `${DEBUGGER_ROOT}code_binary_search.coffee`;
-const { SourceLocationController } = require("devtools/client/framework/source-location");
+const JS_URL = `${URL_ROOT}code_binary_search.js`;
+const COFFEE_URL = `${URL_ROOT}code_binary_search.coffee`;
+const { SourceMapService } = require("devtools/client/framework/source-map-service");
 
 add_task(function* () {
-  let toolbox = yield openNewTabAndToolbox(PAGE_URL, "jsdebugger");
+  const toolbox = yield openNewTabAndToolbox(PAGE_URL, "jsdebugger");
 
-  let controller = new SourceLocationController(toolbox.target);
+  const service = new SourceMapService(toolbox.target);
 
-  let aggregator = [];
+  const aggregator = [];
 
-  function onUpdate(oldLoc, newLoc) {
+  function onUpdate(e, oldLoc, newLoc) {
     if (oldLoc.line === 6) {
       checkLoc1(oldLoc, newLoc);
     } else if (oldLoc.line === 8) {
       checkLoc2(oldLoc, newLoc);
     } else if (oldLoc.line === 2) {
       checkLoc3(oldLoc, newLoc);
     } else {
       throw new Error(`Unexpected location update: ${JSON.stringify(oldLoc)}`);
     }
     aggregator.push(newLoc);
   }
 
   let loc1 = { url: JS_URL, line: 6 };
   let loc2 = { url: JS_URL, line: 8, column: 3 };
-  let loc3 = { url: COFFEE_URL, line: 2, column: 0 };
 
-  controller.bindLocation(loc1, onUpdate);
-  controller.bindLocation(loc2, onUpdate);
-  controller.bindLocation(loc3, onUpdate);
+  service.subscribe(loc1, onUpdate);
+  service.subscribe(loc2, onUpdate);
 
   // Inject JS script
+  let sourceShown = waitForSourceShown(toolbox.getCurrentPanel(), "code_binary_search");
   yield createScript(JS_URL);
+  yield sourceShown;
 
-  yield waitUntil(() => aggregator.length === 3);
+  yield waitUntil(() => aggregator.length === 2);
 
   ok(aggregator.find(i => i.url === COFFEE_URL && i.line === 4), "found first updated location");
   ok(aggregator.find(i => i.url === COFFEE_URL && i.line === 6), "found second updated location");
-  ok(aggregator.find(i => i.url === COFFEE_URL && i.line === 2), "found third updated location");
 
   yield toolbox.destroy();
   gBrowser.removeCurrentTab();
   finish();
 });
 
 function checkLoc1(oldLoc, newLoc) {
   is(oldLoc.line, 6, "Correct line for JS:6");
@@ -75,28 +74,37 @@ function checkLoc2(oldLoc, newLoc) {
   is(oldLoc.line, 8, "Correct line for JS:8:3");
   is(oldLoc.column, 3, "Correct column for JS:8:3");
   is(oldLoc.url, JS_URL, "Correct url for JS:8:3");
   is(newLoc.line, 6, "Correct line for JS:8:3 -> COFFEE");
   is(newLoc.column, 10, "Correct column for JS:8:3 -> COFFEE");
   is(newLoc.url, COFFEE_URL, "Correct url for JS:8:3 -> COFFEE");
 }
 
-function checkLoc3(oldLoc, newLoc) {
-  is(oldLoc.line, 2, "Correct line for COFFEE:2:0");
-  is(oldLoc.column, 0, "Correct column for COFFEE:2:0");
-  is(oldLoc.url, COFFEE_URL, "Correct url for COFFEE:2:0");
-  is(newLoc.line, 2, "Correct line for COFFEE:2:0 -> COFFEE");
-  is(newLoc.column, 0, "Correct column for COFFEE:2:0 -> COFFEE");
-  is(newLoc.url, COFFEE_URL, "Correct url for COFFEE:2:0 -> COFFEE");
-}
-
 function createScript(url) {
   info(`Creating script: ${url}`);
   let mm = getFrameScript();
   let command = `
     let script = document.createElement("script");
     script.setAttribute("src", "${url}");
     document.body.appendChild(script);
     null;
   `;
   return evalInDebuggee(mm, command);
 }
+
+function waitForSourceShown(debuggerPanel, url) {
+  let { panelWin } = debuggerPanel;
+  let deferred = defer();
+
+  info(`Waiting for source ${url} to be shown in the debugger...`);
+  panelWin.on(panelWin.EVENTS.SOURCE_SHOWN, function onSourceShown(_, source) {
+
+    let sourceUrl = source.url || source.generatedUrl;
+    if (sourceUrl.includes(url)) {
+      panelWin.off(panelWin.EVENTS.SOURCE_SHOWN, onSourceShown);
+      info(`Source shown for ${url}`);
+      deferred.resolve(source);
+    }
+  });
+
+  return deferred.promise;
+}
rename from devtools/client/framework/test/browser_source-location-02.js
rename to devtools/client/framework/test/browser_source_map-02.js
--- a/devtools/client/framework/test/browser_source-location-02.js
+++ b/devtools/client/framework/test/browser_source_map-02.js
@@ -1,63 +1,63 @@
 /* vim: set ts=2 et sw=2 tw=80: */
 /* Any copyright is dedicated to the Public Domain.
-   http://creativecommons.org/publicdomain/zero/1.0/ */
+ http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
- * Tests the SourceLocationController updates generated sources when pretty printing
+ * Tests the SourceMapService updates generated sources when pretty printing
  * and un pretty printing.
  */
 
 const DEBUGGER_ROOT = "http://example.com/browser/devtools/client/debugger/test/mochitest/";
 // Empty page
 const PAGE_URL = `${DEBUGGER_ROOT}doc_empty-tab-01.html`;
 const JS_URL = `${URL_ROOT}code_ugly.js`;
-const { SourceLocationController } = require("devtools/client/framework/source-location");
+const { SourceMapService } = require("devtools/client/framework/source-map-service");
 
 add_task(function* () {
   let toolbox = yield openNewTabAndToolbox(PAGE_URL, "jsdebugger");
 
-  let controller = new SourceLocationController(toolbox.target);
+  let service = new SourceMapService(toolbox.target);
 
   let checkedPretty = false;
   let checkedUnpretty = false;
 
-  function onUpdate(oldLoc, newLoc) {
+  function onUpdate(e, oldLoc, newLoc) {
     if (oldLoc.line === 3) {
       checkPrettified(oldLoc, newLoc);
       checkedPretty = true;
     } else if (oldLoc.line === 9) {
       checkUnprettified(oldLoc, newLoc);
       checkedUnpretty = true;
     } else {
       throw new Error(`Unexpected location update: ${JSON.stringify(oldLoc)}`);
     }
   }
-
-  controller.bindLocation({ url: JS_URL, line: 3 }, onUpdate);
+  const loc1 = { url: JS_URL, line: 3 };
+  service.subscribe(loc1, onUpdate);
 
   // Inject JS script
   let sourceShown = waitForSourceShown(toolbox.getCurrentPanel(), "code_ugly.js");
   yield createScript(JS_URL);
   yield sourceShown;
 
   let ppButton = toolbox.getCurrentPanel().panelWin.document.getElementById("pretty-print");
   sourceShown = waitForSourceShown(toolbox.getCurrentPanel(), "code_ugly.js");
   ppButton.click();
   yield sourceShown;
   yield waitUntil(() => checkedPretty);
 
   // TODO check unprettified change once bug 1177446 fixed
-  /*
-  sourceShown = waitForSourceShown(toolbox.getCurrentPanel(), "code_ugly.js");
-  ppButton.click();
-  yield sourceShown;
-  yield waitUntil(() => checkedUnpretty);
-  */
+  // info("Testing un-pretty printing.");
+  // sourceShown = waitForSourceShown(toolbox.getCurrentPanel(), "code_ugly.js");
+  // ppButton.click();
+  // yield sourceShown;
+  // yield waitUntil(() => checkedUnpretty);
+
 
   yield toolbox.destroy();
   gBrowser.removeCurrentTab();
   finish();
 });
 
 function checkPrettified(oldLoc, newLoc) {
   is(oldLoc.line, 3, "Correct line for JS:3");
copy from devtools/client/debugger/test/mochitest/code_binary_search.coffee
copy to devtools/client/framework/test/code_binary_search.coffee
copy from devtools/client/debugger/test/mochitest/code_binary_search.js
copy to devtools/client/framework/test/code_binary_search.js
copy from devtools/client/debugger/test/mochitest/code_binary_search.map
copy to devtools/client/framework/test/code_binary_search.map
copy from devtools/client/debugger/test/mochitest/doc_empty-tab-01.html
copy to devtools/client/framework/test/doc_empty-tab-01.html
--- a/devtools/client/framework/toolbox.js
+++ b/devtools/client/framework/toolbox.js
@@ -6,16 +6,17 @@
 
 const MAX_ORDINAL = 99;
 const SPLITCONSOLE_ENABLED_PREF = "devtools.toolbox.splitconsoleEnabled";
 const SPLITCONSOLE_HEIGHT_PREF = "devtools.toolbox.splitconsoleHeight";
 const OS_HISTOGRAM = "DEVTOOLS_OS_ENUMERATED_PER_USER";
 const OS_IS_64_BITS = "DEVTOOLS_OS_IS_64_BITS_PER_USER";
 const SCREENSIZE_HISTOGRAM = "DEVTOOLS_SCREEN_RESOLUTION_ENUMERATED_PER_USER";
 const HTML_NS = "http://www.w3.org/1999/xhtml";
+const { SourceMapService } = require("./source-map-service");
 
 var {Cc, Ci, Cu} = require("chrome");
 var promise = require("promise");
 var defer = require("devtools/shared/defer");
 var Services = require("Services");
 var {Task} = require("devtools/shared/task");
 var {gDevTools} = require("devtools/client/framework/devtools");
 var EventEmitter = require("devtools/shared/event-emitter");
@@ -113,16 +114,19 @@ const ToolboxButtons = exports.ToolboxBu
  *        Type of host that will host the toolbox (e.g. sidebar, window)
  * @param {object} hostOptions
  *        Options for host specifically
  */
 function Toolbox(target, selectedTool, hostType, hostOptions) {
   this._target = target;
   this._toolPanels = new Map();
   this._telemetry = new Telemetry();
+  if (Services.prefs.getBoolPref("devtools.sourcemap.locations.enabled")) {
+    this._sourceMapService = new SourceMapService(this._target);
+  }
 
   this._initInspector = null;
   this._inspector = null;
 
   // Map of frames (id => frame-info) and currently selected frame id.
   this.frameMap = new Map();
   this.selectedFrameId = null;
 
@@ -2026,16 +2030,20 @@ Toolbox.prototype = {
     this.off("ready", this._showDevEditionPromo);
 
     gDevTools.off("tool-registered", this._toolRegistered);
     gDevTools.off("tool-unregistered", this._toolUnregistered);
 
     gDevTools.off("pref-changed", this._prefChanged);
 
     this._lastFocusedElement = null;
+    if (this._sourceMapService) {
+      this._sourceMapService.destroy();
+      this._sourceMapService = null;
+    }
 
     if (this.webconsolePanel) {
       this._saveSplitConsoleHeight();
       this.webconsolePanel.removeEventListener("resize",
         this._saveSplitConsoleHeight);
     }
     this.closeButton.removeEventListener("click", this.destroy, true);
     this.textboxContextMenuPopup.removeEventListener("popupshowing",
--- a/devtools/client/inspector/markup/test/browser_markup_dragdrop_autoscroll_01.js
+++ b/devtools/client/inspector/markup/test/browser_markup_dragdrop_autoscroll_01.js
@@ -22,29 +22,29 @@ add_task(function* () {
   markup.isDragging = true;
 
   info("Simulate a mousemove on the view, at the bottom, and expect scrolling");
   let onScrolled = waitForScrollStop(markup.doc);
 
   markup._onMouseMove({
     preventDefault: () => {},
     target: markup.doc.body,
-    pageY: viewHeight
+    pageY: viewHeight + markup.doc.defaultView.scrollY
   });
 
   let bottomScrollPos = yield onScrolled;
   ok(bottomScrollPos > 0, "The view was scrolled down");
 
   info("Simulate a mousemove at the top and expect more scrolling");
   onScrolled = waitForScrollStop(markup.doc);
 
   markup._onMouseMove({
     preventDefault: () => {},
     target: markup.doc.body,
-    pageY: 0
+    pageY: markup.doc.defaultView.scrollY
   });
 
   let topScrollPos = yield onScrolled;
   ok(topScrollPos < bottomScrollPos, "The view was scrolled up");
   is(topScrollPos, 0, "The view was scrolled up to the top");
 
   info("Simulate a mouseup to stop dragging");
   markup._onMouseUp();
--- a/devtools/client/inspector/markup/test/browser_markup_dragdrop_autoscroll_02.js
+++ b/devtools/client/inspector/markup/test/browser_markup_dragdrop_autoscroll_02.js
@@ -21,29 +21,28 @@ add_task(function* () {
   markup.isDragging = true;
 
   info("Simulate a mousemove on the view, at the bottom, and expect scrolling");
   let onScrolled = waitForScrollStop(markup.doc);
 
   markup._onMouseMove({
     preventDefault: () => {},
     target: markup.doc.body,
-    pageY: viewHeight
+    pageY: viewHeight + markup.doc.defaultView.scrollY
   });
 
   let bottomScrollPos = yield onScrolled;
   ok(bottomScrollPos > 0, "The view was scrolled down");
-
   info("Simulate a mousemove at the top and expect more scrolling");
   onScrolled = waitForScrollStop(markup.doc);
 
   markup._onMouseMove({
     preventDefault: () => {},
     target: markup.doc.body,
-    pageY: 0
+    pageY: markup.doc.defaultView.scrollY
   });
 
   let topScrollPos = yield onScrolled;
   ok(topScrollPos < bottomScrollPos, "The view was scrolled up");
   is(topScrollPos, 0, "The view was scrolled up to the top");
 
   info("Simulate a mouseup to stop dragging");
   markup._onMouseUp();
--- a/devtools/client/inspector/markup/test/doc_markup_dragdrop_autoscroll_02.html
+++ b/devtools/client/inspector/markup/test/doc_markup_dragdrop_autoscroll_02.html
@@ -31,33 +31,10 @@ https://bugzilla.mozilla.org/show_bug.cg
   <div></div>
   <div></div>
   <div></div>
   <div></div>
   <div></div>
   <div></div>
   <div></div>
   <div></div>
-  <div></div>
-  <div></div>
-  <div></div>
-  <div></div>
-  <div></div>
-  <div></div>
-  <div></div>
-  <div></div>
-  <div></div>
-  <div></div>
-  <div></div>
-  <div></div>
-  <div></div>
-  <div></div>
-  <div></div>
-  <div></div>
-  <div></div>
-  <div></div>
-  <div></div>
-  <div></div>
-  <div></div>
-  <div></div>
-  <div></div>
 </body>
 </html>
--- a/devtools/client/inspector/rules/test/browser_rules_pseudo-element_01.js
+++ b/devtools/client/inspector/rules/test/browser_rules_pseudo-element_01.js
@@ -5,39 +5,39 @@
 "use strict";
 
 // Test that pseudoelements are displayed correctly in the rule view
 
 const TEST_URI = URL_ROOT + "doc_pseudoelement.html";
 const PSEUDO_PREF = "devtools.inspector.show_pseudo_elements";
 
 add_task(function* () {
-  Services.prefs.setBoolPref(PSEUDO_PREF, true);
+  yield pushPref(PSEUDO_PREF, true);
 
   yield addTab(TEST_URI);
   let {inspector, view} = yield openRuleView();
 
   yield testTopLeft(inspector, view);
   yield testTopRight(inspector, view);
   yield testBottomRight(inspector, view);
   yield testBottomLeft(inspector, view);
   yield testParagraph(inspector, view);
   yield testBody(inspector, view);
-
-  Services.prefs.clearUserPref(PSEUDO_PREF);
 });
 
 function* testTopLeft(inspector, view) {
   let id = "#topleft";
   let rules = yield assertPseudoElementRulesNumbers(id,
     inspector, view, {
       elementRulesNb: 4,
       firstLineRulesNb: 2,
       firstLetterRulesNb: 1,
-      selectionRulesNb: 0
+      selectionRulesNb: 0,
+      afterRulesNb: 1,
+      beforeRulesNb: 2
     }
   );
 
   let gutters = assertGutters(view);
 
   info("Make sure that clicking on the twisty hides pseudo elements");
   let expander = gutters[0].querySelector(".ruleview-expander");
   ok(!view.element.children[1].hidden, "Pseudo Elements are expanded");
@@ -117,17 +117,19 @@ function* testTopLeft(inspector, view) {
      "rgb(0, 255, 0)", "Added prop does not apply to pseudo");
 }
 
 function* testTopRight(inspector, view) {
   yield assertPseudoElementRulesNumbers("#topright", inspector, view, {
     elementRulesNb: 4,
     firstLineRulesNb: 1,
     firstLetterRulesNb: 1,
-    selectionRulesNb: 0
+    selectionRulesNb: 0,
+    beforeRulesNb: 2,
+    afterRulesNb: 1
   });
 
   let gutters = assertGutters(view);
 
   let expander = gutters[0].querySelector(".ruleview-expander");
   ok(!view.element.firstChild.classList.contains("show-expandable-container"),
      "Pseudo Elements remain collapsed after switching element");
 
@@ -137,36 +139,42 @@ function* testTopRight(inspector, view) 
     "Pseudo Elements are shown again after clicking twisty");
 }
 
 function* testBottomRight(inspector, view) {
   yield assertPseudoElementRulesNumbers("#bottomright", inspector, view, {
     elementRulesNb: 4,
     firstLineRulesNb: 1,
     firstLetterRulesNb: 1,
-    selectionRulesNb: 0
+    selectionRulesNb: 0,
+    beforeRulesNb: 3,
+    afterRulesNb: 1
   });
 }
 
 function* testBottomLeft(inspector, view) {
   yield assertPseudoElementRulesNumbers("#bottomleft", inspector, view, {
     elementRulesNb: 4,
     firstLineRulesNb: 1,
     firstLetterRulesNb: 1,
-    selectionRulesNb: 0
+    selectionRulesNb: 0,
+    beforeRulesNb: 2,
+    afterRulesNb: 1
   });
 }
 
 function* testParagraph(inspector, view) {
   let rules =
     yield assertPseudoElementRulesNumbers("#bottomleft p", inspector, view, {
       elementRulesNb: 3,
       firstLineRulesNb: 1,
       firstLetterRulesNb: 1,
-      selectionRulesNb: 1
+      selectionRulesNb: 1,
+      beforeRulesNb: 0,
+      afterRulesNb: 0
     });
 
   assertGutters(view);
 
   let elementFirstLineRule = rules.firstLineRules[0];
   is(convertTextPropsToString(elementFirstLineRule.textProps),
      "background: blue",
      "Paragraph first-line properties are correct");
@@ -204,27 +212,35 @@ function* assertPseudoElementRulesNumber
 
   let rules = {
     elementRules: elementStyle.rules.filter(rule => !rule.pseudoElement),
     firstLineRules: elementStyle.rules.filter(rule =>
       rule.pseudoElement === ":first-line"),
     firstLetterRules: elementStyle.rules.filter(rule =>
       rule.pseudoElement === ":first-letter"),
     selectionRules: elementStyle.rules.filter(rule =>
-      rule.pseudoElement === ":-moz-selection")
+      rule.pseudoElement === ":-moz-selection"),
+    beforeRules: elementStyle.rules.filter(rule =>
+      rule.pseudoElement === ":before"),
+    afterRules: elementStyle.rules.filter(rule =>
+      rule.pseudoElement === ":after"),
   };
 
   is(rules.elementRules.length, ruleNbs.elementRulesNb,
      selector + " has the correct number of non pseudo element rules");
   is(rules.firstLineRules.length, ruleNbs.firstLineRulesNb,
      selector + " has the correct number of :first-line rules");
   is(rules.firstLetterRules.length, ruleNbs.firstLetterRulesNb,
      selector + " has the correct number of :first-letter rules");
   is(rules.selectionRules.length, ruleNbs.selectionRulesNb,
      selector + " has the correct number of :selection rules");
+  is(rules.beforeRules.length, ruleNbs.beforeRulesNb,
+     selector + " has the correct number of :before rules");
+  is(rules.afterRules.length, ruleNbs.afterRulesNb,
+     selector + " has the correct number of :after rules");
 
   return rules;
 }
 
 function getGutters(view) {
   return view.element.querySelectorAll(".theme-gutter");
 }
 
--- a/devtools/client/inspector/rules/test/browser_rules_pseudo-element_02.js
+++ b/devtools/client/inspector/rules/test/browser_rules_pseudo-element_02.js
@@ -1,15 +1,15 @@
 /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
 /* Any copyright is dedicated to the Public Domain.
  http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
-// Test that pseudoelements are displayed correctly in the rule view
+// Test that pseudoelements are displayed correctly in the markup view.
 
 const TEST_URI = URL_ROOT + "doc_pseudoelement.html";
 
 add_task(function* () {
   yield addTab(TEST_URI);
   let {inspector} = yield openRuleView();
 
   let node = yield getNodeFront("#topleft", inspector);
--- a/devtools/client/jsonview/components/text-panel.js
+++ b/devtools/client/jsonview/components/text-panel.js
@@ -66,29 +66,29 @@ define(function (require, exports, modul
     onCopy: function (event) {
       this.props.actions.onCopyJson();
     },
 
     render: function () {
       return (
         Toolbar({},
           ToolbarButton({
-            className: "btn prettyprint",
-            onClick: this.onPrettify},
-            Locale.$STR("jsonViewer.PrettyPrint")
-          ),
-          ToolbarButton({
             className: "btn save",
             onClick: this.onSave},
             Locale.$STR("jsonViewer.Save")
           ),
           ToolbarButton({
             className: "btn copy",
             onClick: this.onCopy},
             Locale.$STR("jsonViewer.Copy")
+          ),
+          ToolbarButton({
+            className: "btn prettyprint",
+            onClick: this.onPrettify},
+            Locale.$STR("jsonViewer.PrettyPrint")
           )
         )
       );
     },
   }));
 
   // Exports from this module
   exports.TextPanel = TextPanel;
--- a/devtools/client/preferences/devtools.js
+++ b/devtools/client/preferences/devtools.js
@@ -291,16 +291,19 @@ pref("devtools.webconsole.timestampMessa
 
 // Web Console automatic multiline mode: |true| if you want incomplete statements
 // to automatically trigger multiline editing (equivalent to shift + enter).
 pref("devtools.webconsole.autoMultiline", true);
 
 // Enable the experimental webconsole frontend (work in progress)
 pref("devtools.webconsole.new-frontend-enabled", false);
 
+// Enable the experimental support for source maps in console (work in progress)
+pref("devtools.sourcemap.locations.enabled", false);
+
 // The number of lines that are displayed in the web console.
 pref("devtools.hud.loglimit", 1000);
 
 // The number of lines that are displayed in the web console for the Net,
 // CSS, JS and Web Developer categories. These defaults should be kept in sync
 // with DEFAULT_LOG_LIMIT in the webconsole frontend.
 pref("devtools.hud.loglimit.network", 1000);
 pref("devtools.hud.loglimit.cssparser", 1000);
--- a/devtools/client/shared/components/frame.js
+++ b/devtools/client/shared/components/frame.js
@@ -29,59 +29,136 @@ module.exports = createClass({
     // Option to display a function name even if it's anonymous.
     showAnonymousFunctionName: PropTypes.bool,
     // Option to display a host name after the source link.
     showHost: PropTypes.bool,
     // Option to display a host name if the filename is empty or just '/'
     showEmptyPathAsHost: PropTypes.bool,
     // Option to display a full source instead of just the filename.
     showFullSourceUrl: PropTypes.bool,
+    // Service to enable the source map feature for console.
+    sourceMapService: PropTypes.object,
   },
 
   getDefaultProps() {
     return {
       showFunctionName: false,
       showAnonymousFunctionName: false,
       showHost: false,
       showEmptyPathAsHost: false,
       showFullSourceUrl: false,
     };
   },
 
+  componentWillMount() {
+    const sourceMapService = this.props.sourceMapService;
+    if (sourceMapService) {
+      const source = this.getSource();
+      sourceMapService.subscribe(source, this.onSourceUpdated);
+    }
+  },
+
+  componentWillUnmount() {
+    const sourceMapService = this.props.sourceMapService;
+    if (sourceMapService) {
+      const source = this.getSource();
+      sourceMapService.unsubscribe(source, this.onSourceUpdated);
+    }
+  },
+
+  /**
+   * Component method to update the FrameView when a resolved location is available
+   * @param event
+   * @param location
+   */
+  onSourceUpdated(event, location, resolvedLocation) {
+    const frame = this.getFrame(resolvedLocation);
+    this.setState({
+      frame,
+      isSourceMapped: true,
+    });
+  },
+
+  /**
+   * Utility method to convert the Frame object to the
+   * Source Object model required by SourceMapService
+   * @param frame
+   * @returns {{url: *, line: *, column: *}}
+   */
+  getSource(frame) {
+    frame = frame || this.props.frame;
+    const { source, line, column } = frame;
+    return {
+      url: source,
+      line,
+      column,
+    };
+  },
+
+  /**
+   * Utility method to convert the Source object model to the
+   * Frame object model required by FrameView class.
+   * @param source
+   * @returns {{source: *, line: *, column: *, functionDisplayName: *}}
+   */
+  getFrame(source) {
+    const { url, line, column } = source;
+    return {
+      source: url,
+      line,
+      column,
+      functionDisplayName: this.props.frame.functionDisplayName,
+    };
+  },
+
   render() {
+    let frame, isSourceMapped;
     let {
       onClick,
-      frame,
       showFunctionName,
       showAnonymousFunctionName,
       showHost,
       showEmptyPathAsHost,
       showFullSourceUrl
     } = this.props;
 
+    if (this.state && this.state.isSourceMapped) {
+      frame = this.state.frame;
+      isSourceMapped = this.state.isSourceMapped;
+    } else {
+      frame = this.props.frame;
+    }
+
     let source = frame.source ? String(frame.source) : "";
     let line = frame.line != void 0 ? Number(frame.line) : null;
     let column = frame.column != void 0 ? Number(frame.column) : null;
 
     const { short, long, host } = getSourceNames(source);
     // Reparse the URL to determine if we should link this; `getSourceNames`
     // has already cached this indirectly. We don't want to attempt to
     // link to "self-hosted" and "(unknown)". However, we do want to link
     // to Scratchpad URIs.
-    const isLinkable = !!(isScratchpadScheme(source) || parseURL(source));
+    // Source mapped sources might not necessary linkable, but they
+    // are still valid in the debugger.
+    const isLinkable = !!(isScratchpadScheme(source) || parseURL(source))
+      || isSourceMapped;
     const elements = [];
     const sourceElements = [];
     let sourceEl;
 
     let tooltip = long;
+
+    // If the source is linkable and line > 0
+    const shouldDisplayLine = isLinkable && line;
+
     // Exclude all falsy values, including `0`, as even
     // a number 0 for line doesn't make sense, and should not be displayed.
     // If source isn't linkable, don't attempt to append line and column
     // info, as this probably doesn't make sense.
-    if (isLinkable && line) {
+    if (shouldDisplayLine) {
       tooltip += `:${line}`;
       // Intentionally exclude 0
       if (column) {
         tooltip += `:${column}`;
       }
     }
 
     let attributes = {
@@ -99,26 +176,35 @@ module.exports = createClass({
         elements.push(
           dom.span({ className: "frame-link-function-display-name" },
             functionDisplayName)
         );
       }
     }
 
     let displaySource = showFullSourceUrl ? long : short;
-    if (showEmptyPathAsHost && (displaySource === "" || displaySource === "/")) {
+    // SourceMapped locations might not be parsed properly by parseURL.
+    // Eg: sourcemapped location could be /folder/file.coffee instead of a url
+    // and so the url parser would not parse non-url locations properly
+    // Check for "/" in displaySource. If "/" is in displaySource,
+    // take everything after last "/".
+    if (isSourceMapped) {
+      displaySource = displaySource.lastIndexOf("/") < 0 ?
+        displaySource :
+        displaySource.slice(displaySource.lastIndexOf("/") + 1);
+    } else if (showEmptyPathAsHost && (displaySource === "" || displaySource === "/")) {
       displaySource = host;
     }
 
     sourceElements.push(dom.span({
       className: "frame-link-filename",
     }, displaySource));
 
     // If source is linkable, and we have a line number > 0
-    if (isLinkable && line) {
+    if (shouldDisplayLine) {
       let lineInfo = `:${line}`;
       // Add `data-line` attribute for testing
       attributes["data-line"] = line;
 
       // Intentionally exclude 0
       if (column) {
         lineInfo += `:${column}`;
         // Add `data-column` attribute for testing
@@ -129,17 +215,17 @@ module.exports = createClass({
     }
 
     // If source is not a URL (self-hosted, eval, etc.), don't make
     // it an anchor link, as we can't link to it.
     if (isLinkable) {
       sourceEl = dom.a({
         onClick: e => {
           e.preventDefault();
-          onClick(frame);
+          onClick(this.getSource(frame));
         },
         href: source,
         className: "frame-link-source",
         draggable: false,
         title: l10n.getFormatStr("frame.viewsourceindebugger", tooltip)
       }, sourceElements);
     } else {
       sourceEl = dom.span({
--- a/devtools/client/shared/components/reps/array.js
+++ b/devtools/client/shared/components/reps/array.js
@@ -122,17 +122,18 @@ define(function (require, exports, modul
     },
 
     render: function () {
       let mode = this.props.mode || "short";
       let object = this.props.object;
       let items;
 
       if (mode == "tiny") {
-        items = DOM.span({className: "length"}, object.length);
+        let isEmpty = object.length === 0;
+        items = DOM.span({className: "length"}, isEmpty ? "" : object.length);
       } else {
         let max = (mode == "short") ? 3 : 300;
         items = this.arrayIterator(object, max);
       }
 
       let objectLink = this.props.objectLink || DOM.span;
 
       return (
--- a/devtools/client/shared/components/reps/grip-array.js
+++ b/devtools/client/shared/components/reps/grip-array.js
@@ -103,17 +103,19 @@ define(function (require, exports, modul
 
     render: function () {
       let mode = this.props.mode || "short";
       let object = this.props.object;
 
       let items;
 
       if (mode == "tiny") {
-        items = span({className: "length"}, this.getLength(object));
+        let objectLength = this.getLength(object);
+        let isEmpty = objectLength === 0;
+        items = span({className: "length"}, isEmpty ? "" : objectLength);
       } else {
         let max = (mode == "short") ? 3 : 300;
         items = this.arrayIterator(object, max);
       }
 
       let objectLink = this.props.objectLink || span;
 
       return (
--- a/devtools/client/shared/components/tabs/tabbar.css
+++ b/devtools/client/shared/components/tabs/tabbar.css
@@ -43,12 +43,12 @@
   flex-grow: 1;
 }
 
 .tabs .tabs-navigation .tabs-menu-item a {
   text-align: center;
 }
 
 /* Firebug theme doesn't stretch the tabs. */
-.theme-firebug .tabs .tabs-navigation .tabs-menu {
-  display: block;
+.theme-firebug .tabs .tabs-navigation .tabs-menu-item {
+  flex-grow: 0;
 }
 
--- a/devtools/client/shared/components/tabs/tabs.css
+++ b/devtools/client/shared/components/tabs/tabs.css
@@ -12,17 +12,17 @@
 .tabs .tabs-menu {
   display: table;
   list-style: none;
   padding: 0;
   margin: 0;
 }
 
 .tabs .tabs-menu-item {
-  float: left;
+  float: inline-start;
 }
 
 .tabs .tabs-menu-item a {
   display: block;
   color: #A9A9A9;
   padding: 4px 8px;
   border: 1px solid transparent;
   text-decoration: none;
--- a/devtools/client/shared/components/test/mochitest/test_reps_array.html
+++ b/devtools/client/shared/components/test/mochitest/test_reps_array.html
@@ -52,17 +52,17 @@ window.onload = Task.async(function* () 
 
     const modeTests = [
       {
         mode: undefined,
         expectedOutput: defaultOutput,
       },
       {
         mode: "tiny",
-        expectedOutput: `[0]`,
+        expectedOutput: `[]`,
       },
       {
         mode: "short",
         expectedOutput: defaultOutput,
       },
       {
         mode: "long",
         expectedOutput: defaultOutput,
--- a/devtools/client/shared/components/test/mochitest/test_reps_grip-array.html
+++ b/devtools/client/shared/components/test/mochitest/test_reps_grip-array.html
@@ -52,17 +52,17 @@ window.onload = Task.async(function* () 
 
     const modeTests = [
       {
         mode: undefined,
         expectedOutput: defaultOutput,
       },
       {
         mode: "tiny",
-        expectedOutput: `[0]`,
+        expectedOutput: `[]`,
       },
       {
         mode: "short",
         expectedOutput: defaultOutput,
       },
       {
         mode: "long",
         expectedOutput: defaultOutput,
--- a/devtools/client/shared/components/tree/tree-view.css
+++ b/devtools/client/shared/components/tree/tree-view.css
@@ -18,16 +18,21 @@
 /* TreeView Table*/
 
 .treeTable .treeLabelCell {
   padding: 2px 0 2px 0px;
   vertical-align: top;
   white-space: nowrap;
 }
 
+.treeTable .treeLabelCell::after {
+  content: ":";
+  color: var(--object-color);
+}
+
 .treeTable .treeValueCell {
   padding: 2px 0 2px 5px;
   overflow: hidden;
 }
 
 .treeTable .treeLabel {
   cursor: default;
   overflow: hidden;
--- a/devtools/client/shared/test/unit/test_parseDeclarations.js
+++ b/devtools/client/shared/test/unit/test_parseDeclarations.js
@@ -341,17 +341,23 @@ const TEST_DATA = [
   },
 
   // Parsing our special comments skips the name-check heuristic.
   {
     parseComments: true,
     input: "/*! walrus: zebra; */",
     expected: [{name: "walrus", value: "zebra", priority: "",
                 offsets: [4, 18], commentOffsets: [0, 21]}]
-  }
+  },
+
+  // Regression test for bug 1287620.
+  {
+    input: "color: blue \\9 no\\_need",
+    expected: [{name: "color", value: "blue \\9 no_need", priority: "", offsets: [0, 23]}]
+  },
 ];
 
 function run_test() {
   run_basic_tests();
   run_comment_tests();
 }
 
 // Test parseDeclarations.
--- a/devtools/client/shared/widgets/TableWidget.js
+++ b/devtools/client/shared/widgets/TableWidget.js
@@ -47,35 +47,38 @@ const MAX_VISIBLE_STRING_SIZE = 100;
  *
  * @param {nsIDOMNode} node
  *        The container element for the table widget.
  * @param {object} options
  *        - initialColumns: map of key vs display name for initial columns of
  *                          the table. See @setupColumns for more info.
  *        - uniqueId: the column which will be the unique identifier of each
  *                    entry in the table. Default: name.
+ *        - wrapTextInElements: Don't ever use 'value' attribute on labels.
+ *                              Default: false.
  *        - emptyText: text to display when no entries in the table to display.
  *        - highlightUpdated: true to highlight the changed/added row.
  *        - removableColumns: Whether columns are removeable. If set to false,
  *                            the context menu in the headers will not appear.
  *        - firstColumn: key of the first column that should appear.
  *        - cellContextMenuId: ID of a <menupopup> element to be set as a
  *                             context menu of every cell.
  */
 function TableWidget(node, options = {}) {
   EventEmitter.decorate(this);
 
   this.document = node.ownerDocument;
   this.window = this.document.defaultView;
   this._parent = node;
 
   let {initialColumns, emptyText, uniqueId, highlightUpdated, removableColumns,
-       firstColumn, cellContextMenuId} = options;
+       firstColumn, wrapTextInElements, cellContextMenuId} = options;
   this.emptyText = emptyText || "";
   this.uniqueId = uniqueId || "name";
+  this.wrapTextInElements = wrapTextInElements || false;
   this.firstColumn = firstColumn || "";
   this.highlightUpdated = highlightUpdated || false;
   this.removableColumns = removableColumns !== false;
   this.cellContextMenuId = cellContextMenuId;
 
   this.tbody = this.document.createElementNS(XUL_NS, "hbox");
   this.tbody.className = "table-widget-body theme-body";
   this.tbody.setAttribute("flex", "1");
@@ -959,16 +962,17 @@ module.exports.TableWidget = TableWidget
  *        The displayed string on the column's header.
  */
 function Column(table, id, header) {
   this.tbody = table.tbody;
   this.document = table.document;
   this.window = table.window;
   this.id = id;
   this.uniqueId = table.uniqueId;
+  this.wrapTextInElements = table.wrapTextInElements;
   this.table = table;
   this.cells = [];
   this.items = {};
 
   this.highlightUpdated = table.highlightUpdated;
 
   // This wrapping element is required solely so that position:sticky works on
   // the headers of the columns.
@@ -1441,16 +1445,17 @@ Column.prototype = {
  *        can be a DOMNode that is appended or a string value.
  * @param {Cell} nextCell
  *        The cell object which is next to this cell. null if this cell is last
  *        cell of the column
  */
 function Cell(column, item, nextCell) {
   let document = column.document;
 
+  this.wrapTextInElements = column.wrapTextInElements;
   this.label = document.createElementNS(XUL_NS, "label");
   this.label.setAttribute("crop", "end");
   this.label.className = "plain table-widget-cell";
 
   if (nextCell) {
     column.column.insertBefore(this.label, nextCell.label);
   } else {
     column.column.appendChild(this.label);
@@ -1494,16 +1499,22 @@ Cell.prototype = {
 
   set value(value) {
     this._value = value;
     if (value == null) {
       this.label.setAttribute("value", "");
       return;
     }
 
+    if (this.wrapTextInElements && !(value instanceof Ci.nsIDOMNode)) {
+      let span = this.label.ownerDocument.createElementNS(HTML_NS, "span");
+      span.textContent = value;
+      value = span;
+    }
+
     if (!(value instanceof Ci.nsIDOMNode) &&
         value.length > MAX_VISIBLE_STRING_SIZE) {
       value = value .substr(0, MAX_VISIBLE_STRING_SIZE) + "\u2026";
     }
 
     if (value instanceof Ci.nsIDOMNode) {
       this.label.removeAttribute("value");
 
--- a/devtools/client/themes/webconsole.css
+++ b/devtools/client/themes/webconsole.css
@@ -493,16 +493,26 @@ a {
   border: 1px solid var(--theme-splitter-color);
   border-radius: 3px;
 }
 
 .consoletable {
   margin: 5px 0 0 0;
 }
 
+/* Force cells to only show one row of contents.  Getting normal ellipses
+   behavior has proven impossible so far, so this is better than letting
+   rows get out of vertical alignment when one cell has a lot of content. */
+.consoletable .table-widget-cell > span {
+  overflow: hidden;
+  display: flex;
+  height: 1.25em;
+  line-height: 1.25em;
+}
+
 .theme-light .message[severity=error] .stacktrace {
   background-color: rgba(255, 255, 255, 0.5);
 }
 
 .theme-dark .message[severity=error] .stacktrace {
   background-color: rgba(0, 0, 0, 0.5);
 }
 
--- a/devtools/client/webconsole/console-output.js
+++ b/devtools/client/webconsole/console-output.js
@@ -3615,16 +3615,17 @@ Widgets.Table.prototype = extend(Widgets
     if (this.element) {
       return this;
     }
 
     let result = this.element = this.document.createElementNS(XHTML_NS, "div");
     result.className = "consoletable devtools-monospace";
 
     this.table = new TableWidget(result, {
+      wrapTextInElements: true,
       initialColumns: this.columns,
       uniqueId: "_index",
       firstColumn: "_index"
     });
 
     for (let row of this.data) {
       this.table.push(row);
     }
--- a/devtools/client/webconsole/webconsole.js
+++ b/devtools/client/webconsole/webconsole.js
@@ -2526,55 +2526,62 @@ WebConsoleFrame.prototype = {
     locationNode.className = "message-location devtools-monospace";
 
     if (!url) {
       url = "";
     }
 
     let fullURL = url.split(" -> ").pop();
     // Make the location clickable.
-    let onClick = () => {
+    let onClick = ({ url, line }) => {
       let category = locationNode.closest(".message").category;
       let target = null;
 
       if (/^Scratchpad\/\d+$/.test(url)) {
         target = "scratchpad";
       } else if (category === CATEGORY_CSS) {
         target = "styleeditor";
       } else if (category === CATEGORY_JS || category === CATEGORY_WEBDEV) {
         target = "jsdebugger";
-      } else if (/\.js$/.test(fullURL)) {
+      } else if (/\.js$/.test(url)) {
         // If it ends in .js, let's attempt to open in debugger
         // anyway, as this falls back to normal view-source.
         target = "jsdebugger";
+      } else {
+        // Point everything else to debugger, if source not available,
+        // it will fall back to view-source.
+        target = "jsdebugger";
       }
 
       switch (target) {
         case "scratchpad":
           this.owner.viewSourceInScratchpad(url, line);
           return;
         case "jsdebugger":
-          this.owner.viewSourceInDebugger(fullURL, line);
+          this.owner.viewSourceInDebugger(url, line);
           return;
         case "styleeditor":
-          this.owner.viewSourceInStyleEditor(fullURL, line);
+          this.owner.viewSourceInStyleEditor(url, line);
           return;
       }
       // No matching tool found; use old school view-source
-      this.owner.viewSource(fullURL, line);
+      this.owner.viewSource(url, line);
     };
 
+    const toolbox = gDevTools.getToolbox(this.owner.target);
+
     this.ReactDOM.render(this.FrameView({
       frame: {
         source: fullURL,
         line,
         column
       },
       showEmptyPathAsHost: true,
       onClick,
+      sourceMapService: toolbox ? toolbox._sourceMapService : null,
     }), locationNode);
 
     return locationNode;
   },
 
   /**
    * Adjusts the category and severity of the given message.
    *
--- a/devtools/server/actors/eventlooplag.js
+++ b/devtools/server/actors/eventlooplag.js
@@ -8,74 +8,53 @@
  * The eventLoopLag actor emits "event-loop-lag" events when the event
  * loop gets unresponsive. The event comes with a "time" property (the
  * duration of the lag in milliseconds).
  */
 
 const {Ci} = require("chrome");
 const Services = require("Services");
 const {XPCOMUtils} = require("resource://gre/modules/XPCOMUtils.jsm");
-const protocol = require("devtools/shared/protocol");
-const {method, Arg, RetVal} = protocol;
+const {Actor, ActorClassWithSpec} = require("devtools/shared/protocol");
 const events = require("sdk/event/core");
-
-var EventLoopLagActor = exports.EventLoopLagActor = protocol.ActorClass({
-
-  typeName: "eventLoopLag",
+const {eventLoopLagSpec} = require("devtools/shared/specs/eventlooplag");
 
+var EventLoopLagActor = exports.EventLoopLagActor = ActorClassWithSpec(eventLoopLagSpec, {
   _observerAdded: false,
 
-  events: {
-    "event-loop-lag" : {
-      type: "event-loop-lag",
-      time: Arg(0, "number") // duration of the lag in milliseconds.
-    }
-  },
-
   /**
    * Start tracking the event loop lags.
    */
-  start: method(function () {
+  start: function () {
     if (!this._observerAdded) {
       Services.obs.addObserver(this, "event-loop-lag", false);
       this._observerAdded = true;
     }
     return Services.appShell.startEventLoopLagTracking();
-  }, {
-    request: {},
-    response: {success: RetVal("number")}
-  }),
+  },
 
   /**
    * Stop tracking the event loop lags.
    */
-  stop: method(function () {
+  stop: function () {
     if (this._observerAdded) {
       Services.obs.removeObserver(this, "event-loop-lag");
       this._observerAdded = false;
     }
     Services.appShell.stopEventLoopLagTracking();
-  }, {request: {}, response: {}}),
+  },
 
   destroy: function () {
     this.stop();
-    protocol.Actor.prototype.destroy.call(this);
+    Actor.prototype.destroy.call(this);
   },
 
   // nsIObserver
 
   observe: function (subject, topic, data) {
     if (topic == "event-loop-lag") {
       // Forward event loop lag event
       events.emit(this, "event-loop-lag", data);
     }
   },
 
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver]),
 });
-
-exports.EventLoopLagFront = protocol.FrontClass(EventLoopLagActor, {
-  initialize: function (client, form) {
-    protocol.Front.prototype.initialize.call(this, client);
-    this.actorID = form.eventLoopLagActor;
-    this.manage(this);
-  },
-});
--- a/devtools/server/actors/script.js
+++ b/devtools/server/actors/script.js
@@ -15,17 +15,16 @@ const { FrameActor } = require("devtools
 const { ObjectActor, createValueGrip, longStringGrip } = require("devtools/server/actors/object");
 const { SourceActor, getSourceURL } = require("devtools/server/actors/source");
 const { DebuggerServer } = require("devtools/server/main");
 const { ActorClassWithSpec } = require("devtools/shared/protocol");
 const DevToolsUtils = require("devtools/shared/DevToolsUtils");
 const { assert, dumpn, update, fetch } = DevToolsUtils;
 const promise = require("promise");
 const xpcInspector = require("xpcInspector");
-const ScriptStore = require("./utils/ScriptStore");
 const { DevToolsWorker } = require("devtools/shared/worker/worker");
 const object = require("sdk/util/object");
 const { threadSpec } = require("devtools/shared/specs/script");
 
 const { defer, resolve, reject, all } = promise;
 
 loader.lazyGetter(this, "Debugger", () => {
   let Debugger = require("Debugger");
@@ -488,24 +487,16 @@ const ThreadActor = ActorClassWithSpec(t
     if (!this._threadLifetimePool) {
       this._threadLifetimePool = new ActorPool(this.conn);
       this.conn.addActorPool(this._threadLifetimePool);
       this._threadLifetimePool.objectActors = new WeakMap();
     }
     return this._threadLifetimePool;
   },
 
-  get scripts() {
-    if (!this._scripts) {
-      this._scripts = new ScriptStore();
-      this._scripts.addScripts(this.dbg.findScripts());
-    }
-    return this._scripts;
-  },
-
   get sources() {
     return this._parent.sources;
   },
 
   get youngestFrame() {
     if (this.state != "paused") {
       return null;
     }
@@ -639,18 +630,16 @@ const ThreadActor = ActorClassWithSpec(t
     try {
       // Put ourselves in the paused state.
       let packet = this._paused();
       if (!packet) {
         return { error: "notAttached" };
       }
       packet.why = { type: "attached" };
 
-      this._restoreBreakpoints();
-
       // Send the response to the attach request now (rather than
       // returning it), because we're going to start a nested event loop
       // here.
       this.conn.send(packet);
 
       // Start a nested event loop.
       this._pushThreadPause();
 
@@ -1178,17 +1167,18 @@ const ThreadActor = ActorClassWithSpec(t
   _breakOnEnter: function (script) {
     let offsets = script.getAllOffsets();
     for (let line = 0, n = offsets.length; line < n; line++) {
       if (offsets[line]) {
         // N.B. Hidden breakpoints do not have an original location, and are not
         // stored in the breakpoint actor map.
         let actor = new BreakpointActor(this);
         this.threadLifetimePool.addActor(actor);
-        let scripts = this.scripts.getScriptsBySourceAndLine(script.source, line);
+
+        let scripts = this.dbg.findScripts({ source: script.source, line: line });
         let entryPoints = findEntryPointsForLine(scripts, line);
         setBreakpointAtEntryPoints(actor, entryPoints);
         this._hiddenBreakpoints.set(actor.actorID, actor);
         break;
       }
     }
   },
 
@@ -1312,17 +1302,18 @@ const ThreadActor = ActorClassWithSpec(t
   },
 
   /**
    * Get the script and source lists from the debugger.
    */
   _discoverSources: function () {
     // Only get one script per Debugger.Source.
     const sourcesToScripts = new Map();
-    const scripts = this.scripts.getAllScripts();
+    const scripts = this.dbg.findScripts();
+
     for (let i = 0, len = scripts.length; i < len; i++) {
       let s = scripts[i];
       if (s.source) {
         sourcesToScripts.set(s.source, s);
       }
     }
 
     return all([...sourcesToScripts.values()].map(script => {
@@ -1918,46 +1909,27 @@ const ThreadActor = ActorClassWithSpec(t
         from: this.actorID,
         type: name,
         source: source.form()
       });
     }
   },
 
   /**
-   * Restore any pre-existing breakpoints to the sources that we have access to.
-   */
-  _restoreBreakpoints: function () {
-    if (this.breakpointActorMap.size === 0) {
-      return;
-    }
-
-    for (let s of this.scripts.getSources()) {
-      this._addSource(s);
-    }
-  },
-
-  /**
    * Add the provided source to the server cache.
    *
    * @param aSource Debugger.Source
    *        The source that will be stored.
    * @returns true, if the source was added; false otherwise.
    */
   _addSource: function (aSource) {
     if (!this.sources.allowSource(aSource) || this._debuggerSourcesSeen.has(aSource)) {
       return false;
     }
 
-    // The scripts must be added to the ScriptStore before restoring
-    // breakpoints. If we try to add them to the ScriptStore any later, we can
-    // accidentally set a breakpoint in a top level script as a "closest match"
-    // because we wouldn't have added the child scripts to the ScriptStore yet.
-    this.scripts.addScripts(this.dbg.findScripts({ source: aSource }));
-
     let sourceActor = this.sources.createNonSourceMappedActor(aSource);
     let bpActors = [...this.breakpointActorMap.findActors()];
 
     if (this._options.useSourceMaps) {
       let promises = [];
 
       // Go ahead and establish the source actors for this script, which
       // fetches sourcemaps if available and sends onNewSource
--- a/devtools/server/actors/source.js
+++ b/devtools/server/actors/source.js
@@ -180,17 +180,16 @@ let SourceActor = ActorClassWithSpec(sou
 
   get isInlineSource() {
     return this._isInlineSource;
   },
 
   get threadActor() { return this._threadActor; },
   get sources() { return this._threadActor.sources; },
   get dbg() { return this.threadActor.dbg; },
-  get scripts() { return this.threadActor.scripts; },
   get source() { return this._source; },
   get generatedSource() { return this._generatedSource; },
   get breakpointActorMap() { return this.threadActor.breakpointActorMap; },
   get url() {
     if (this.source) {
       return getSourceURL(this.source, this.threadActor._parent.window);
     }
     return this._originalUrl;
@@ -439,17 +438,17 @@ let SourceActor = ActorClassWithSpec(sou
   /**
    * Extract all executable offsets from the given script
    * @param String url - extract offsets of the script with this url
    * @param Boolean onlyLine - will return only the line number
    * @return Set - Executable offsets/lines of the script
    **/
   getExecutableOffsets: function (source, onlyLine) {
     let offsets = new Set();
-    for (let s of this.threadActor.scripts.getScriptsBySource(source)) {
+    for (let s of this.dbg.findScripts({ source })) {
       for (let offset of s.getAllColumnOffsets()) {
         offsets.add(onlyLine ? offset.lineNumber : offset);
       }
     }
 
     return offsets;
   },
 
@@ -713,20 +712,27 @@ let SourceActor = ActorClassWithSpec(sou
     const { originalLocation } = actor;
     const { originalLine, originalSourceActor } = originalLocation;
 
     if (!this.isSourceMapped) {
       if (!this._setBreakpointAtGeneratedLocation(
         actor,
         GeneratedLocation.fromOriginalLocation(originalLocation)
       )) {
-        const scripts = this.scripts.getScriptsBySourceActorAndLine(
-          this,
-          originalLine
-        );
+        const query = { line: originalLine };
+        // For most cases, we have a real source to query for. The
+        // only time we don't is for HTML pages. In that case we want
+        // to query for scripts in an HTML page based on its URL, as
+        // there could be several sources within an HTML page.
+        if (this.source) {
+          query.source = this.source;
+        } else {
+          query.url = this.url;
+        }
+        const scripts = this.dbg.findScripts(query);
 
         // Never do breakpoint sliding for column breakpoints.
         // Additionally, never do breakpoint sliding if no scripts
         // exist on this line.
         //
         // Sliding can go horribly wrong if we always try to find the
         // next line with valid entry points in the entire file.
         // Scripts may be completely GCed and we never knew they
@@ -831,21 +837,25 @@ let SourceActor = ActorClassWithSpec(sou
   _setBreakpointAtGeneratedLocation: function (actor, generatedLocation) {
     let {
       generatedSourceActor,
       generatedLine,
       generatedColumn,
       generatedLastColumn
     } = generatedLocation;
 
-    // Find all scripts that match the given source actor and line number.
-    let scripts = this.scripts.getScriptsBySourceActorAndLine(
-      generatedSourceActor,
-      generatedLine
-    );
+    // Find all scripts that match the given source actor and line
+    // number.
+    const query = { line: generatedLine };
+    if (generatedSourceActor.source) {
+      query.source = generatedSourceActor.source;
+    } else {
+      query.url = generatedSourceActor.url;
+    }
+    let scripts = this.dbg.findScripts(query);
 
     scripts = scripts.filter((script) => !actor.hasScript(script));
 
     // Find all entry points that correspond to the given location.
     let entryPoints = [];
     if (generatedColumn === undefined) {
       // This is a line breakpoint, so we are interested in all offsets
       // that correspond to the given line number.
--- a/devtools/server/actors/styles.js
+++ b/devtools/server/actors/styles.js
@@ -18,22 +18,18 @@ const events = require("sdk/event/core")
 const {UPDATE_PRESERVING_RULES, UPDATE_GENERAL} = require("devtools/server/actors/stylesheets");
 const {pageStyleSpec, styleRuleSpec, ELEMENT_STYLE} = require("devtools/shared/specs/styles");
 
 loader.lazyRequireGetter(this, "CSS", "CSS");
 loader.lazyGetter(this, "CssLogic", () => require("devtools/server/css-logic").CssLogic);
 loader.lazyGetter(this, "SharedCssLogic", () => require("devtools/shared/inspector/css-logic"));
 loader.lazyGetter(this, "DOMUtils", () => Cc["@mozilla.org/inspector/dom-utils;1"].getService(Ci.inIDOMUtils));
 
-// When gathering rules to read for pseudo elements, we will skip
-// :before and :after, which are handled as a special case.
-loader.lazyGetter(this, "PSEUDO_ELEMENTS_TO_READ", () => {
-  return DOMUtils.getCSSPseudoElementNames().filter(pseudo => {
-    return pseudo !== ":before" && pseudo !== ":after";
-  });
+loader.lazyGetter(this, "PSEUDO_ELEMENTS", () => {
+  return DOMUtils.getCSSPseudoElementNames();
 });
 
 const XHTML_NS = "http://www.w3.org/1999/xhtml";
 const FONT_PREVIEW_TEXT = "Abc";
 const FONT_PREVIEW_FONT_SIZE = 40;
 const FONT_PREVIEW_FILLSTYLE = "black";
 const NORMAL_FONT_WEIGHT = 400;
 const BOLD_FONT_WEIGHT = 700;
@@ -536,20 +532,19 @@ var PageStyleActor = protocol.ActorClass
           // The only case when there would be a pseudo here is
           // ::before/::after, and in this case we want to tell the
           // view that it belongs to the element (which is a
           // _moz_generated_content native anonymous element).
           oneRule.pseudoElement = null;
           rules.push(oneRule);
         });
 
-    // Now any pseudos (except for ::before / ::after, which was handled as
-    // a 'normal rule' above.
+    // Now any pseudos.
     if (showElementStyles) {
-      for (let readPseudo of PSEUDO_ELEMENTS_TO_READ) {
+      for (let readPseudo of PSEUDO_ELEMENTS) {
         this._getElementRules(bindingElement, readPseudo, inherited, options)
             .forEach(oneRule => {
               rules.push(oneRule);
             });
       }
     }
 
     return rules;
--- a/devtools/server/actors/stylesheets.js
+++ b/devtools/server/actors/stylesheets.js
@@ -455,17 +455,20 @@ var StyleSheetActor = protocol.ActorClas
       charset: this._getCSSCharset()
     };
 
     // Bug 1282660 - We use the system principal to load the default internal
     // stylesheets instead of the content principal since such stylesheets
     // require system principal to load. At meanwhile, we strip the loadGroup
     // for preventing the assertion of the userContextId mismatching.
     // The default internal stylesheets load from the 'resource:' URL.
-    if (!/^resource:\/\//.test(this.href)) {
+    // Bug 1287607 - The 'chrome:' URL will be also loaded from here, so we do
+    // the same thing for such URLs as well.
+    if (!/^resource:\/\//.test(this.href) &&
+        !/^chrome:\/\//.test(this.href)) {
       options.window = this.window;
       options.principal = this.document.nodePrincipal;
     }
 
     return fetch(this.href, options).then(({ content }) => {
       this.text = content;
       return content;
     });
deleted file mode 100644
--- a/devtools/server/actors/utils/ScriptStore.js
+++ /dev/null
@@ -1,219 +0,0 @@
-/* -*- indent-tabs-mode: nil; js-indent-level: 2; js-indent-level: 2 -*- */
-/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
-/* 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";
-
-const { noop } = require("devtools/shared/DevToolsUtils");
-
-/**
- * A `ScriptStore` is a cache of `Debugger.Script` instances. It holds strong
- * references to the cached scripts to alleviate the GC-sensitivity issues that
- * plague `Debugger.prototype.findScripts`, but this means that its lifetime
- * must be managed carefully. It is the `ScriptStore` user's responsibility to
- * ensure that the `ScriptStore` stays up to date.
- *
- * Implementation Notes:
- *
- * The ScriptStore's prototype methods are very hot, in general. To help the
- * JIT, they avoid ES6-isms and higher-order iteration functions, for the most
- * part. You might be wondering why we don't maintain indices on, say,
- * Debugger.Source for faster querying, if these methods are so hot. First, the
- * hottest method is actually just getting all scripts; second, populating the
- * store becomes prohibitively expensive. So we fall back to linear queries
- * (which isn't so bad, because Debugger.prototype.findScripts is also linear).
- */
-function ScriptStore() {
-  // Set of every Debugger.Script in the cache.
-  this._scripts = new NoDeleteSet;
-}
-
-module.exports = ScriptStore;
-
-ScriptStore.prototype = {
-  // Populating a ScriptStore.
-
-  /**
-   * Add one script to the cache.
-   *
-   * @param Debugger.Script script
-   */
-  addScript(script) {
-    this._scripts.add(script);
-  },
-
-  /**
-   * Add many scripts to the cache at once.
-   *
-   * @param Array scripts
-   *        The set of Debugger.Scripts to add to the cache.
-   */
-  addScripts(scripts) {
-    for (var i = 0, len = scripts.length; i < len; i++) {
-      this.addScript(scripts[i]);
-    }
-  },
-
-  // Querying a ScriptStore.
-
-  /**
-   * Get all the sources for which we have scripts cached.
-   *
-   * @returns Array of Debugger.Source
-   */
-  getSources() {
-    return [...new Set(this._scripts.items.map(s => s.source))];
-  },
-
-  /**
-   * Get all the scripts in the cache.
-   *
-   * @returns read-only Array of Debugger.Script.
-   *
-   * NB: The ScriptStore retains ownership of the returned array, and the
-   * ScriptStore's consumers MUST NOT MODIFY its contents!
-   */
-  getAllScripts() {
-    return this._scripts.items;
-  },
-
-  getScriptsBySourceActor(sourceActor) {
-    return sourceActor.source ?
-           this.getScriptsBySource(sourceActor.source) :
-           this.getScriptsByURL(sourceActor._originalUrl);
-  },
-
-  getScriptsBySourceActorAndLine(sourceActor, line) {
-    return sourceActor.source ?
-           this.getScriptsBySourceAndLine(sourceActor.source, line) :
-           this.getScriptsByURLAndLine(sourceActor._originalUrl, line);
-  },
-
-  /**
-   * Get all scripts produced from the given source.
-   *
-   * @oaram Debugger.Source source
-   * @returns Array of Debugger.Script
-   */
-  getScriptsBySource(source) {
-    var results = [];
-    var scripts = this._scripts.items;
-    var length = scripts.length;
-    for (var i = 0; i < length; i++) {
-      if (scripts[i].source === source) {
-        results.push(scripts[i]);
-      }
-    }
-    return results;
-  },
-
-  /**
-   * Get all scripts produced from the given source whose source code definition
-   * spans the given line.
-   *
-   * @oaram Debugger.Source source
-   * @param Number line
-   * @returns Array of Debugger.Script
-   */
-  getScriptsBySourceAndLine(source, line) {
-    var results = [];
-    var scripts = this._scripts.items;
-    var length = scripts.length;
-    for (var i = 0; i < length; i++) {
-      var script = scripts[i];
-      if (script.source === source &&
-          script.startLine <= line &&
-          (script.startLine + script.lineCount) > line) {
-        results.push(script);
-      }
-    }
-    return results;
-  },
-
-  /**
-   * Get all scripts defined by a source at the given URL.
-   *
-   * @param String url
-   * @returns Array of Debugger.Script
-   */
-  getScriptsByURL(url) {
-    var results = [];
-    var scripts = this._scripts.items;
-    var length = scripts.length;
-    for (var i = 0; i < length; i++) {
-      if (scripts[i].url === url) {
-        results.push(scripts[i]);
-      }
-    }
-    return results;
-  },
-
-  /**
-   * Get all scripts defined by a source a the given URL and whose source code
-   * definition spans the given line.
-   *
-   * @param String url
-   * @param Number line
-   * @returns Array of Debugger.Script
-   */
-  getScriptsByURLAndLine(url, line) {
-    var results = [];
-    var scripts = this._scripts.items;
-    var length = scripts.length;
-    for (var i = 0; i < length; i++) {
-      var script = scripts[i];
-      if (script.url === url &&
-          script.startLine <= line &&
-          (script.startLine + script.lineCount) > line) {
-        results.push(script);
-      }
-    }
-    return results;
-  },
-};
-
-
-/**
- * A set which can only grow, and does not support the delete operation.
- * Provides faster iteration than the native Set by maintaining an array of all
- * items, in addition to the internal set of all items, which allows direct
- * iteration (without the iteration protocol and calling into C++, which are
- * both expensive).
- */
-function NoDeleteSet() {
-  this._set = new Set();
-  this.items = [];
-}
-
-NoDeleteSet.prototype = {
-  /**
-   * An array containing every item in the set for convenience and faster
-   * iteration. This is public for reading only, and consumers MUST NOT modify
-   * this array!
-   */
-  items: null,
-
-  /**
-   * Add an item to the set.
-   *
-   * @param any item
-   */
-  add(item) {
-    if (!this._set.has(item)) {
-      this._set.add(item);
-      this.items.push(item);
-    }
-  },
-
-  /**
-   * Return true if the item is in the set, false otherwise.
-   *
-   * @param any item
-   * @returns Boolean
-   */
-  has(item) {
-    return this._set.has(item);
-  }
-};
--- a/devtools/server/actors/utils/TabSources.js
+++ b/devtools/server/actors/utils/TabSources.js
@@ -240,17 +240,17 @@ TabSources.prototype = {
         }
       }
 
       if (url in this._sourceMappedSourceActors) {
         return this._sourceMappedSourceActors[url];
       }
     }
 
-    throw new Error("getSourceByURL: could not find source for " + url);
+    throw new Error("getSourceActorByURL: could not find source for " + url);
     return null;
   },
 
   /**
    * Returns true if the URL likely points to a minified resource, false
    * otherwise.
    *
    * @param String aURL
--- a/devtools/server/actors/utils/moz.build
+++ b/devtools/server/actors/utils/moz.build
@@ -5,13 +5,12 @@
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 DevToolsModules(
     'actor-registry-utils.js',
     'audionodes.json',
     'automation-timeline.js',
     'make-debugger.js',
     'map-uri-to-addon-id.js',
-    'ScriptStore.js',
     'stack.js',
     'TabSources.js',
     'walker-search.js'
 )
--- a/devtools/server/actors/webbrowser.js
+++ b/devtools/server/actors/webbrowser.js
@@ -2086,51 +2086,45 @@ TabActor.prototype = {
    * @param {String} request.url
    * @param {Number} request.line
    * @param {Number?} request.column
    * @return {Promise<Object>}
    */
   onResolveLocation(request) {
     let { url, line } = request;
     let column = request.column || 0;
-    let actor = this.sources.getSourceActorByURL(url);
-
-    if (actor) {
-      // Get the generated source actor if this is source mapped
-      let generatedActor = actor.generatedSource ?
-        this.sources.createNonSourceMappedActor(actor.generatedSource) :
-        actor;
-      let generatedLocation = new GeneratedLocation(
-        generatedActor, line, column);
+    const scripts = this.threadActor.dbg.findScripts({ url });
 
-      return this.sources.getOriginalLocation(generatedLocation).then(loc => {
-        // If no map found, return this packet
-        if (loc.originalLine == null) {
-          return {
-            from: this.actorID,
-            type: "resolveLocation",
-            error: "MAP_NOT_FOUND"
-          };
-        }
-
-        loc = loc.toJSON();
-        return {
-          from: this.actorID,
-          url: loc.source.url,
-          column: loc.column,
-          line: loc.line
-        };
+    if (!scripts[0] || !scripts[0].source) {
+      return promise.resolve({
+        from: this.actorID,
+        type: "resolveLocation",
+        error: "SOURCE_NOT_FOUND"
       });
     }
+    const source = scripts[0].source;
+    const generatedActor = this.sources.createNonSourceMappedActor(source);
+    let generatedLocation = new GeneratedLocation(
+      generatedActor, line, column);
+    return this.sources.getOriginalLocation(generatedLocation).then(loc => {
+      // If no map found, return this packet
+      if (loc.originalLine == null) {
+        return {
+          type: "resolveLocation",
+          error: "MAP_NOT_FOUND"
+        };
+      }
 
-    // Fall back to this packet when source is not found
-    return promise.resolve({
-      from: this.actorID,
-      type: "resolveLocation",
-      error: "SOURCE_NOT_FOUND"
+      loc = loc.toJSON();
+      return {
+        from: this.actorID,
+        url: loc.source.url,
+        column: loc.column,
+        line: loc.line
+      };
     });
   },
 };
 
 /**
  * The request types this actor can handle.
  */
 TabActor.prototype.requestTypes = {
deleted file mode 100644
--- a/devtools/server/tests/unit/test_ScriptStore.js
+++ /dev/null
@@ -1,168 +0,0 @@
-/* -*- js-indent-level: 2; indent-tabs-mode: nil -*- */
-/* Any copyright is dedicated to the Public Domain.
-   http://creativecommons.org/publicdomain/zero/1.0/ */
-
-// Test the functionality of ScriptStore.
-
-const ScriptStore = require("devtools/server/actors/utils/ScriptStore");
-
-// Fixtures
-
-const firstSource = "firstSource";
-const secondSource = "secondSource";
-const thirdSource = "thirdSource";
-
-const scripts = new Set([
-  {
-    url: "a.js",
-    source: firstSource,
-    startLine: 1,
-    lineCount: 100,
-    global: "g1"
-  },
-  {
-    url: "a.js",
-    source: firstSource,
-    startLine: 1,
-    lineCount: 40,
-    global: "g1"
-  },
-  {
-    url: "a.js",
-    source: firstSource,
-    startLine: 50,
-    lineCount: 100,
-    global: "g1"
-  },
-  {
-    url: "a.js",
-    source: firstSource,
-    startLine: 60,
-    lineCount: 90,
-    global: "g1"
-  },
-  {
-    url: "index.html",
-    source: secondSource,
-    startLine: 150,
-    lineCount: 1,
-    global: "g2"
-  },
-  {
-    url: "index.html",
-    source: thirdSource,
-    startLine: 200,
-    lineCount: 100,
-    global: "g2"
-  },
-  {
-    url: "index.html",
-    source: thirdSource,
-    startLine: 250,
-    lineCount: 10,
-    global: "g2"
-  },
-  {
-    url: "index.html",
-    source: thirdSource,
-    startLine: 275,
-    lineCount: 5,
-    global: "g2"
-  }
-]);
-
-function contains(script, line) {
-  return script.startLine <= line &&
-    line < script.startLine + script.lineCount;
-}
-
-function run_test() {
-  testAddScript();
-  testAddScripts();
-  testGetSources();
-  testGetScriptsBySource();
-  testGetScriptsBySourceAndLine();
-  testGetScriptsByURL();
-  testGetScriptsByURLAndLine();
-}
-
-function testAddScript() {
-  const ss = new ScriptStore();
-
-  for (let s of scripts) {
-    ss.addScript(s);
-  }
-
-  equal(ss.getAllScripts().length, scripts.size);
-
-  for (let s of ss.getAllScripts()) {
-    ok(scripts.has(s));
-  }
-}
-
-function testAddScripts() {
-  const ss = new ScriptStore();
-  ss.addScripts([...scripts]);
-
-  equal(ss.getAllScripts().length, scripts.size);
-
-  for (let s of ss.getAllScripts()) {
-    ok(scripts.has(s));
-  }
-}
-
-function testGetSources() {
-  const ss = new ScriptStore();
-  ss.addScripts([...scripts]);
-
-  const expected = new Set([firstSource, secondSource, thirdSource]);
-  const actual = ss.getSources();
-  equal(expected.size, actual.length);
-
-  for (let s of actual) {
-    ok(expected.has(s));
-    expected.delete(s);
-  }
-}
-
-function testGetScriptsBySource() {
-  const ss = new ScriptStore();
-  ss.addScripts([...scripts]);
-
-  const expected = [...scripts].filter(s => s.source === thirdSource);
-  const actual = ss.getScriptsBySource(thirdSource);
-
-  deepEqual(actual, expected);
-}
-
-function testGetScriptsBySourceAndLine() {
-  const ss = new ScriptStore();
-  ss.addScripts([...scripts]);
-
-  const expected = [...scripts].filter(
-    s => s.source === firstSource && contains(s, 65));
-  const actual = ss.getScriptsBySourceAndLine(firstSource, 65);
-
-  deepEqual(actual, expected);
-}
-
-function testGetScriptsByURL() {
-  const ss = new ScriptStore();
-  ss.addScripts([...scripts]);
-
-  const expected = [...scripts].filter(s => s.url === "index.html");
-  const actual = ss.getScriptsByURL("index.html");
-
-  deepEqual(actual, expected);
-}
-
-function testGetScriptsByURLAndLine() {
-  const ss = new ScriptStore();
-  ss.addScripts([...scripts]);
-
-  const expected = [...scripts].filter(
-    s => s.url === "index.html" && contains(s, 250));
-  const actual = ss.getScriptsByURLAndLine("index.html", 250);
-
-  deepEqual(actual, expected);
-}
--- a/devtools/server/tests/unit/test_eventlooplag_actor.js
+++ b/devtools/server/tests/unit/test_eventlooplag_actor.js
@@ -4,17 +4,17 @@
 /**
  * Test the eventLoopLag actor.
  */
 
 "use strict";
 
 function run_test()
 {
-  let {EventLoopLagFront} = require("devtools/server/actors/eventlooplag");
+  let {EventLoopLagFront} = require("devtools/shared/fronts/eventlooplag");
 
   DebuggerServer.init();
   DebuggerServer.addBrowserActors();
 
   // As seen in EventTracer.cpp
   let threshold = 20;
   let interval = 10;
 
--- a/devtools/server/tests/unit/xpcshell.ini
+++ b/devtools/server/tests/unit/xpcshell.ini
@@ -32,17 +32,16 @@ support-files =
   setBreakpoint-on-line-with-no-offsets-in-gcd-script.js
   addons/web-extension/manifest.json
   addons/web-extension2/manifest.json
 
 [test_addon_reload.js]
 [test_addons_actor.js]
 [test_animation_name.js]
 [test_animation_type.js]
-[test_ScriptStore.js]
 [test_actor-registry-actor.js]
 [test_nesting-01.js]
 [test_nesting-02.js]
 [test_nesting-03.js]
 [test_forwardingprefix.js]
 [test_getyoungestframe.js]
 [test_nsjsinspector.js]
 [test_dbgactor.js]
--- a/devtools/shared/css-parsing-utils.js
+++ b/devtools/shared/css-parsing-utils.js
@@ -342,17 +342,19 @@ function parseDeclarationsInternal(isCss
     } else if (token.tokenType === "ident") {
       if (token.text === "important" && hasBang) {
         lastProp.priority = "important";
         hasBang = false;
       } else {
         if (hasBang) {
           current += "!";
         }
-        current += token.text;
+        // Re-escape the token to avoid dequoting problems.
+        // See bug 1287620.
+        current += CSS.escape(token.text);
       }
     } else if (token.tokenType === "symbol" && token.text === "!") {
       hasBang = true;
     } else if (token.tokenType === "whitespace") {
       if (current !== "") {
         current += " ";
       }
     } else if (token.tokenType === "comment") {
new file mode 100644
--- /dev/null
+++ b/devtools/shared/fronts/eventlooplag.js
@@ -0,0 +1,15 @@
+/* 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";
+
+const { Front, FrontClassWithSpec } = require("devtools/shared/protocol");
+const { eventLoopLagSpec } = require("devtools/shared/specs/eventlooplag");
+
+exports.EventLoopLagFront = FrontClassWithSpec(eventLoopLagSpec, {
+  initialize: function (client, form) {
+    Front.prototype.initialize.call(this, client);
+    this.actorID = form.eventLoopLagActor;
+    this.manage(this);
+  },
+});
--- a/devtools/shared/fronts/moz.build
+++ b/devtools/shared/fronts/moz.build
@@ -10,16 +10,17 @@ DevToolsModules(
     'animation.js',
     'call-watcher.js',
     'canvas.js',
     'css-properties.js',
     'csscoverage.js',
     'device.js',
     'director-manager.js',
     'director-registry.js',
+    'eventlooplag.js',
     'framerate.js',
     'gcli.js',
     'highlighters.js',
     'inspector.js',
     'layout.js',
     'memory.js',
     'performance-recording.js',
     'performance.js',
new file mode 100644
--- /dev/null
+++ b/devtools/shared/specs/eventlooplag.js
@@ -0,0 +1,31 @@
+/* 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";
+
+const { Arg, RetVal, generateActorSpec } = require("devtools/shared/protocol");
+
+const eventLoopLagSpec = generateActorSpec({
+  typeName: "eventLoopLag",
+
+  events: {
+    "event-loop-lag": {
+      type: "event-loop-lag",
+      // duration of the lag in milliseconds.
+      time: Arg(0, "number")
+    }
+  },
+
+  methods: {
+    start: {
+      request: {},
+      response: {success: RetVal("number")}
+    },
+    stop: {
+      request: {},
+      response: {}
+    }
+  }
+});
+
+exports.eventLoopLagSpec = eventLoopLagSpec;
--- a/devtools/shared/specs/moz.build
+++ b/devtools/shared/specs/moz.build
@@ -12,16 +12,17 @@ DevToolsModules(
     'call-watcher.js',
     'canvas.js',
     'css-properties.js',
     'csscoverage.js',
     'device.js',
     'director-manager.js',
     'director-registry.js',
     'environment.js',
+    'eventlooplag.js',
     'frame.js',
     'framerate.js',
     'gcli.js',
     'heap-snapshot-file.js',
     'highlighters.js',
     'inspector.js',
     'layout.js',
     'memory.js',
--- a/devtools/shared/webconsole/test/common.js
+++ b/devtools/shared/webconsole/test/common.js
@@ -92,16 +92,19 @@ function _attachConsole(aListeners, aCal
           aCallback(aState, aResponse);
           return;
         }
         let tab = aResponse.tabs[aResponse.selected];
         aState.dbgClient.attachTab(tab.actor, function (response, tabClient) {
           if (aAttachToWorker) {
             let workerName = "console-test-worker.js#" + new Date().getTime();
             var worker = new Worker(workerName);
+            // Keep a strong reference to the Worker to avoid it being
+            // GCd during the test (bug 1237492).
+            aState._worker_ref = worker;
             worker.addEventListener("message", function listener() {
               worker.removeEventListener("message", listener);
               tabClient.listWorkers(function (response) {
                 let worker = response.workers.filter(w => w.url == workerName)[0];
                 if (!worker) {
                   console.error("listWorkers failed. Unable to find the " +
                                 "worker actor\n");
                   return;
--- a/dom/base/nsContentCreatorFunctions.h
+++ b/dom/base/nsContentCreatorFunctions.h
@@ -25,26 +25,28 @@ namespace dom {
 class Element;
 class NodeInfo;
 } // namespace dom
 } // namespace mozilla
 
 nsresult
 NS_NewElement(mozilla::dom::Element** aResult,
               already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo,
-              mozilla::dom::FromParser aFromParser);
+              mozilla::dom::FromParser aFromParser,
+              nsAString* aIs = nullptr);
 
 nsresult
 NS_NewXMLElement(mozilla::dom::Element** aResult,
                  already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo);
 
 nsresult
 NS_NewHTMLElement(mozilla::dom::Element** aResult,
                   already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo,
-                  mozilla::dom::FromParser aFromParser);
+                  mozilla::dom::FromParser aFromParser,
+                  nsAString* aIs = nullptr);
 
 // First argument should be nsHTMLTag, but that adds dependency to parser
 // for a bunch of files.
 already_AddRefed<nsGenericHTMLElement>
 CreateHTMLElement(uint32_t aNodeType,
                   already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo,
                   mozilla::dom::FromParser aFromParser);
 
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -5585,73 +5585,108 @@ nsDocument::GetDocumentElement(nsIDOMEle
 }
 
 NS_IMETHODIMP
 nsDocument::CreateElement(const nsAString& aTagName,
                           nsIDOMElement** aReturn)
 {
   *aReturn = nullptr;
   ErrorResult rv;
-  nsCOMPtr<Element> element = nsIDocument::CreateElement(aTagName, rv);
+  ElementCreationOptions options;
+  nsCOMPtr<Element> element = CreateElement(aTagName, options, rv);
   NS_ENSURE_FALSE(rv.Failed(), rv.StealNSResult());
   return CallQueryInterface(element, aReturn);
 }
 
 bool IsLowercaseASCII(const nsAString& aValue)
 {
   int32_t len = aValue.Length();
   for (int32_t i = 0; i < len; ++i) {
     char16_t c = aValue[i];
     if (!(0x0061 <= (c) && ((c) <= 0x007a))) {
       return false;
     }
   }
   return true;
 }
 
+CustomElementDefinition*
+nsDocument::LookupCustomElementDefinition(const nsAString& aLocalName,
+                                          uint32_t aNameSpaceID,
+                                          const nsAString* aIs)
+{
+  if (!mRegistry || aNameSpaceID != kNameSpaceID_XHTML) {
+    return nullptr;
+  }
+
+  nsCOMPtr<nsIAtom> localNameAtom = NS_Atomize(aLocalName);
+  nsCOMPtr<nsIAtom> typeAtom = aIs ? NS_Atomize(*aIs) : localNameAtom;
+
+  CustomElementDefinition* data;
+  CustomElementHashKey key(aNameSpaceID, typeAtom);
+  if (mRegistry->mCustomDefinitions.Get(&key, &data) &&
+      data->mLocalName == localNameAtom) {
+    return data;
+  }
+
+  return nullptr;
+}
+
 already_AddRefed<Element>
-nsIDocument::CreateElement(const nsAString& aTagName, ErrorResult& rv)
+nsDocument::CreateElement(const nsAString& aTagName,
+                          const ElementCreationOptions& aOptions,
+                          ErrorResult& rv)
 {
   rv = nsContentUtils::CheckQName(aTagName, false);
   if (rv.Failed()) {
     return nullptr;
   }
 
   bool needsLowercase = IsHTMLDocument() && !IsLowercaseASCII(aTagName);
   nsAutoString lcTagName;
   if (needsLowercase) {
     nsContentUtils::ASCIIToLower(aTagName, lcTagName);
   }
 
-  return CreateElem(needsLowercase ? lcTagName : aTagName, nullptr,
-                    mDefaultElementType);
+  // Throw NotFoundError if 'is' is not-null and definition is null
+  nsString* is = CheckCustomElementName(
+    aOptions, needsLowercase ? lcTagName : aTagName, mDefaultElementType, rv);
+  if (rv.Failed()) {
+    return nullptr;
+  }
+
+  RefPtr<Element> elem = CreateElem(
+    needsLowercase ? lcTagName : aTagName, nullptr, mDefaultElementType, is);
+
+  return elem.forget();
 }
 
 void
 nsDocument::SetupCustomElement(Element* aElement,
                                uint32_t aNamespaceID,
                                const nsAString* aTypeExtension)
 {
-  if (!mRegistry) {
+  if (!mRegistry || aNamespaceID != kNameSpaceID_XHTML) {
     return;
   }
 
   nsCOMPtr<nsIAtom> tagAtom = aElement->NodeInfo()->NameAtom();
   nsCOMPtr<nsIAtom> typeAtom = aTypeExtension ?
     NS_Atomize(*aTypeExtension) : tagAtom;
 
   if (aTypeExtension && !aElement->HasAttr(kNameSpaceID_None, nsGkAtoms::is)) {
     // Custom element setup in the parser happens after the "is"
     // attribute is added.
     aElement->SetAttr(kNameSpaceID_None, nsGkAtoms::is, *aTypeExtension, true);
   }
 
-  CustomElementDefinition* data;
-  CustomElementHashKey key(aNamespaceID, typeAtom);
-  if (!mRegistry->mCustomDefinitions.Get(&key, &data)) {
+  CustomElementDefinition* data = LookupCustomElementDefinition(
+    aElement->NodeInfo()->LocalName(), aNamespaceID, aTypeExtension);
+
+  if (!data) {
     // The type extension doesn't exist in the registry,
     // thus we don't need to enqueue callback or adjust
     // the "is" attribute, but it is possibly an upgrade candidate.
     RegisterUnresolvedElement(aElement, typeAtom);
     return;
   }
 
   if (data->mLocalName != tagAtom) {
@@ -5661,105 +5696,61 @@ nsDocument::SetupCustomElement(Element* 
     return;
   }
 
   // Enqueuing the created callback will set the CustomElementData on the
   // element, causing prototype swizzling to occur in Element::WrapObject.
   EnqueueLifecycleCallback(nsIDocument::eCreated, aElement, nullptr, data);
 }
 
-already_AddRefed<Element>
-nsDocument::CreateElement(const nsAString& aTagName,
-                          const nsAString& aTypeExtension,
-                          ErrorResult& rv)
-{
-  RefPtr<Element> elem = nsIDocument::CreateElement(aTagName, rv);
-  if (rv.Failed()) {
-    return nullptr;
-  }
-
-  if (!aTypeExtension.IsVoid() &&
-      !aTagName.Equals(aTypeExtension)) {
-    // do not process 'is' if it is null or the extended type is the same as
-    // the localName
-    SetupCustomElement(elem, GetDefaultNamespaceID(), &aTypeExtension);
-  }
-
-  return elem.forget();
-}
-
 NS_IMETHODIMP
 nsDocument::CreateElementNS(const nsAString& aNamespaceURI,
                             const nsAString& aQualifiedName,
                             nsIDOMElement** aReturn)
 {
   *aReturn = nullptr;
+  ElementCreationOptions options;
   ErrorResult rv;
   nsCOMPtr<Element> element =
-    nsIDocument::CreateElementNS(aNamespaceURI, aQualifiedName, rv);
+    CreateElementNS(aNamespaceURI, aQualifiedName, options, rv);
   NS_ENSURE_FALSE(rv.Failed(), rv.StealNSResult());
   return CallQueryInterface(element, aReturn);
 }
 
 already_AddRefed<Element>
-nsIDocument::CreateElementNS(const nsAString& aNamespaceURI,
-                             const nsAString& aQualifiedName,
-                             ErrorResult& rv)
+nsDocument::CreateElementNS(const nsAString& aNamespaceURI,
+                            const nsAString& aQualifiedName,
+                            const ElementCreationOptions& aOptions,
+                            ErrorResult& rv)
 {
   RefPtr<mozilla::dom::NodeInfo> nodeInfo;
   rv = nsContentUtils::GetNodeInfoFromQName(aNamespaceURI,
                                             aQualifiedName,
                                             mNodeInfoManager,
                                             nsIDOMNode::ELEMENT_NODE,
                                             getter_AddRefs(nodeInfo));
   if (rv.Failed()) {
     return nullptr;
   }
 
-  nsCOMPtr<Element> element;
-  rv = NS_NewElement(getter_AddRefs(element), nodeInfo.forget(),
-                     NOT_FROM_PARSER);
-  if (rv.Failed()) {
-    return nullptr;
-  }
-  return element.forget();
-}
-
-already_AddRefed<Element>
-nsDocument::CreateElementNS(const nsAString& aNamespaceURI,
-                            const nsAString& aQualifiedName,
-                            const nsAString& aTypeExtension,
-                            ErrorResult& rv)
-{
-  RefPtr<Element> elem = nsIDocument::CreateElementNS(aNamespaceURI,
-                                                        aQualifiedName,
-                                                        rv);
+  // Throw NotFoundError if 'is' is not-null and definition is null
+  nsString* is = CheckCustomElementName(
+    aOptions, aQualifiedName, nodeInfo->NamespaceID(), rv);
   if (rv.Failed()) {
     return nullptr;
   }
 
-  if (aTypeExtension.IsVoid() ||
-      aQualifiedName.Equals(aTypeExtension)) {
-    // do not process 'is' if it is null or the extended type is the same as
-    // the localName
-    return elem.forget();
-  }
-
-  int32_t nameSpaceId = kNameSpaceID_Wildcard;
-  if (!aNamespaceURI.EqualsLiteral("*")) {
-    rv = nsContentUtils::NameSpaceManager()->RegisterNameSpace(aNamespaceURI,
-                                                               nameSpaceId);
-    if (rv.Failed()) {
-      return nullptr;
-    }
-  }
-
-  SetupCustomElement(elem, nameSpaceId, &aTypeExtension);
-
-  return elem.forget();
+  nsCOMPtr<Element> element;
+  rv = NS_NewElement(getter_AddRefs(element), nodeInfo.forget(),
+                     NOT_FROM_PARSER, is);
+  if (rv.Failed()) {
+    return nullptr;
+  }
+
+  return element.forget();
 }
 
 NS_IMETHODIMP
 nsDocument::CreateTextNode(const nsAString& aData, nsIDOMText** aReturn)
 {
   *aReturn = nsIDocument::CreateTextNode(aData).take();
   return NS_OK;
 }
@@ -8779,17 +8770,17 @@ nsDocument::RetrieveRelevantHeaders(nsIC
   mLastModified.Truncate();
   if (modDate != 0) {
     GetFormattedTimeString(modDate, mLastModified);
   }
 }
 
 already_AddRefed<Element>
 nsDocument::CreateElem(const nsAString& aName, nsIAtom *aPrefix,
-                       int32_t aNamespaceID)
+                       int32_t aNamespaceID, nsAString* aIs)
 {
 #ifdef DEBUG
   nsAutoString qName;
   if (aPrefix) {
     aPrefix->ToString(qName);
     qName.Append(':');
   }
   qName.Append(aName);
@@ -8806,17 +8797,17 @@ nsDocument::CreateElem(const nsAString& 
   RefPtr<mozilla::dom::NodeInfo> nodeInfo;
   mNodeInfoManager->GetNodeInfo(aName, aPrefix, aNamespaceID,
                                 nsIDOMNode::ELEMENT_NODE,
                                 getter_AddRefs(nodeInfo));
   NS_ENSURE_TRUE(nodeInfo, nullptr);
 
   nsCOMPtr<Element> element;
   nsresult rv = NS_NewElement(getter_AddRefs(element), nodeInfo.forget(),
-                              NOT_FROM_PARSER);
+                              NOT_FROM_PARSER, aIs);
   return NS_SUCCEEDED(rv) ? element.forget() : nullptr;
 }
 
 bool
 nsDocument::IsSafeToFlush() const
 {
   nsIPresShell* shell = GetShell();
   if (!shell)
@@ -13492,8 +13483,31 @@ nsIDocument::UpdateStyleBackendType()
     nsLayoutUtils::SupportsServoStyleBackend(this) &&
     mDocumentContainer ?
       StyleBackendType::Servo :
       StyleBackendType::Gecko;
 #else
   mStyleBackendType = StyleBackendType::Gecko;
 #endif
 }
+
+nsString*
+nsDocument::CheckCustomElementName(const ElementCreationOptions& aOptions,
+                                   const nsAString& aLocalName,
+                                   uint32_t aNamespaceID,
+                                   ErrorResult& rv)
+{
+  // only check aOptions if 'is' is passed and the webcomponents preference
+  // is enabled
+  if (!aOptions.mIs.WasPassed() ||
+      !Preferences::GetBool("dom.webcomponents.enabled")) {
+      return nullptr;
+  }
+
+  nsString* is = const_cast<nsString*>(&(aOptions.mIs.Value()));
+
+  // Throw NotFoundError if 'is' is not-null and definition is null
+  if (!LookupCustomElementDefinition(aLocalName, aNamespaceID, is)) {
+      rv.Throw(NS_ERROR_DOM_NOT_FOUND_ERR);
+  }
+
+  return is;
+}
--- a/dom/base/nsDocument.h
+++ b/dom/base/nsDocument.h
@@ -999,17 +999,18 @@ public:
   NS_DECL_NSIOBSERVER
 
   NS_DECL_NSIDOMXPATHEVALUATOR
 
   virtual nsresult Init();
 
   virtual already_AddRefed<Element> CreateElem(const nsAString& aName,
                                                nsIAtom* aPrefix,
-                                               int32_t aNamespaceID) override;
+                                               int32_t aNamespaceID,
+                                               nsAString* aIs = nullptr) override;
 
   virtual void Sanitize() override;
 
   virtual void EnumerateSubDocuments(nsSubDocEnumFunc aCallback,
                                                  void *aData) override;
 
   virtual bool CanSavePresentation(nsIRequest *aNewRequest) override;
   virtual void Destroy() override;
@@ -1327,24 +1328,22 @@ public:
                     const mozilla::dom::ElementRegistrationOptions& aOptions,
                     JS::MutableHandle<JSObject*> aRetval,
                     mozilla::ErrorResult& rv) override;
   virtual mozilla::dom::StyleSheetList* StyleSheets() override;
   virtual void SetSelectedStyleSheetSet(const nsAString& aSheetSet) override;
   virtual void GetLastStyleSheetSet(nsString& aSheetSet) override;
   virtual mozilla::dom::DOMStringList* StyleSheetSets() override;
   virtual void EnableStyleSheetsForSet(const nsAString& aSheetSet) override;
-  using nsIDocument::CreateElement;
-  using nsIDocument::CreateElementNS;
   virtual already_AddRefed<Element> CreateElement(const nsAString& aTagName,
-                                                  const nsAString& aTypeExtension,
-                                                  mozilla::ErrorResult& rv) override;
+                                                  const mozilla::dom::ElementCreationOptions& aOptions,
+                                                  ErrorResult& rv) override;
   virtual already_AddRefed<Element> CreateElementNS(const nsAString& aNamespaceURI,
                                                     const nsAString& aQualifiedName,
-                                                    const nsAString& aTypeExtension,
+                                                    const mozilla::dom::ElementCreationOptions& aOptions,
                                                     mozilla::ErrorResult& rv) override;
   virtual void UseRegistryFromDocument(nsIDocument* aDocument) override;
 
   virtual nsIDocument* MasterDocument() override
   {
     return mMasterDocument ? mMasterDocument.get()
                            : this;
   }
@@ -1581,16 +1580,37 @@ private:
   // element queues. Each queue is represented by a sequence of
   // CustomElementData in this array, separated by nullptr that
   // represent the boundaries of the items in the stack. The first
   // queue in the stack is the base element queue.
   static mozilla::Maybe<nsTArray<RefPtr<mozilla::dom::CustomElementData>>> sProcessingStack;
 
   static bool CustomElementConstructor(JSContext* aCx, unsigned aArgc, JS::Value* aVp);
 
+  /**
+   * Looking up a custom element definition.
+   * https://html.spec.whatwg.org/#look-up-a-custom-element-definition
+   */
+  mozilla::dom::CustomElementDefinition* LookupCustomElementDefinition(
+    const nsAString& aLocalName, uint32_t aNameSpaceID, const nsAString* aIs);
+
+  /**
+   * Check if the passed custom element name, aOptions.mIs, is a registered
+   * custom element type or not, then return the custom element name for future
+   * usage.
+   *
+   * If there is no existing custom element definition for this name, throw a
+   * NotFoundError.
+   */
+  nsString* CheckCustomElementName(
+    const mozilla::dom::ElementCreationOptions& aOptions,
+    const nsAString& aLocalName,
+    uint32_t aNamespaceID,
+    ErrorResult& rv);
+
 public:
   // Enqueue created callback or register upgrade candidate for
   // newly created custom elements, possibly extending an existing type.
   // ex. <x-button>, <button is="x-button> (type extension)
   virtual void SetupCustomElement(Element* aElement,
                                   uint32_t aNamespaceID,
                                   const nsAString* aTypeExtension) override;
 
--- a/dom/base/nsIDocument.h
+++ b/dom/base/nsIDocument.h
@@ -113,16 +113,17 @@ class CDATASection;
 class Comment;
 struct CustomElementDefinition;
 class DocumentFragment;
 class DocumentTimeline;
 class DocumentType;
 class DOMImplementation;
 class DOMStringList;
 class Element;
+struct ElementCreationOptions;
 struct ElementRegistrationOptions;
 class Event;
 class EventTarget;
 class FontFaceSet;
 class FrameRequestCallback;
 struct FullscreenRequest;
 class ImportManager;
 class HTMLBodyElement;
@@ -1503,17 +1504,18 @@ public:
   virtual bool IsScriptEnabled() = 0;
 
   /**
    * Create an element with the specified name, prefix and namespace ID.
    * Returns null if element name parsing failed.
    */
   virtual already_AddRefed<Element> CreateElem(const nsAString& aName,
                                                nsIAtom* aPrefix,
-                                               int32_t aNamespaceID) = 0;
+                                               int32_t aNamespaceID,
+                                               nsAString* aIs = nullptr) = 0;
 
   /**
    * Get the security info (i.e. SSL state etc) that the document got
    * from the channel/document that created the content of the
    * document.
    *
    * @see nsIChannel
    */
@@ -2513,28 +2515,25 @@ public:
   }
   already_AddRefed<nsContentList>
     GetElementsByTagNameNS(const nsAString& aNamespaceURI,
                            const nsAString& aLocalName,
                            mozilla::ErrorResult& aResult);
   already_AddRefed<nsContentList>
     GetElementsByClassName(const nsAString& aClasses);
   // GetElementById defined above
-  already_AddRefed<Element> CreateElement(const nsAString& aTagName,
-                                          mozilla::ErrorResult& rv);
-  already_AddRefed<Element> CreateElementNS(const nsAString& aNamespaceURI,
-                                            const nsAString& aQualifiedName,
-                                            mozilla::ErrorResult& rv);
-  virtual already_AddRefed<Element> CreateElement(const nsAString& aTagName,
-                                                  const nsAString& aTypeExtension,
-                                                  mozilla::ErrorResult& rv) = 0;
-  virtual already_AddRefed<Element> CreateElementNS(const nsAString& aNamespaceURI,
-                                                    const nsAString& aQualifiedName,
-                                                    const nsAString& aTypeExtension,
-                                                    mozilla::ErrorResult& rv) = 0;
+  virtual already_AddRefed<Element>
+    CreateElement(const nsAString& aTagName,
+                  const mozilla::dom::ElementCreationOptions& aOptions,
+                  mozilla::ErrorResult& rv) = 0;
+  virtual already_AddRefed<Element>
+    CreateElementNS(const nsAString& aNamespaceURI,
+                    const nsAString& aQualifiedName,
+                    const mozilla::dom::ElementCreationOptions& aOptions,
+                    mozilla::ErrorResult& rv) = 0;
   already_AddRefed<mozilla::dom::DocumentFragment>
     CreateDocumentFragment() const;
   already_AddRefed<nsTextNode> CreateTextNode(const nsAString& aData) const;
   already_AddRefed<mozilla::dom::Comment>
     CreateComment(const nsAString& aData) const;
   already_AddRefed<mozilla::dom::ProcessingInstruction>
     CreateProcessingInstruction(const nsAString& target, const nsAString& data,
                                 mozilla::ErrorResult& rv) const;
--- a/dom/base/nsNameSpaceManager.cpp
+++ b/dom/base/nsNameSpaceManager.cpp
@@ -134,22 +134,23 @@ nsNameSpaceManager::GetNameSpaceID(nsIAt
   }
 
   return kNameSpaceID_Unknown;
 }
 
 nsresult
 NS_NewElement(Element** aResult,
               already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo,
-              FromParser aFromParser)
+              FromParser aFromParser,
+              nsAString* aIs)
 {
   RefPtr<mozilla::dom::NodeInfo> ni = aNodeInfo;
   int32_t ns = ni->NamespaceID();
   if (ns == kNameSpaceID_XHTML) {
-    return NS_NewHTMLElement(aResult, ni.forget(), aFromParser);
+    return NS_NewHTMLElement(aResult, ni.forget(), aFromParser, aIs);
   }
 #ifdef MOZ_XUL
   if (ns == kNameSpaceID_XUL) {
     return NS_NewXULElement(aResult, ni.forget());
   }
 #endif
   if (ns == kNameSpaceID_MathML) {
     return NS_NewMathMLElement(aResult, ni.forget());
--- a/dom/html/HTMLInputElement.cpp
+++ b/dom/html/HTMLInputElement.cpp
@@ -3897,30 +3897,35 @@ HTMLInputElement::CancelRangeThumbDrag(b
   }
 }
 
 void
 HTMLInputElement::SetValueOfRangeForUserEvent(Decimal aValue)
 {
   MOZ_ASSERT(aValue.isFinite());
 
+  Decimal oldValue = GetValueAsDecimal();
+
   nsAutoString val;
   ConvertNumberToString(aValue, val);
   // TODO: What should we do if SetValueInternal fails?  (The allocation
   // is small, so we should be fine here.)
   SetValueInternal(val, nsTextEditorState::eSetValue_BySetUserInput |
                         nsTextEditorState::eSetValue_Notify);
   nsRangeFrame* frame = do_QueryFrame(GetPrimaryFrame());
   if (frame) {
     frame->UpdateForValueChange();
   }
-  nsContentUtils::DispatchTrustedEvent(OwnerDoc(),
-                                       static_cast<nsIDOMHTMLInputElement*>(this),
-                                       NS_LITERAL_STRING("input"), true,
-                                       false);
+
+  if (GetValueAsDecimal() != oldValue) {
+    nsContentUtils::DispatchTrustedEvent(OwnerDoc(),
+                                         static_cast<nsIDOMHTMLInputElement*>(this),
+                                         NS_LITERAL_STRING("input"), true,
+                                         false);
+  }
 }
 
 void
 HTMLInputElement::StartNumberControlSpinnerSpin()
 {
   MOZ_ASSERT(!mNumberControlSpinnerIsSpinning);
 
   mNumberControlSpinnerIsSpinning = true;
--- a/dom/html/HTMLMediaElement.cpp
+++ b/dom/html/HTMLMediaElement.cpp
@@ -3091,21 +3091,61 @@ HTMLMediaElement::ReportTelemetry()
       uint32_t percentage = 100 * droppedFrames / totalFrames;
       LOG(LogLevel::Debug,
           ("Reporting telemetry DROPPED_FRAMES_IN_VIDEO_PLAYBACK"));
       Telemetry::Accumulate(Telemetry::VIDEO_DROPPED_FRAMES_PROPORTION,
                             percentage);
     }
   }
 
-  Telemetry::Accumulate(Telemetry::VIDEO_PLAY_TIME_MS, SECONDS_TO_MS(mPlayTime.Total()));
-  LOG(LogLevel::Debug, ("%p VIDEO_PLAY_TIME_MS = %f", this, mPlayTime.Total()));
-
-  Telemetry::Accumulate(Telemetry::VIDEO_HIDDEN_PLAY_TIME_MS, SECONDS_TO_MS(mHiddenPlayTime.Total()));
-  LOG(LogLevel::Debug, ("%p VIDEO_HIDDEN_PLAY_TIME_MS = %f", this, mHiddenPlayTime.Total()));
+  double playTime = mPlayTime.Total();
+  double hiddenPlayTime = mHiddenPlayTime.Total();
+
+  Telemetry::Accumulate(Telemetry::VIDEO_PLAY_TIME_MS, SECONDS_TO_MS(playTime));
+  LOG(LogLevel::Debug, ("%p VIDEO_PLAY_TIME_MS = %f", this, playTime));
+
+  Telemetry::Accumulate(Telemetry::VIDEO_HIDDEN_PLAY_TIME_MS, SECONDS_TO_MS(hiddenPlayTime));
+  LOG(LogLevel::Debug, ("%p VIDEO_HIDDEN_PLAY_TIME_MS = %f", this, hiddenPlayTime));
+
+  if (playTime > 0.0 &&
+      mMediaInfo.HasVideo() &&
+      mMediaInfo.mVideo.mImage.height > 0) {
+    // We have actually played some valid video -> Report hidden/total ratio.
+    uint32_t hiddenPercentage = uint32_t(hiddenPlayTime / playTime * 100.0 + 0.5);
+
+    // Keyed by audio+video or video alone, and by a resolution range.
+    nsCString key(mMediaInfo.HasAudio() ? "AV," : "V,");
+    static const struct { int32_t mH; const char* mRes; } sResolutions[] = {
+      {  240, "0<h<=240" },
+      {  480, "240<h<=480" },
+      {  576, "480<h<=576" },
+      {  720, "576<h<=720" },
+      { 1080, "720<h<=1080" },
+      { 2160, "1080<h<=2160" }
+    };
+    const char* resolution = "h>2160";
+    int32_t height = mMediaInfo.mVideo.mImage.height;
+    for (const auto& res : sResolutions) {
+      if (height <= res.mH) {
+        resolution = res.mRes;
+        break;
+      }
+    }
+    key.AppendASCII(resolution);
+
+    Telemetry::Accumulate(Telemetry::VIDEO_HIDDEN_PLAY_TIME_PERCENTAGE,
+                          key,
+                          hiddenPercentage);
+    // Also accumulate all percentages in an "All" key.
+    Telemetry::Accumulate(Telemetry::VIDEO_HIDDEN_PLAY_TIME_PERCENTAGE,
+                          NS_LITERAL_CSTRING("All"),
+                          hiddenPercentage);
+    LOG(LogLevel::Debug, ("%p VIDEO_HIDDEN_PLAY_TIME_PERCENTAGE = %u, keys: '%s' and 'All'",
+                          this, hiddenPercentage, key.get()));
+  }
 }
 
 void HTMLMediaElement::UnbindFromTree(bool aDeep,
                                       bool aNullParent)
 {
   if (!mPaused && mNetworkState != nsIDOMHTMLMediaElement::NETWORK_EMPTY) {
     Pause();
   }
--- a/dom/html/HTMLMediaElement.h
+++ b/dom/html/HTMLMediaElement.h
@@ -1570,44 +1570,57 @@ protected:
     // tree before.
     ELEMENT_NOT_INTREE_HAD_INTREE
   };
 
   ElementInTreeState mElementInTreeState;
 
 public:
   // Helper class to measure times for MSE telemetry stats
-  class TimeDurationAccumulator {
+  class TimeDurationAccumulator
+  {
   public:
     TimeDurationAccumulator()
       : mCount(0)
+    {}
+    void Start()
     {
-    }
-    void Start() {
       if (IsStarted()) {
         return;
       }
       mStartTime = TimeStamp::Now();
     }
-    void Pause() {
+    void Pause()
+    {
       if (!IsStarted()) {
         return;
       }
       mSum += (TimeStamp::Now() - mStartTime);
       mCount++;
       mStartTime = TimeStamp();
     }
-    bool IsStarted() const {
+    bool IsStarted() const
+    {
       return !mStartTime.IsNull();
     }
-    double Total() const {
-      return mSum.ToSeconds();
+    double Total() const
+    {
+      if (!IsStarted()) {
+        return mSum.ToSeconds();
+      }
+      // Add current running time until now, but keep it running.
+      return (mSum + (TimeStamp::Now() - mStartTime)).ToSeconds();
     }
-    uint32_t Count() const {
-      return mCount;
+    uint32_t Count() const
+    {
+      if (!IsStarted()) {
+        return mCount;
+      }
+      // Count current run in this report, without increasing the stored count.
+      return mCount + 1;
     }
   private:
     TimeStamp mStartTime;
     TimeDuration mSum;
     uint32_t mCount;
   };
 private:
   // Total time a video has spent playing.
--- a/dom/html/nsHTMLContentSink.cpp
+++ b/dom/html/nsHTMLContentSink.cpp
@@ -231,17 +231,17 @@ public:
 
   Node* mStack;
   int32_t mStackSize;
   int32_t mStackPos;
 };
 
 nsresult
 NS_NewHTMLElement(Element** aResult, already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo,
-                  FromParser aFromParser)
+                  FromParser aFromParser, nsAString* aIs)
 {
   *aResult = nullptr;
 
   RefPtr<mozilla::dom::NodeInfo> nodeInfo = aNodeInfo;
 
   nsIParserService* parserService = nsContentUtils::GetParserService();
   if (!parserService)
     return NS_ERROR_OUT_OF_MEMORY;
@@ -249,26 +249,27 @@ NS_NewHTMLElement(Element** aResult, alr
   nsIAtom *name = nodeInfo->NameAtom();
 
   NS_ASSERTION(nodeInfo->NamespaceEquals(kNameSpaceID_XHTML),
                "Trying to HTML elements that don't have the XHTML namespace");
 
   // Per the Custom Element specification, unknown tags that are valid custom
   // element names should be HTMLElement instead of HTMLUnknownElement.
   int32_t tag = parserService->HTMLCaseSensitiveAtomTagToId(name);
-  if (tag == eHTMLTag_userdefined &&
-      nsContentUtils::IsCustomElementName(name)) {
+  if ((tag == eHTMLTag_userdefined &&
+      nsContentUtils::IsCustomElementName(name)) ||
+      aIs) {
     nsIDocument* doc = nodeInfo->GetDocument();
 
     NS_IF_ADDREF(*aResult = NS_NewHTMLElement(nodeInfo.forget(), aFromParser));
     if (!*aResult) {
       return NS_ERROR_OUT_OF_MEMORY;
     }
 
-    doc->SetupCustomElement(*aResult, kNameSpaceID_XHTML);
+    doc->SetupCustomElement(*aResult, kNameSpaceID_XHTML, aIs);
 
     return NS_OK;
   }
 
   *aResult = CreateHTMLElement(tag,
                                nodeInfo.forget(), aFromParser).take();
   return *aResult ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
 }
--- a/dom/html/test/mochitest.ini
+++ b/dom/html/test/mochitest.ini
@@ -452,16 +452,17 @@ skip-if = (toolkit == 'gonk' && debug) #
 [test_bug893537.html]
 [test_bug95530.html]
 [test_bug969346.html]
 [test_bug982039.html]
 [test_bug1003539.html]
 [test_bug1045270.html]
 [test_bug1146116.html]
 [test_bug1264157.html]
+[test_bug1287321.html]
 [test_change_crossorigin.html]
 [test_checked.html]
 [test_dir_attributes_reflection.html]
 [test_dl_attributes_reflection.html]
 [test_element_prototype.html]
 [test_embed_attributes_reflection.html]
 [test_formData.html]
 [test_formSubmission.html]
new file mode 100644
--- /dev/null
+++ b/dom/html/test/test_bug1287321.html
@@ -0,0 +1,57 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1287321
+-->
+<head>
+  <meta charset="utf-8">
+  <title>Test for Bug 1287321</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <script type="application/javascript">
+
+  /** Test for Bug 1287321 **/
+
+  function test() {
+    var r = document.getElementById("range");
+    var rect = r.getBoundingClientRect();
+    var y = parseInt((rect.height / 2));
+    var movement = parseInt(rect.width / 10);
+    var x = movement;
+    synthesizeMouse(r, x, y, { type: "mousedown" });
+    x += movement;
+    var eventCount = 0;
+    r.oninput = function() {
+      ++eventCount;
+    }
+    synthesizeMouse(r, x, y, { type: "mousemove" });
+    is(eventCount, 1, "Got the expected input event");
+
+    x += movement;
+    synthesizeMouse(r, x, y, { type: "mousemove" });
+    is(eventCount, 2, "Got the expected input event");
+
+    synthesizeMouse(r, x, y, { type: "mousemove" });
+    is(eventCount, 2, "Got the expected input event");
+
+    x += movement;
+    synthesizeMouse(r, x, y, { type: "mousemove" });
+    is(eventCount, 3, "Got the expected input event");
+
+    synthesizeMouse(r, x, y, { type: "mouseup" });
+    is(eventCount, 3, "Got the expected input event");
+
+    SimpleTest.finish();
+  }
+
+  SimpleTest.waitForExplicitFinish();
+  SimpleTest.waitForFocus(test);
+
+  </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1287321">Mozilla Bug 1287321</a>
+<input type="range" id="range">
+</body>
+</html>
--- a/dom/ipc/ContentProcess.h
+++ b/dom/ipc/ContentProcess.h
@@ -6,16 +6,20 @@
 
 #ifndef dom_tabs_ContentThread_h
 #define dom_tabs_ContentThread_h 1
 
 #include "mozilla/ipc/ProcessChild.h"
 #include "mozilla/ipc/ScopedXREEmbed.h"
 #include "ContentChild.h"
 
+#if defined(XP_WIN)
+#include "mozilla/mscom/MainThreadRuntime.h"
+#endif
+
 namespace mozilla {
 namespace dom {
 
 /**
  * ContentProcess is a singleton on the content process which represents
  * the main thread where tab instances live.
  */
 class ContentProcess : public mozilla::ipc::ProcessChild
@@ -33,16 +37,20 @@ public:
   virtual bool Init() override;
   virtual void CleanUp() override;
 
   void SetAppDir(const nsACString& aPath);
 
 private:
   ContentChild mContent;
   mozilla::ipc::ScopedXREEmbed mXREEmbed;
+#if defined(XP_WIN)
+  // This object initializes and configures COM.
+  mozilla::mscom::MainThreadRuntime mCOMRuntime;
+#endif
 
   DISALLOW_EVIL_CONSTRUCTORS(ContentProcess);
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif  // ifndef dom_tabs_ContentThread_h
--- a/dom/media/AccurateSeekTask.h
+++ b/dom/media/AccurateSeekTask.h
@@ -3,17 +3,16 @@
 /* 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/. */
 
 #ifndef ACCURATE_SEEK_TASK_H
 #define ACCURATE_SEEK_TASK_H
 
 #include "SeekTask.h"
-#include "MediaCallbackID.h"
 #include "MediaDecoderReader.h"
 #include "SeekJob.h"
 
 namespace mozilla {
 
 class AccurateSeekTask final : public SeekTask {
 public:
   AccurateSeekTask(const void* aDecoderID,
deleted file mode 100644
--- a/dom/media/MediaCallbackID.cpp
+++ /dev/null
@@ -1,50 +0,0 @@
-#include "MediaCallbackID.h"
-
-namespace mozilla {
-
-char const* CallbackID::INVALID_TAG = "INVALID_TAG";
-int32_t const CallbackID::INVALID_ID = -1;
-
-CallbackID::CallbackID()
-  : mTag(INVALID_TAG), mID(INVALID_ID)
-{
-}
-
-CallbackID::CallbackID(char const* aTag, int32_t aID /* = 0*/)
-  : mTag(aTag), mID(aID)
-{
-}
-
-CallbackID&
-CallbackID::operator++()
-{
-  ++mID;
-  return *this;
-}
-
-CallbackID
-CallbackID::operator++(int)
-{
-  CallbackID ret = *this;
-  ++(*this); // call prefix++
-  return ret;
-}
-
-bool
-CallbackID::operator==(const CallbackID& rhs) const
-{
-  return (strcmp(mTag, rhs.mTag) == 0) && (mID == rhs.mID);
-}
-
-bool
-CallbackID::operator!=(const CallbackID& rhs) const
-{
-  return !(*this == rhs);
-}
-
-CallbackID::operator int() const
-{
-  return mID;
-}
-
-} // namespace mozilla
deleted file mode 100644
--- a/dom/media/MediaCallbackID.h
+++ /dev/null
@@ -1,38 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim: set ts=8 sts=2 et sw=2 tw=80: */
-/* 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/. */
-
-#ifndef MediaCallbackID_h_
-#define MediaCallbackID_h_
-
-namespace mozilla {
-
-struct CallbackID
-{
-  static char const* INVALID_TAG;
-  static int32_t const INVALID_ID;
-
-  CallbackID();
-
-  explicit CallbackID(char const* aTag, int32_t aID = 0);
-
-  CallbackID& operator++();   // prefix++
-
-  CallbackID operator++(int); // postfix++
-
-  bool operator==(const CallbackID& rhs) const;
-
-  bool operator!=(const CallbackID& rhs) const;
-
-  operator int() const;
-
-private:
-  char const* mTag;
-  int32_t mID;
-};
-
-} // namespace mozilla
-
-#endif // MediaCallbackID_h_
--- a/dom/media/MediaDecoderStateMachine.cpp
+++ b/dom/media/MediaDecoderStateMachine.cpp
@@ -201,25 +201,21 @@ static void InitVideoQueuePrefs() {
       "media.video-queue.hw-accel-size", HW_VIDEO_QUEUE_SIZE);
     sVideoQueueSendToCompositorSize = Preferences::GetUint(
       "media.video-queue.send-to-compositor-size", VIDEO_QUEUE_SEND_TO_COMPOSITOR_SIZE);
   }
 }
 
 // Delay, in milliseconds, that tabs needs to be in background before video
 // decoding is suspended.
-static TimeDuration sSuspendBackgroundVideoDelay;
-
-static void
-InitSuspendBackgroundPref()
+static TimeDuration
+SuspendBackgroundVideoDelay()
 {
-  MOZ_ASSERT(NS_IsMainThread(), "Must be on main thread.");
-
-  sSuspendBackgroundVideoDelay = TimeDuration::FromMilliseconds(
-      MediaPrefs::MDSMSuspendBackgroundVideoDelay());
+  return TimeDuration::FromMilliseconds(
+    MediaPrefs::MDSMSuspendBackgroundVideoDelay());
 }
 
 #define INIT_WATCHABLE(name, val) \
   name(val, "MediaDecoderStateMachine::" #name)
 #define INIT_MIRROR(name, val) \
   name(mTaskQueue, val, "MediaDecoderStateMachine::" #name " (Mirror)")
 #define INIT_CANONICAL(name, val) \
   name(mTaskQueue, val, "MediaDecoderStateMachine::" #name " (Canonical)")
@@ -289,17 +285,16 @@ MediaDecoderStateMachine::MediaDecoderSt
   INIT_CANONICAL(mCurrentPosition, 0),
   INIT_CANONICAL(mPlaybackOffset, 0),
   INIT_CANONICAL(mIsAudioDataAudible, false)
 {
   MOZ_COUNT_CTOR(MediaDecoderStateMachine);
   NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
 
   InitVideoQueuePrefs();
-  InitSuspendBackgroundPref();
 
   mBufferingWait = IsRealTime() ? 0 : 15;
   mLowDataThresholdUsecs = IsRealTime() ? 0 : detail::LOW_DATA_THRESHOLD_USECS;
 
 #ifdef XP_WIN
   // Ensure high precision timers are enabled on Windows, otherwise the state
   // machine isn't woken up at reliable intervals to set the next frame,
   // and we drop frames while painting. Note that multiple calls to this
@@ -758,17 +753,16 @@ MediaDecoderStateMachine::MaybeFinishDec
     return false;
   }
   FinishDecodeFirstFrame();
   if (!mQueuedSeek.Exists()) {
     return false;
   }
 
   // We can now complete the pending seek.
-  SetState(DECODER_STATE_SEEKING);
   InitiateSeek(Move(mQueuedSeek));
   return true;
 }
 
 void
 MediaDecoderStateMachine::OnVideoDecoded(MediaData* aVideoSample,
                                          TimeStamp aDecodeStartTime)
 {
@@ -1266,17 +1260,16 @@ void MediaDecoderStateMachine::StartDeco
       // the first samples in order to determine the media start time,
       // we have the start time from last time we loaded.
       // FinishDecodeFirstFrame will be launched upon completion of the seek when
       // we have data ready to play.
       MOZ_ASSERT(mQueuedSeek.Exists() && mSentFirstFrameLoadedEvent,
                  "Return from dormant must have queued seek");
     }
     if (mQueuedSeek.Exists()) {
-      SetState(DECODER_STATE_SEEKING);
       InitiateSeek(Move(mQueuedSeek));
       return;
     }
   }
 
   mDecodeStartTime = TimeStamp::Now();
 
   CheckIfDecodeComplete();
@@ -1348,17 +1341,17 @@ void MediaDecoderStateMachine::Visibilit
 
   // If not playing then there's nothing to do.
   if (mPlayState != MediaDecoder::PLAY_STATE_PLAYING) {
     return;
   }
 
   // Start timer to trigger suspended decoding state when going invisible.
   if (!mIsVisible) {
-    TimeStamp target = TimeStamp::Now() + sSuspendBackgroundVideoDelay;
+    TimeStamp target = TimeStamp::Now() + SuspendBackgroundVideoDelay();
 
     RefPtr<MediaDecoderStateMachine> self = this;
     mVideoDecodeSuspendTimer.Ensure(target,
                                     [=]() { self->OnSuspendTimerResolved(); },
                                     [=]() { self->OnSuspendTimerRejected(); });
     return;
   }
 
@@ -1519,22 +1512,20 @@ MediaDecoderStateMachine::Seek(SeekTarge
     DECODER_LOG("Seek() Not Enough Data to continue at this stage, queuing seek");
     mQueuedSeek.RejectIfExists(__func__);
     mQueuedSeek.mTarget = aTarget;
     return mQueuedSeek.mPromise.Ensure(__func__);
   }
   mQueuedSeek.RejectIfExists(__func__);
 
   DECODER_LOG("Changed state to SEEKING (to %lld)", aTarget.GetTime().ToMicroseconds());
-  SetState(DECODER_STATE_SEEKING);
 
   SeekJob seekJob;
   seekJob.mTarget = aTarget;
-  InitiateSeek(Move(seekJob));
-  return mCurrentSeek.mPromise.Ensure(__func__);
+  return InitiateSeek(Move(seekJob));
 }
 
 RefPtr<MediaDecoder::SeekPromise>
 MediaDecoderStateMachine::InvokeSeek(SeekTarget aTarget)
 {
   return InvokeAsync(OwnerThread(), this, __func__,
                      &MediaDecoderStateMachine::Seek, aTarget);
 }
@@ -1604,65 +1595,69 @@ MediaDecoderStateMachine::DispatchDecode
   if (needIdle) {
     DECODER_LOG("Dispatching SetIdle() audioQueue=%lld videoQueue=%lld",
                 GetDecodedAudioDuration(),
                 VideoQueue().Duration());
     mReader->SetIdle();
   }
 }
 
-void
+RefPtr<MediaDecoder::SeekPromise>
 MediaDecoderStateMachine::InitiateSeek(SeekJob aSeekJob)
 {
   MOZ_ASSERT(OnTaskQueue());
 
+  SetState(DECODER_STATE_SEEKING);
+
   // Discard the existing seek task.
   DiscardSeekTaskIfExist();
 
   mSeekTaskRequest.DisconnectIfExists();
 
   // SeekTask will register its callbacks to MediaDecoderReaderWrapper.
   CancelMediaDecoderReaderWrapperCallback();
 
   // Create a new SeekTask instance for the incoming seek task.
-  MOZ_ASSERT(!mCurrentSeek.Exists());
-  mCurrentSeek = Move(aSeekJob);
-  if (mCurrentSeek.mTarget.IsAccurate() || mCurrentSeek.mTarget.IsFast()) {
+  if (aSeekJob.mTarget.IsAccurate() || aSeekJob.mTarget.IsFast()) {
     mSeekTask = new AccurateSeekTask(mDecoderID, OwnerThread(),
-                                     mReader.get(), mCurrentSeek.mTarget,
+                                     mReader.get(), aSeekJob.mTarget,
                                      mInfo, Duration(), GetMediaTime());
-  } else if (mCurrentSeek.mTarget.IsNextFrame()) {
+  } else if (aSeekJob.mTarget.IsNextFrame()) {
     mSeekTask = new NextFrameSeekTask(mDecoderID, OwnerThread(), mReader.get(),
-                                      mCurrentSeek.mTarget, mInfo, Duration(),
+                                      aSeekJob.mTarget, mInfo, Duration(),
                                       GetMediaTime(), AudioQueue(), VideoQueue());
   } else {
     MOZ_DIAGNOSTIC_ASSERT(false, "Cannot handle this seek task.");
   }
 
   // Stop playback now to ensure that while we're outside the monitor
   // dispatching SeekingStarted, playback doesn't advance and mess with
   // mCurrentPosition that we've setting to seekTime here.
   StopPlayback();
 
-  // mCurrentSeek.mTarget.mTime might be different from
+  // aSeekJob.mTarget.mTime might be different from
   // mSeekTask->GetSeekTarget().mTime because the seek task might clamp the seek
   // target to [0, duration]. We want to update the playback position to the
   // clamped value.
   UpdatePlaybackPositionInternal(mSeekTask->GetSeekTarget().GetTime().ToMicroseconds());
 
-  mOnSeekingStart.Notify(mCurrentSeek.mTarget.mEventVisibility);
+  mOnSeekingStart.Notify(aSeekJob.mTarget.mEventVisibility);
 
   // Reset our state machine and decoding pipeline before seeking.
   if (mSeekTask->NeedToResetMDSM()) { Reset(); }
 
   // Do the seek.
   mSeekTaskRequest.Begin(mSeekTask->Seek(Duration())
     ->Then(OwnerThread(), __func__, this,
            &MediaDecoderStateMachine::OnSeekTaskResolved,
            &MediaDecoderStateMachine::OnSeekTaskRejected));
+
+  MOZ_ASSERT(!mCurrentSeek.Exists());
+  mCurrentSeek = Move(aSeekJob);
+  return mCurrentSeek.mPromise.Ensure(__func__);
 }
 
 void
 MediaDecoderStateMachine::OnSeekTaskResolved(SeekTaskResolveValue aValue)
 {
   MOZ_ASSERT(OnTaskQueue());
   MOZ_ASSERT(mState == DECODER_STATE_SEEKING);
 
--- a/dom/media/MediaDecoderStateMachine.h
+++ b/dom/media/MediaDecoderStateMachine.h
@@ -83,17 +83,16 @@ hardware (via AudioStream).
 #define MediaDecoderStateMachine_h__
 
 #include "mozilla/Attributes.h"
 #include "mozilla/ReentrantMonitor.h"
 #include "mozilla/StateMirroring.h"
 
 #include "nsAutoPtr.h"
 #include "nsThreadUtils.h"
-#include "MediaCallbackID.h"
 #include "MediaDecoder.h"
 #include "MediaDecoderReader.h"
 #include "MediaDecoderOwner.h"
 #include "MediaEventSource.h"
 #include "MediaMetadataManager.h"
 #include "MediaStatistics.h"
 #include "MediaTimer.h"
 #include "ImageContainer.h"
@@ -504,18 +503,17 @@ protected:
   // Dispatches a LoadedMetadataEvent.
   // This is threadsafe and can be called on any thread.
   // The decoder monitor must be held.
   void EnqueueLoadedMetadataEvent();
 
   void EnqueueFirstFrameLoadedEvent();
 
   // Clears any previous seeking state and initiates a new seek on the decoder.
-  // The decoder monitor must be held.
-  void InitiateSeek(SeekJob aSeekJob);
+  RefPtr<MediaDecoder::SeekPromise> InitiateSeek(SeekJob aSeekJob);
 
   // Clears any previous seeking state and initiates a seek on the decoder to
   // resync the video and audio positions, when recovering from video decoding
   // being suspended in background or from audio and video decoding being
   // suspended due to the decoder limit.
   void InitiateDecodeRecoverySeek(TrackSet aTracks);
 
   nsresult DispatchAudioDecodeTaskIfNeeded();
--- a/dom/media/MediaPrefs.cpp
+++ b/dom/media/MediaPrefs.cpp
@@ -64,9 +64,16 @@ void MediaPrefs::PrefAddVarCache(uint32_
 
 void MediaPrefs::PrefAddVarCache(float* aVariable,
                                  const char* aPref,
                                  float aDefault)
 {
   Preferences::AddFloatVarCache(aVariable, aPref, aDefault);
 }
 
+void MediaPrefs::PrefAddVarCache(AtomicUint32* aVariable,
+                                 const char* aPref,
+                                 uint32_t aDefault)
+{
+  Preferences::AddAtomicUintVarCache(aVariable, aPref, aDefault);
+}
+
 } // namespace mozilla
--- a/dom/media/MediaPrefs.h
+++ b/dom/media/MediaPrefs.h
@@ -5,16 +5,18 @@
 
 #ifndef MEDIA_PREFS_H
 #define MEDIA_PREFS_H
 
 #ifdef MOZ_WIDGET_ANDROID
 #include "AndroidBridge.h"
 #endif
 
+#include "mozilla/Atomics.h"
+
 // First time MediaPrefs::GetSingleton() needs to be called on the main thread,
 // before any of the methods accessing the values are used, but after
 // the Preferences system has been initialized.
 
 // The static methods to access the preference value are safe to call
 // from any thread after that first call.
 
 // To register a preference, you need to add a line in this file using
@@ -28,34 +30,48 @@
 // "media.resampling.enabled".  If the value is not set, the default would be
 // false.
 
 #define DECL_MEDIA_PREF(Pref, Name, Type, Default)                            \
 public:                                                                       \
 static const Type& Name() { MOZ_ASSERT(SingletonExists()); return GetSingleton().mPref##Name.mValue; } \
 private:                                                                      \
 static const char* Get##Name##PrefName() { return Pref; }                     \
-static Type Get##Name##PrefDefault() { return Default; }                      \
+static StripAtomic<Type> Get##Name##PrefDefault() { return Default; }         \
 PrefTemplate<Type, Get##Name##PrefDefault, Get##Name##PrefName> mPref##Name
 
 // Custom Definitions.
 #define GMP_DEFAULT_ASYNC_SHUTDOWN_TIMEOUT 3000
 #define SUSPEND_BACKGROUND_VIDEO_DELAY_MS 10000
 #define TEST_PREFERENCE_FAKE_RECOGNITION_SERVICE "media.webspeech.test.fake_recognition_service"
 
 namespace mozilla {
 
 template<class T> class StaticAutoPtr;
 
 class MediaPrefs final
 {
+  typedef Atomic<uint32_t, Relaxed> AtomicUint32;
+
+  template <typename T>
+  struct StripAtomicImpl {
+    typedef T Type;
+  };
+
+  template <typename T, MemoryOrdering Order>
+  struct StripAtomicImpl<Atomic<T, Order>> {
+    typedef T Type;
+  };
+
+  template <typename T>
+  using StripAtomic = typename StripAtomicImpl<T>::Type;
 
 private:
   // Since we cannot use const char*, use a function that returns it.
-  template <class T, T Default(), const char* Pref()>
+  template <class T, StripAtomic<T> Default(), const char* Pref()>
   class PrefTemplate
   {
   public:
     PrefTemplate()
     : mValue(Default())
     {
       Register(Pref());
     }
@@ -109,17 +125,17 @@ private:
   DECL_MEDIA_PREF("media.decoder.fuzzing.video-output-minimum-interval-ms", PDMFuzzingInterval, uint32_t, 0);
   DECL_MEDIA_PREF("media.decoder.fuzzing.dont-delay-inputexhausted", PDMFuzzingDelayInputExhausted, bool, true);
   DECL_MEDIA_PREF("media.gmp.decoder.enabled",                PDMGMPEnabled, bool, true);
   DECL_MEDIA_PREF("media.gmp.decoder.aac",                    GMPAACPreferred, uint32_t, 0);
   DECL_MEDIA_PREF("media.gmp.decoder.h264",                   GMPH264Preferred, uint32_t, 0);
 
   // MediaDecoderStateMachine
   DECL_MEDIA_PREF("media.suspend-bkgnd-video.enabled",        MDSMSuspendBackgroundVideoEnabled, bool, false);
-  DECL_MEDIA_PREF("media.suspend-bkgnd-video.delay-ms",       MDSMSuspendBackgroundVideoDelay, uint32_t, SUSPEND_BACKGROUND_VIDEO_DELAY_MS);
+  DECL_MEDIA_PREF("media.suspend-bkgnd-video.delay-ms",       MDSMSuspendBackgroundVideoDelay, AtomicUint32, SUSPEND_BACKGROUND_VIDEO_DELAY_MS);
 
   // WebSpeech
   DECL_MEDIA_PREF("media.webspeech.synth.force_global_queue", WebSpeechForceGlobal, bool, false);
   DECL_MEDIA_PREF("media.webspeech.test.enable",              WebSpeechTestEnabled, bool, false);
   DECL_MEDIA_PREF("media.webspeech.test.fake_fsm_events",     WebSpeechFakeFSMEvents, bool, false);
   DECL_MEDIA_PREF(TEST_PREFERENCE_FAKE_RECOGNITION_SERVICE,   WebSpeechFakeRecognitionService, bool, false);
   DECL_MEDIA_PREF("media.webspeech.recognition.enable",       WebSpeechRecognitionEnabled, bool, false);
   DECL_MEDIA_PREF("media.webspeech.recognition.force_enable", WebSpeechRecognitionForceEnabled, bool, false);
@@ -151,16 +167,17 @@ private:
     return -1;
   }
 
   // Creating these to avoid having to include Preferences.h in the .h
   static void PrefAddVarCache(bool*, const char*, bool);
   static void PrefAddVarCache(int32_t*, const char*, int32_t);
   static void PrefAddVarCache(uint32_t*, const char*, uint32_t);
   static void PrefAddVarCache(float*, const char*, float);
+  static void PrefAddVarCache(AtomicUint32*, const char*, uint32_t);
 
   static void AssertMainThread();
 
   MediaPrefs();
   MediaPrefs(const MediaPrefs&) = delete;
   MediaPrefs& operator=(const MediaPrefs&) = delete;
 };
 
--- a/dom/media/SeekTask.h
+++ b/dom/media/SeekTask.h
@@ -3,17 +3,16 @@
 /* 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/. */
 
 #ifndef SEEK_TASK_H
 #define SEEK_TASK_H
 
 #include "mozilla/MozPromise.h"
-#include "MediaCallbackID.h"
 #include "SeekTarget.h"
 
 namespace mozilla {
 
 class AbstractThread;
 class MediaData;
 class MediaDecoderReaderWrapper;
 
--- a/dom/media/gmp/GMPProcessParent.cpp
+++ b/dom/media/gmp/GMPProcessParent.cpp
@@ -58,17 +58,30 @@ GMPProcessParent::Launch(int32_t aTimeou
   // The sandbox doesn't allow file system rules where the paths contain
   // symbolic links or junction points. Sometimes the Users folder has been
   // moved to another drive using a junction point, so allow for this specific
   // case. See bug 1236680 for details.
   if (!widget::WinUtils::ResolveMovedUsersFolder(wGMPPath)) {
     NS_WARNING("ResolveMovedUsersFolder failed for GMP path.");
     return false;
   }
-  mAllowedFilesRead.push_back(wGMPPath + L"\\*");
+
+  // If the GMP path is a network path that is not mapped to a drive letter,
+  // then we need to fix the path format for the sandbox rule.
+  wchar_t volPath[MAX_PATH];
+  if (::GetVolumePathNameW(wGMPPath.c_str(), volPath, MAX_PATH) &&
+      ::GetDriveTypeW(volPath) == DRIVE_REMOTE &&
+      wGMPPath.compare(0, 2, L"\\\\") == 0) {
+    std::wstring sandboxGMPPath(wGMPPath);
+    sandboxGMPPath.insert(1, L"??\\UNC");
+    mAllowedFilesRead.push_back(sandboxGMPPath + L"\\*");
+  } else {
+    mAllowedFilesRead.push_back(wGMPPath + L"\\*");
+  }
+
   args.push_back(WideToUTF8(wGMPPath));
 #else
   args.push_back(mGMPPath);
 #endif
 
   args.push_back(string(voucherPath.BeginReading(), voucherPath.EndReading()));
 
   return SyncLaunch(args, aTimeoutMs, base::GetCurrentProcessArchitecture());
--- a/dom/media/moz.build
+++ b/dom/media/moz.build
@@ -103,17 +103,16 @@ EXPORTS += [
     'DecoderTraits.h',
     'DOMMediaStream.h',
     'EncodedBufferCache.h',
     'FileBlockCache.h',
     'FrameStatistics.h',
     'Intervals.h',
     'Latency.h',
     'MediaCache.h',
-    'MediaCallbackID.h',
     'MediaData.h',
     'MediaDataDemuxer.h',
     'MediaDecoder.h',
     'MediaDecoderOwner.h',
     'MediaDecoderReader.h',
     'MediaDecoderStateMachine.h',
     'MediaEventSource.h',
     'MediaFormatReader.h',
@@ -217,17 +216,16 @@ UNIFIED_SOURCES += [
     'DecoderDoctorDiagnostics.cpp',
     'DOMMediaStream.cpp',
     'EncodedBufferCache.cpp',
     'FileBlockCache.cpp',
     'GetUserMediaRequest.cpp',
     'GraphDriver.cpp',
     'Latency.cpp',
     'MediaCache.cpp',
-    'MediaCallbackID.cpp',
     'MediaData.cpp',
     'MediaDecoder.cpp',
     'MediaDecoderReader.cpp',
     'MediaDecoderReaderWrapper.cpp',
     'MediaDecoderStateMachine.cpp',
     'MediaDeviceInfo.cpp',
     'MediaDevices.cpp',
     'MediaFormatReader.cpp',
--- a/dom/plugins/ipc/PluginProcessChild.cpp
+++ b/dom/plugins/ipc/PluginProcessChild.cpp
@@ -16,17 +16,16 @@
 #if defined(XP_MACOSX)
 #include "nsCocoaFeatures.h"
 // An undocumented CoreGraphics framework method, present in the same form
 // since at least OS X 10.5.
 extern "C" CGError CGSSetDebugOptions(int options);
 #endif
 
 #ifdef XP_WIN
-#include <objbase.h>
 bool ShouldProtectPluginCurrentDirectory(char16ptr_t pluginFilePath);
 #if defined(MOZ_SANDBOX)
 #define TARGET_SANDBOX_EXPORTS
 #include "mozilla/sandboxTarget.h"
 #endif
 #endif
 
 using mozilla::ipc::IOThreadChild;
@@ -76,22 +75,16 @@ PluginProcessChild::Init()
             }
             // Values passed to PR_SetEnv() must be seperately allocated.
             char* setInterposePtr = strdup(setInterpose.get());
             PR_SetEnv(setInterposePtr);
         }
     }
 #endif
 
-#ifdef XP_WIN
-    // Drag-and-drop needs OleInitialize to be called, and Silverlight depends
-    // on the host calling CoInitialize (which is called by OleInitialize).
-    ::OleInitialize(nullptr);
-#endif
-
     // Certain plugins, such as flash, steal the unhandled exception filter
     // thus we never get crash reports when they fault. This call fixes it.
     message_loop()->set_exception_restoration(true);
 
     std::string pluginFilename;
 
 #if defined(OS_POSIX)
     // NB: need to be very careful in ensuring that the first arg
@@ -144,16 +137,13 @@ PluginProcessChild::Init()
     }
 #endif
     return retval;
 }
 
 void
 PluginProcessChild::CleanUp()
 {
-#ifdef XP_WIN
-    ::OleUninitialize();
-#endif
     nsRegion::ShutdownStatic();
 }
 
 } // namespace plugins
 } // namespace mozilla
--- a/dom/plugins/ipc/PluginProcessChild.h
+++ b/dom/plugins/ipc/PluginProcessChild.h
@@ -5,16 +5,20 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef dom_plugins_PluginProcessChild_h
 #define dom_plugins_PluginProcessChild_h 1
 
 #include "mozilla/ipc/ProcessChild.h"
 #include "mozilla/plugins/PluginModuleChild.h"
 
+#if defined(XP_WIN)
+#include "mozilla/mscom/MainThreadRuntime.h"
+#endif
+
 namespace mozilla {
 namespace plugins {
 //-----------------------------------------------------------------------------
 
 class PluginProcessChild : public mozilla::ipc::ProcessChild {
 protected:
     typedef mozilla::ipc::ProcessChild ProcessChild;
 
@@ -30,16 +34,21 @@ public:
     virtual void CleanUp() override;
 
 protected:
     static PluginProcessChild* current() {
         return static_cast<PluginProcessChild*>(ProcessChild::current());
     }
 
 private:
+#if defined(XP_WIN)
+    /* Drag-and-drop and Silverlight depend on the host initializing COM.
+     * This object initializes and configures COM. */
+    mozilla::mscom::MainThreadRuntime mCOMRuntime;
+#endif
     PluginModuleChild mPlugin;
 
     DISALLOW_EVIL_CONSTRUCTORS(PluginProcessChild);
 };
 
 } // namespace plugins
 } // namespace mozilla
 
--- a/dom/tests/mochitest/webcomponents/test_custom_element_clone_callbacks_extended.html
+++ b/dom/tests/mochitest/webcomponents/test_custom_element_clone_callbacks_extended.html
@@ -9,48 +9,32 @@ https://bugzilla.mozilla.org/show_bug.cg
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 </head>
 <body>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1081039">Bug 1081039</a>
 <script>
 
 SimpleTest.waitForExplicitFinish();
 
-// Test to make sure created callback is called on clones that are upgraded and clones
-// created after registering the custom element.
-
-var callbackCalledOnUpgrade = false;
-var callbackCalledOnClone = false;
+// Test to make sure created callback is called on clones created after
+// registering the custom element.
 
-var foo = document.createElement("button", "x-foo");
-is(foo.getAttribute("is"), "x-foo");
-
-var fooClone = foo.cloneNode(true);
-
+var count = 0;
 var p = Object.create(HTMLButtonElement.prototype);
-p.createdCallback = function() {
+p.createdCallback = function() { // should be called by createElement and cloneNode
   is(this.__proto__, p, "Correct prototype should be set on custom elements.");
 
-  if (this == fooClone) {
-    // Callback called for the element created before registering the custom element.
-    // Should be called on element upgrade.
-    is(callbackCalledOnUpgrade, false, "Upgrade should only be called once per clone.");
-    callbackCalledOnUpgrade = true;
-  } else if (this != foo) {
-    // Callback called for the element created after registering the custom element.
-    is(callbackCalledOnClone, false, "Upgrade should only be called once per clone.");
-    callbackCalledOnClone = true;
-  }
-
-  if (callbackCalledOnUpgrade && callbackCalledOnClone) {
+  if (++count == 2) {
     SimpleTest.finish();
   }
 };
 
 document.registerElement("x-foo", { prototype: p, extends: "button" });
+var foo = document.createElement("button", {is: "x-foo"});
+is(foo.getAttribute("is"), "x-foo");
 
-var anotherFooClone = foo.cloneNode(true);
+var fooClone = foo.cloneNode(true);
 
 SimpleTest.waitForExplicitFinish();
 
 </script>
 </body>
 </html>
--- a/dom/tests/mochitest/webcomponents/test_document_register.html
+++ b/dom/tests/mochitest/webcomponents/test_document_register.html
@@ -111,60 +111,66 @@ function startTest() {
   testRegisterSimple("X-SVG-DUPE-ME", Object.create(HTMLElement.prototype), true);
   testRegisterSimple("x-svg-dupe-me", null, true);
   testRegisterExtend("x-svg-dupe-me", "span", Object.create(HTMLElement.prototype), true);
   testRegisterExtend("x-svg-dupe-me", "shape", Object.create(SVGElement.prototype), true);
 
   // document.createElement with extended type.
   var extendedProto = Object.create(HTMLButtonElement.prototype);
   var buttonConstructor = document.registerElement("x-extended-button", { prototype: extendedProto, extends: "button" });
-  var extendedButton = document.createElement("button", "x-extended-button");
+  var extendedButton = document.createElement("button", {is: "x-extended-button"});
   is(extendedButton.tagName, "BUTTON", "Created element should have local name of BUTTON");
   is(extendedButton.__proto__, extendedProto, "Created element should have the prototype of the extended type.");
   is(extendedButton.getAttribute("is"), "x-extended-button", "The |is| attribute of the created element should be the extended type.");
 
   // document.createElementNS with different namespace than definition.
-  var svgButton = document.createElementNS("http://www.w3.org/2000/svg", "button", "x-extended-button");
-  isnot(svgButton.__proto__, extendedProto, "Definition for element is in html namespace, registration should not apply for SVG elements.");
+  try {
+    var svgButton = document.createElementNS("http://www.w3.org/2000/svg", "button", {is: "x-extended-button"});
+    ok(false, "An exception should've been thrown");
+  } catch(err) {
+    is(err.name, "NotFoundError", "A NotFoundError exception should've been thrown");
+  }
 
   // document.createElementNS with no namespace.
-  var noNamespaceButton = document.createElementNS("", "button", "x-extended-button");
-  isnot(noNamespaceButton.__proto__, extendedProto, "Definition for element is in html namespace, registration should not apply for elements with no namespace.");
+  try {
+    var noNamespaceButton = document.createElementNS("", "button", {is: "x-extended-button"});
+    ok(false, "An exception should've been thrown");
+  } catch(err) {
+    is(err.name, "NotFoundError", "A NotFoundError exception should've been thrown");
+  }
 
   // document.createElement with non-existant extended type.
-  var normalButton = document.createElement("button", "x-non-existant");
-  is(normalButton.__proto__, HTMLButtonElement.prototype, "When the extended type doesn't exist, prototype should not change.");
+  try {
+    var normalButton = document.createElement("button", {is: "x-non-existant"});
+    ok(false, "An exception should've been thrown");
+  } catch(err) {
+    is(err.name, "NotFoundError", "A NotFoundError exception should've been thrown");
+  }
 
   // document.createElement with exteneded type that does not match with local name of element.
-  var normalDiv = document.createElement("div", "x-extended-button");
-  is(normalDiv.__proto__, HTMLDivElement.prototype, "Prototype should not change when local name of extended type defintion does not match.");
+  try {
+    var normalDiv = document.createElement("div", {is: "x-extended-button"});
+    ok(false, "An exception should've been thrown");
+  } catch(err) {
+    is(err.name, "NotFoundError", "A NotFoundError exception should've been thrown");
+  }
 
   // Custom element constructor.
   var constructedButton = new buttonConstructor();
   is(constructedButton.tagName, "BUTTON", "Created element should have local name of BUTTON");
   is(constructedButton.__proto__, extendedProto, "Created element should have the prototype of the extended type.");
   is(constructedButton.getAttribute("is"), "x-extended-button", "The |is| attribute of the created element should be the extended type.");
 
-  // document.createElementNS with extended type.
-  var svgExtendedProto = Object.create(SVGTextElement.prototype);
-  var svgConstructor = document.registerElement("x-extended-text", { prototype: svgExtendedProto, extends: "text"});
-  var extendedText = document.createElementNS("http://www.w3.org/2000/svg", "text", "x-extended-text");
-  is(extendedText.tagName, "text", "Created element should have a local name of |text|.");
-  is(extendedText.__proto__, svgExtendedProto, "Created element have the registered prototype.");
-  is(extendedText.getAttribute("is"), "x-extended-text", "The |is| attribute of the created element should be the extended type.");
-
   // document.createElement with different namespace than definition for extended element.
-  var htmlText = document.createElement("text", "x-extended-text");
-  isnot(htmlText.__proto__, svgExtendedProto, "Definition for element in SVG namespace should not apply to HTML elements.");
-
-  // Custom element constructor for a SVG element.
-  var constructedText = new svgConstructor();
-  is(constructedText.tagName, "text", "Created element should have a local name of |text|.");
-  is(constructedText.__proto__, svgExtendedProto, "Created element have the registered prototype.");
-  is(constructedText.getAttribute("is"), "x-extended-text", "The |is| attribute of the created element should be the extended type.");
+  try {
+    var htmlText = document.createElement("text", {is: "x-extended-text"});
+    ok(false, "An exception should've been thrown");
+  } catch(err) {
+    is(err.name, "NotFoundError", "A NotFoundError exception should've been thrown");
+  }
 
   // Try creating an element with a custom element name, but not in the html namespace.
   var htmlNamespaceProto = Object.create(HTMLElement.prototype);
   document.registerElement("x-in-html-namespace", { prototype: htmlNamespaceProto });
   var wrongNamespaceElem = document.createElementNS("http://www.w3.org/2000/svg", "x-in-html-namespace");
   isnot(wrongNamespaceElem.__proto__, htmlNamespaceProto, "Definition for element in html namespace should not apply to SVG elements.");
 }
 
--- a/dom/tests/mochitest/webcomponents/test_document_register_lifecycle.html
+++ b/dom/tests/mochitest/webcomponents/test_document_register_lifecycle.html
@@ -295,17 +295,17 @@ function testAttributeChangedExtended() 
   p.attributeChangedCallback = function(name, oldValue, newValue) {
     is(callbackCalled, false, "Callback should only be called once in this test.");
     callbackCalled = true;
     runNextTest();
   };
 
   document.registerElement("x-extended-attribute-change", { prototype: p, extends: "button" });
 
-  var elem = document.createElement("button", "x-extended-attribute-change");
+  var elem = document.createElement("button", {is: "x-extended-attribute-change"});
   elem.setAttribute("foo", "bar");
 }
 
 // Creates a custom element that is an upgrade candidate (no registration)
 // and mutate the element in ways that would call callbacks for registered
 // elements.
 function testUpgradeCandidate() {
   var createdElement = document.createElement("x-upgrade-candidate");
--- a/dom/tv/TVTuner.cpp
+++ b/dom/tv/TVTuner.cpp
@@ -230,17 +230,18 @@ TVTuner::CreateSimulatedMediaStream()
   }
 
   nsIDocument* doc = domWin->GetExtantDoc();
   if (NS_WARN_IF(!doc)) {
     return nullptr;
   }
 
   ErrorResult error;
-  RefPtr<Element> element = doc->CreateElement(VIDEO_TAG, error);
+  ElementCreationOptions options;
+  RefPtr<Element> element = doc->CreateElement(VIDEO_TAG, options, error);
   if (NS_WARN_IF(error.Failed())) {
     return nullptr;
   }
 
   nsCOMPtr<nsIContent> content(do_QueryInterface(element));
   if (NS_WARN_IF(!content)) {
     return nullptr;
   }
--- a/dom/webidl/Document.webidl
+++ b/dom/webidl/Document.webidl
@@ -9,16 +9,21 @@
 interface WindowProxy;
 interface nsISupports;
 interface URI;
 interface nsIDocShell;
 interface nsILoadGroup;
 
 enum VisibilityState { "hidden", "visible", "prerender" };
 
+/* https://dom.spec.whatwg.org/#dictdef-elementcreationoptions */
+dictionary ElementCreationOptions {
+  DOMString is;
+};
+
 /* http://dom.spec.whatwg.org/#interface-document */
 [Constructor]
 interface Document : Node {
   [Throws]
   readonly attribute DOMImplementation implementation;
   [Pure]
   readonly attribute DOMString URL;
   [Pure]
@@ -43,19 +48,19 @@ interface Document : Node {
   [Pure, Throws]
   HTMLCollection getElementsByTagNameNS(DOMString? namespace, DOMString localName);
   [Pure]
   HTMLCollection getElementsByClassName(DOMString classNames);
   [Pure]
   Element? getElementById(DOMString elementId);
 
   [NewObject, Throws]
-  Element createElement(DOMString localName);
+  Element createElement(DOMString localName, optional ElementCreationOptions options);
   [NewObject, Throws]
-  Element createElementNS(DOMString? namespace, DOMString qualifiedName);
+  Element createElementNS(DOMString? namespace, DOMString qualifiedName, optional ElementCreationOptions options);
   [NewObject]
   DocumentFragment createDocumentFragment();
   [NewObject]
   Text createTextNode(DOMString data);
   [NewObject]
   Comment createComment(DOMString data);
   [NewObject, Throws]
   ProcessingInstruction createProcessingInstruction(DOMString target, DOMString data);
@@ -255,24 +260,16 @@ partial interface Document {
 };
 
 //http://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/custom/index.html#dfn-document-register
 partial interface Document {
     [Throws, Func="nsDocument::IsWebComponentsEnabled"]
     object registerElement(DOMString name, optional ElementRegistrationOptions options);
 };
 
-//http://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/custom/index.html#dfn-document-register
-partial interface Document {
-    [NewObject, Throws]
-    Element createElement(DOMString localName, DOMString? typeExtension);
-    [NewObject, Throws]
-    Element createElementNS(DOMString? namespace, DOMString qualifiedName, DOMString? typeExtension);
-};
-
 // http://dvcs.w3.org/hg/webperf/raw-file/tip/specs/PageVisibility/Overview.html#sec-document-interface
 partial interface Document {
   readonly attribute boolean hidden;
   readonly attribute boolean mozHidden;
   readonly attribute VisibilityState visibilityState;
   readonly attribute VisibilityState mozVisibilityState;
 };
 
--- a/gfx/2d/Logging.h
+++ b/gfx/2d/Logging.h
@@ -496,19 +496,24 @@ private:
       }
       mMessage << "]: ";
     }
   }
 
   void WriteLog(const std::string &aString) {
     if (MOZ_UNLIKELY(LogIt())) {
       Logger::OutputMessage(aString, L, NoNewline());
+      // Assert if required.  We don't have a three parameter MOZ_ASSERT
+      // so use the underlying functions instead (see bug 1281702):
+#ifdef DEBUG
       if (mOptions & int(LogOptions::AssertOnCall)) {
-        MOZ_ASSERT(false, "An assert from the graphics logger");
+        MOZ_ReportAssertionFailure(aString.c_str(), __FILE__, __LINE__);
+        MOZ_CRASH("GFX: An assert from the graphics logger");
       }
+#endif
       if ((mOptions & int(LogOptions::CrashAction)) && ValidReason()) {
         Logger::CrashAction(mReason);
       }
     }
   }
 
   std::stringstream mMessage;
   int mOptions;
--- a/gfx/gl/GLContext.cpp
+++ b/gfx/gl/GLContext.cpp
@@ -6,16 +6,20 @@
 
 #include "GLContext.h"
 
 #include <algorithm>
 #include <stdio.h>
 #include <string.h>
 #include <ctype.h>
 #include <vector>
+#ifdef MOZ_WIDGET_ANDROID
+#include <fcntl.h>
+#include <sys/mman.h>
+#endif
 
 #include "GLBlitHelper.h"
 #include "GLReadTexImageHelper.h"
 #include "GLScreenBuffer.h"
 
 #include "gfxCrashReporterUtils.h"
 #include "gfxEnv.h"
 #include "gfxUtils.h"
@@ -27,29 +31,34 @@
 #include "prlink.h"
 #include "ScopedGLHelpers.h"
 #include "SharedSurfaceGL.h"
 #include "GfxTexturesReporter.h"
 #include "TextureGarbageBin.h"
 #include "gfx2DGlue.h"
 #include "gfxPrefs.h"
 #include "mozilla/IntegerPrintfMacros.h"
+#include "mozilla/gfx/Logging.h"
 
 #include "OGLShaderProgram.h" // for ShaderProgramType
 
 #include "mozilla/DebugOnly.h"
 
 #ifdef XP_MACOSX
 #include <CoreServices/CoreServices.h>
 #endif
 
 #if defined(MOZ_WIDGET_COCOA)
 #include "nsCocoaFeatures.h"
 #endif
 
+#ifdef MOZ_WIDGET_ANDROID
+#include "AndroidBridge.h"
+#endif
+
 namespace mozilla {
 namespace gl {
 
 using namespace mozilla::gfx;
 using namespace mozilla::layers;
 
 #ifdef MOZ_GL_DEBUG
 unsigned GLContext::sCurrentGLContextTLS = -1;
@@ -456,16 +465,17 @@ GLContext::GLContext(CreateContextFlags 
     mLockedSurface(nullptr),
     mMaxTextureSize(0),
     mMaxCubeMapTextureSize(0),
     mMaxTextureImageSize(0),
     mMaxRenderbufferSize(0),
     mMaxSamples(0),
     mNeedsTextureSizeChecks(false),
     mNeedsFlushBeforeDeleteFB(false),
+    mTextureAllocCrashesOnMapFailure(false),
     mWorkAroundDriverBugs(true),
     mHeavyGLCallsSinceLastFlush(false)
 {
     mMaxViewportDims[0] = 0;
     mMaxViewportDims[1] = 0;
     mOwningThreadId = PlatformThread::CurrentId();
 }
 
@@ -800,17 +810,19 @@ GLContext::InitWithPrefixImpl(const char
 
     // The order of these strings must match up with the order of the enum
     // defined in GLContext.h for renderer IDs.
     const char* rendererMatchStrings[size_t(GLRenderer::Other)] = {
         "Adreno 200",
         "Adreno 205",
         "Adreno (TM) 200",
         "Adreno (TM) 205",
+        "Adreno (TM) 305",
         "Adreno (TM) 320",
+        "Adreno (TM) 330",
         "Adreno (TM) 420",
         "Mali-400 MP",
         "PowerVR SGX 530",
         "PowerVR SGX 540",
         "NVIDIA Tegra",
         "Android Emulator",
         "Gallium 0.4 on llvmpipe",
         "Intel HD Graphics 3000 OpenGL Engine",
@@ -1043,16 +1055,27 @@ GLContext::InitWithPrefixImpl(const char
     }
 #endif
     if (mWorkAroundDriverBugs &&
         Renderer() == GLRenderer::AdrenoTM420) {
         // see bug 1194923. Calling glFlush before glDeleteFramebuffers
         // prevents occasional driver crash.
         mNeedsFlushBeforeDeleteFB = true;
     }
+#ifdef MOZ_WIDGET_ANDROID
+    if (mWorkAroundDriverBugs &&
+        (Renderer() == GLRenderer::AdrenoTM305 ||
+         Renderer() == GLRenderer::AdrenoTM320 ||
+         Renderer() == GLRenderer::AdrenoTM330) &&
+        AndroidBridge::Bridge()->GetAPIVersion() < 21) {
+        // Bug 1164027. Driver crashes when functions such as
+        // glTexImage2D fail due to virtual memory exhaustion.
+        mTextureAllocCrashesOnMapFailure = true;
+    }
+#endif
 
     mMaxTextureImageSize = mMaxTextureSize;
 
     if (IsSupported(GLFeature::framebuffer_multisample)) {
         fGetIntegerv(LOCAL_GL_MAX_SAMPLES, (GLint*)&mMaxSamples);
     }
 
     ////////////////////////////////////////////////////////////////////////////
@@ -2845,16 +2868,68 @@ GLContext::fDeleteFramebuffers(GLsizei n
     if (n == 1 && *names == 0) {
         // Deleting framebuffer 0 causes hangs on the DROID. See bug 623228.
     } else {
         raw_fDeleteFramebuffers(n, names);
     }
     TRACKING_CONTEXT(DeletedFramebuffers(this, n, names));
 }
 
+#ifdef MOZ_WIDGET_ANDROID
+/**
+ * Conservatively estimate whether there is enough available
+ * contiguous virtual address space to map a newly allocated texture.
+ */
+static bool
+WillTextureMapSucceed(GLsizei width, GLsizei height, GLenum format, GLenum type)
+{
+    bool willSucceed = false;
+    // Some drivers leave large gaps between textures, so require
+    // there to be double the actual size of the texture available.
+    size_t size = width * height * GetBytesPerTexel(format, type) * 2;
+
+    int fd = open("/dev/zero", O_RDONLY);
+
+    void *p = mmap(nullptr, size, PROT_NONE, MAP_SHARED, fd, 0);
+    if (p != MAP_FAILED) {
+        willSucceed = true;
+        munmap(p, size);
+    }
+
+    close(fd);
+
+    return willSucceed;
+}
+#endif // MOZ_WIDGET_ANDROID
+
+void
+GLContext::fTexImage2D(GLenum target, GLint level, GLint internalformat,
+                       GLsizei width, GLsizei height, GLint border,
+                       GLenum format, GLenum type, const GLvoid* pixels) {
+    if (!IsTextureSizeSafeToPassToDriver(target, width, height)) {
+        // pass wrong values to cause the GL to generate GL_INVALID_VALUE.
+        // See bug 737182 and the comment in IsTextureSizeSafeToPassToDriver.
+        level = -1;
+        width = -1;
+        height = -1;
+        border = -1;
+    }
+#if MOZ_WIDGET_ANDROID
+    if (mTextureAllocCrashesOnMapFailure) {
+        // We have no way of knowing whether this texture already has
+        // storage allocated for it, and therefore whether this check
+        // is necessary. We must therefore assume it does not and
+        // always perform the check.
+        if (!WillTextureMapSucceed(width, height, internalformat, type)) {
+            return;
+        }
+    }
+#endif
+    raw_fTexImage2D(target, level, internalformat, width, height, border, format, type, pixels);
+}
 
 GLuint
 GLContext::GetDrawFB()
 {
     if (mScreen)
         return mScreen->GetDrawFB();
 
     GLuint ret = 0;
@@ -2963,11 +3038,55 @@ CreateTextureForOffscreen(GLContext* aGL
         MOZ_ASSERT(unpackType == LOCAL_GL_UNSIGNED_BYTE);
         internalFormat = LOCAL_GL_BGRA_EXT;
         unpackFormat = LOCAL_GL_BGRA_EXT;
     }
 
     return CreateTexture(aGL, internalFormat, unpackFormat, unpackType, aSize);
 }
 
+uint32_t
+GetBytesPerTexel(GLenum format, GLenum type)
+{
+    // If there is no defined format or type, we're not taking up any memory
+    if (!format || !type) {
+        return 0;
+    }
+
+    if (format == LOCAL_GL_DEPTH_COMPONENT) {
+        if (type == LOCAL_GL_UNSIGNED_SHORT)
+            return 2;
+        else if (type == LOCAL_GL_UNSIGNED_INT)
+            return 4;
+    } else if (format == LOCAL_GL_DEPTH_STENCIL) {
+        if (type == LOCAL_GL_UNSIGNED_INT_24_8_EXT)
+            return 4;
+    }
+
+    if (type == LOCAL_GL_UNSIGNED_BYTE || type == LOCAL_GL_FLOAT || type == LOCAL_GL_UNSIGNED_INT_8_8_8_8_REV) {
+        uint32_t multiplier = type == LOCAL_GL_UNSIGNED_BYTE ? 1 : 4;
+        switch (format) {
+            case LOCAL_GL_ALPHA:
+            case LOCAL_GL_LUMINANCE:
+                return 1 * multiplier;
+            case LOCAL_GL_LUMINANCE_ALPHA:
+                return 2 * multiplier;
+            case LOCAL_GL_RGB:
+                return 3 * multiplier;
+            case LOCAL_GL_RGBA:
+                return 4 * multiplier;
+            default:
+                break;
+        }
+    } else if (type == LOCAL_GL_UNSIGNED_SHORT_4_4_4_4 ||
+               type == LOCAL_GL_UNSIGNED_SHORT_5_5_5_1 ||
+               type == LOCAL_GL_UNSIGNED_SHORT_5_6_5)
+    {
+        return 2;
+    }
+
+    gfxCriticalError() << "Unknown texture type " << type << " or format " << format;
+    MOZ_CRASH();
+    return 0;
+}
 
 } /* namespace gl */
 } /* namespace mozilla */
--- a/gfx/gl/GLContext.h
+++ b/gfx/gl/GLContext.h
@@ -165,17 +165,19 @@ enum class GLVendor {
     Other
 };
 
 enum class GLRenderer {
     Adreno200,
     Adreno205,
     AdrenoTM200,
     AdrenoTM205,
+    AdrenoTM305,
     AdrenoTM320,
+    AdrenoTM330,
     AdrenoTM420,
     Mali400MP,
     SGX530,
     SGX540,
     Tegra,
     AndroidEmulator,
     GalliumLlvmpipe,
     IntelHD3000,
@@ -1616,27 +1618,19 @@ private:
         ASSERT_NOT_PASSING_STACK_BUFFER_TO_GL(pixels);
         BEFORE_GL_CALL;
         mSymbols.fTexImage2D(target, level, internalformat, width, height, border, format, type, pixels);
         AFTER_GL_CALL;
         mHeavyGLCallsSinceLastFlush = true;
     }
 
 public:
-    void fTexImage2D(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid* pixels) {
-        if (!IsTextureSizeSafeToPassToDriver(target, width, height)) {
-            // pass wrong values to cause the GL to generate GL_INVALID_VALUE.
-            // See bug 737182 and the comment in IsTextureSizeSafeToPassToDriver.
-            level = -1;
-            width = -1;
-            height = -1;
-            border = -1;
-        }
-        raw_fTexImage2D(target, level, internalformat, width, height, border, format, type, pixels);
-    }
+    void fTexImage2D(GLenum target, GLint level, GLint internalformat,
+                     GLsizei width, GLsizei height, GLint border,
+                     GLenum format, GLenum type, const GLvoid* pixels);
 
     void fTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid* pixels) {
         ASSERT_NOT_PASSING_STACK_BUFFER_TO_GL(pixels);
         BEFORE_GL_CALL;
         mSymbols.fTexSubImage2D(target, level, xoffset, yoffset, width, height, format, type, pixels);
         AFTER_GL_CALL;
         mHeavyGLCallsSinceLastFlush = true;
     }
@@ -3526,16 +3520,17 @@ protected:
     GLint mMaxTextureSize;
     GLint mMaxCubeMapTextureSize;
     GLint mMaxTextureImageSize;
     GLint mMaxRenderbufferSize;
     GLint mMaxViewportDims[2];
     GLsizei mMaxSamples;
     bool mNeedsTextureSizeChecks;
     bool mNeedsFlushBeforeDeleteFB;
+    bool mTextureAllocCrashesOnMapFailure;
     bool mWorkAroundDriverBugs;
 
     bool IsTextureSizeSafeToPassToDriver(GLenum target, GLsizei width, GLsizei height) const {
         if (mNeedsTextureSizeChecks) {
             // some drivers incorrectly handle some large texture sizes that are below the
             // max texture size that they report. So we check ourselves against our own values
             // (mMax[CubeMap]TextureSize).
             // see bug 737182 for Mac Intel 2D textures
@@ -3694,12 +3689,18 @@ GLuint CreateTextureForOffscreen(GLConte
  *    GL_TEXTURE_MIN_FILTER = GL_LINEAR
  *    GL_TEXTURE_MAG_FILTER = GL_LINEAR
  *    GL_TEXTURE_WRAP_S = GL_CLAMP_TO_EDGE
  *    GL_TEXTURE_WRAP_T = GL_CLAMP_TO_EDGE
  */
 GLuint CreateTexture(GLContext* aGL, GLenum aInternalFormat, GLenum aFormat,
                      GLenum aType, const gfx::IntSize& aSize, bool linear = true);
 
+/**
+ * Helper function that calculates the number of bytes required per
+ * texel for a texture from its format and type.
+ */
+uint32_t GetBytesPerTexel(GLenum format, GLenum type);
+
 } /* namespace gl */
 } /* namespace mozilla */
 
 #endif /* GLCONTEXT_H_ */
--- a/gfx/gl/GLUploadHelpers.cpp
+++ b/gfx/gl/GLUploadHelpers.cpp
@@ -376,61 +376,16 @@ TexImage2DHelper(GLContext* gl,
                         format,
                         type,
                         pixels);
         gl->fPixelStorei(LOCAL_GL_UNPACK_ROW_LENGTH, 0);
         gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, 4);
     }
 }
 
-static uint32_t
-GetBytesPerTexel(GLenum format, GLenum type)
-{
-    // If there is no defined format or type, we're not taking up any memory
-    if (!format || !type) {
-        return 0;
-    }
-
-    if (format == LOCAL_GL_DEPTH_COMPONENT) {
-        if (type == LOCAL_GL_UNSIGNED_SHORT)
-            return 2;
-        else if (type == LOCAL_GL_UNSIGNED_INT)
-            return 4;
-    } else if (format == LOCAL_GL_DEPTH_STENCIL) {
-        if (type == LOCAL_GL_UNSIGNED_INT_24_8_EXT)
-            return 4;
-    }
-
-    if (type == LOCAL_GL_UNSIGNED_BYTE || type == LOCAL_GL_FLOAT || type == LOCAL_GL_UNSIGNED_INT_8_8_8_8_REV) {
-        uint32_t multiplier = type == LOCAL_GL_UNSIGNED_BYTE ? 1 : 4;
-        switch (format) {
-            case LOCAL_GL_ALPHA:
-            case LOCAL_GL_LUMINANCE:
-                return 1 * multiplier;
-            case LOCAL_GL_LUMINANCE_ALPHA:
-                return 2 * multiplier;
-            case LOCAL_GL_RGB:
-                return 3 * multiplier;
-            case LOCAL_GL_RGBA:
-                return 4 * multiplier;
-            default:
-                break;
-        }
-    } else if (type == LOCAL_GL_UNSIGNED_SHORT_4_4_4_4 ||
-               type == LOCAL_GL_UNSIGNED_SHORT_5_5_5_1 ||
-               type == LOCAL_GL_UNSIGNED_SHORT_5_6_5)
-    {
-        return 2;
-    }
-
-    gfxCriticalError() << "Unknown texture type " << type << " or format " << format;
-    MOZ_CRASH();
-    return 0;
-}
-
 SurfaceFormat
 UploadImageDataToTexture(GLContext* gl,
                          unsigned char* aData,
                          int32_t aStride,
                          SurfaceFormat aFormat,
                          const nsIntRegion& aDstRegion,
                          GLuint& aTexture,
                          size_t* aOutUploadSize,
--- a/ipc/mscom/COMApartmentRegion.h
+++ b/ipc/mscom/COMApartmentRegion.h
@@ -11,17 +11,17 @@
 #include "mozilla/Attributes.h"
 
 #include <objbase.h>
 
 namespace mozilla {
 namespace mscom {
 
 template<COINIT T>
-class MOZ_RAII COMApartmentRegion
+class MOZ_NON_TEMPORARY_CLASS COMApartmentRegion
 {
 public:
   COMApartmentRegion()
     : mInitResult(::CoInitializeEx(nullptr, T))
   {
     // If this fires then we're probably mixing apartments on the same thread
     MOZ_ASSERT(IsValid());
   }
new file mode 100644
--- /dev/null
+++ b/ipc/mscom/MainThreadRuntime.cpp
@@ -0,0 +1,166 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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 "mozilla/mscom/MainThreadRuntime.h"
+
+#include "mozilla/ArrayUtils.h"
+#include "mozilla/Assertions.h"
+#include "mozilla/RefPtr.h"
+#include "mozilla/UniquePtr.h"
+#include "mozilla/WindowsVersion.h"
+#include "nsDebug.h"
+#include "nsWindowsHelpers.h"
+
+#include <accctrl.h>
+#include <aclapi.h>
+#include <objbase.h>
+#include <objidl.h>
+
+namespace {
+
+struct LocalFreeDeleter
+{
+  void operator()(void* aPtr)
+  {
+    ::LocalFree(aPtr);
+  }
+};
+
+} // anonymous namespace
+
+namespace mozilla {
+namespace mscom {
+
+MainThreadRuntime::MainThreadRuntime()
+  : mInitResult(E_UNEXPECTED)
+{
+  // We must be the outermost COM initialization on this thread. The COM runtime
+  // cannot be configured once we start manipulating objects
+  MOZ_ASSERT(mStaRegion.IsValidOutermost());
+  if (NS_WARN_IF(!mStaRegion.IsValidOutermost())) {
+    return;
+  }
+
+  // Windows XP doesn't support setting of the COM exception policy, so we'll
+  // just stop here in that case.
+  if (!IsVistaOrLater()) {
+    mInitResult = S_OK;
+    return;
+  }
+
+  // We are required to initialize security in order to configure global options.
+  mInitResult = InitializeSecurity();
+  MOZ_ASSERT(SUCCEEDED(mInitResult));
+  if (FAILED(mInitResult)) {
+    return;
+  }
+
+  RefPtr<IGlobalOptions> globalOpts;
+  mInitResult = ::CoCreateInstance(CLSID_GlobalOptions, nullptr,
+                                   CLSCTX_INPROC_SERVER, IID_IGlobalOptions,
+                                   (void**)getter_AddRefs(globalOpts));
+  MOZ_ASSERT(SUCCEEDED(mInitResult));
+  if (FAILED(mInitResult)) {
+    return;
+  }
+
+  // Windows 7 has a policy that is even more strict. We should use that one
+  // whenever possible.
+  ULONG_PTR exceptionSetting = IsWin7OrLater() ?
+                               COMGLB_EXCEPTION_DONOT_HANDLE_ANY :
+                               COMGLB_EXCEPTION_DONOT_HANDLE;
+  mInitResult = globalOpts->Set(COMGLB_EXCEPTION_HANDLING, exceptionSetting);
+  MOZ_ASSERT(SUCCEEDED(mInitResult));
+}
+
+HRESULT
+MainThreadRuntime::InitializeSecurity()
+{
+  HANDLE rawToken = nullptr;
+  BOOL ok = ::OpenProcessToken(::GetCurrentProcess(), TOKEN_QUERY, &rawToken);
+  if (!ok) {
+    return HRESULT_FROM_WIN32(::GetLastError());
+  }
+  nsAutoHandle token(rawToken);
+
+  DWORD len = 0;
+  ok = ::GetTokenInformation(token, TokenUser, nullptr, len, &len);
+  DWORD win32Error = ::GetLastError();
+  if (!ok && win32Error != ERROR_INSUFFICIENT_BUFFER) {
+    return HRESULT_FROM_WIN32(win32Error);
+  }
+
+  auto tokenUserBuf = MakeUnique<BYTE[]>(len);
+  TOKEN_USER& tokenUser = *reinterpret_cast<TOKEN_USER*>(tokenUserBuf.get());
+  ok = ::GetTokenInformation(token, TokenUser, tokenUserBuf.get(), len, &len);
+  if (!ok) {
+    return HRESULT_FROM_WIN32(::GetLastError());
+  }
+
+  len = 0;
+  ok = ::GetTokenInformation(token, TokenPrimaryGroup, nullptr, len, &len);
+  win32Error = ::GetLastError();
+  if (!ok && win32Error != ERROR_INSUFFICIENT_BUFFER) {
+    return HRESULT_FROM_WIN32(win32Error);
+  }
+
+  auto tokenPrimaryGroupBuf = MakeUnique<BYTE[]>(len);
+  TOKEN_PRIMARY_GROUP& tokenPrimaryGroup =
+    *reinterpret_cast<TOKEN_PRIMARY_GROUP*>(tokenPrimaryGroupBuf.get());
+  ok = ::GetTokenInformation(token, TokenPrimaryGroup, tokenPrimaryGroupBuf.get(),
+                             len, &len);
+  if (!ok) {
+    return HRESULT_FROM_WIN32(::GetLastError());
+  }
+
+  SECURITY_DESCRIPTOR sd;
+  if (!::InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION)) {
+    return HRESULT_FROM_WIN32(::GetLastError());
+  }
+
+  // Grant access to SYSTEM, Administrators, and the user.
+  EXPLICIT_ACCESS entries[] = {
+    {COM_RIGHTS_EXECUTE, GRANT_ACCESS, NO_INHERITANCE,
+      {nullptr, NO_MULTIPLE_TRUSTEE, TRUSTEE_IS_NAME, TRUSTEE_IS_USER,
+       L"SYSTEM"}},
+    {COM_RIGHTS_EXECUTE, GRANT_ACCESS, NO_INHERITANCE,
+      {nullptr, NO_MULTIPLE_TRUSTEE, TRUSTEE_IS_NAME, TRUSTEE_IS_WELL_KNOWN_GROUP,
+       L"ADMINISTRATORS"}},
+    {COM_RIGHTS_EXECUTE, GRANT_ACCESS, NO_INHERITANCE,
+      {nullptr, NO_MULTIPLE_TRUSTEE, TRUSTEE_IS_SID, TRUSTEE_IS_USER,
+       reinterpret_cast<LPWSTR>(tokenUser.User.Sid)}}
+  };
+
+  PACL rawDacl = nullptr;
+  win32Error = ::SetEntriesInAcl(ArrayLength(entries), entries, nullptr,
+                                 &rawDacl);
+  if (win32Error != ERROR_SUCCESS) {
+    return HRESULT_FROM_WIN32(win32Error);
+  }
+
+  UniquePtr<ACL, LocalFreeDeleter> dacl(rawDacl);
+
+  if (!::SetSecurityDescriptorDacl(&sd, TRUE, dacl.get(), FALSE)) {
+    return HRESULT_FROM_WIN32(::GetLastError());
+  }
+
+  if (!::SetSecurityDescriptorOwner(&sd, tokenUser.User.Sid, FALSE)) {
+    return HRESULT_FROM_WIN32(::GetLastError());
+  }
+
+  if (!::SetSecurityDescriptorGroup(&sd, tokenPrimaryGroup.PrimaryGroup, FALSE)) {
+    return HRESULT_FROM_WIN32(::GetLastError());
+  }
+
+  return ::CoInitializeSecurity(&sd, -1, nullptr, nullptr,
+                                RPC_C_AUTHN_LEVEL_DEFAULT,
+                                RPC_C_IMP_LEVEL_IDENTIFY, nullptr, EOAC_NONE,
+                                nullptr);
+}
+
+} // namespace mscom
+} // namespace mozilla
+
new file mode 100644
--- /dev/null
+++ b/ipc/mscom/MainThreadRuntime.h
@@ -0,0 +1,42 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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/. */
+
+#ifndef mozilla_mscom_MainThreadRuntime_h
+#define mozilla_mscom_MainThreadRuntime_h
+
+#include "mozilla/Attributes.h"
+#include "mozilla/mscom/COMApartmentRegion.h"
+
+namespace mozilla {
+namespace mscom {
+
+class MOZ_NON_TEMPORARY_CLASS MainThreadRuntime
+{
+public:
+  MainThreadRuntime();
+
+  explicit operator bool() const
+  {
+    return mStaRegion.IsValidOutermost() && SUCCEEDED(mInitResult);
+  }
+
+  MainThreadRuntime(MainThreadRuntime&) = delete;
+  MainThreadRuntime(MainThreadRuntime&&) = delete;
+  MainThreadRuntime& operator=(MainThreadRuntime&) = delete;
+  MainThreadRuntime& operator=(MainThreadRuntime&&) = delete;
+
+private:
+  HRESULT InitializeSecurity();
+
+  STARegion mStaRegion;
+  HRESULT mInitResult;
+};
+
+} // namespace mscom
+} // namespace mozilla
+
+#endif // mozilla_mscom_MainThreadRuntime_h
+
--- a/ipc/mscom/moz.build
+++ b/ipc/mscom/moz.build
@@ -1,18 +1,20 @@
 # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # 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/.
 
 EXPORTS.mozilla.mscom += [
     'COMApartmentRegion.h',
+    'MainThreadRuntime.h',
     'Utils.h',
 ]
 
 UNIFIED_SOURCES += [
+    'MainThreadRuntime.cpp',
     'Utils.cpp',
 ]
 
 include('/ipc/chromium/chromium-config.mozbuild')
 
 FINAL_LIBRARY = 'xul'
--- a/netwerk/protocol/res/ExtensionProtocolHandler.cpp
+++ b/netwerk/protocol/res/ExtensionProtocolHandler.cpp
@@ -73,16 +73,49 @@ protected:
   virtual ~PipeCloser() {}
 
 private:
   nsCOMPtr<nsIOutputStream> mOutputStream;
 };
 
 NS_IMPL_ISUPPORTS(PipeCloser, nsIRequestObserver)
 
+bool
+ExtensionProtocolHandler::ResolveSpecialCases(const nsACString& aHost,
+                                              const nsACString& aPath,
+                                              const nsACString& aPathname,
+                                              nsACString& aResult)
+{
+  // Create special moz-extension:-pages such as moz-extension://foo/_blank.html
+  // for all registered extensions. We can't just do this as a substitution
+  // because substitutions can only match on host.
+  if (!SubstitutingProtocolHandler::HasSubstitution(aHost)) {
+    return false;
+  }
+  if (aPathname.EqualsLiteral("/_blank.html")) {
+    aResult.AssignLiteral("about:blank");
+    return true;
+  }
+  if (aPathname.EqualsLiteral("/_generated_background_page.html")) {
+    nsCOMPtr<nsIAddonPolicyService> aps =
+      do_GetService("@mozilla.org/addons/policy-service;1");
+    if (!aps) {
+      return false;
+    }
+    nsresult rv = aps->GetGeneratedBackgroundPageUrl(aHost, aResult);
+    NS_ENSURE_SUCCESS(rv, false);
+    if (!aResult.IsEmpty()) {
+      MOZ_RELEASE_ASSERT(Substring(aResult, 0, 5).Equals("data:"));
+      return true;
+    }
+  }
+
+  return false;
+}
+
 nsresult
 ExtensionProtocolHandler::SubstituteChannel(nsIURI* aURI,
                                             nsILoadInfo* aLoadInfo,
                                             nsIChannel** result)
 {
   nsresult rv;
   nsCOMPtr<nsIURL> url = do_QueryInterface(aURI, &rv);
   NS_ENSURE_SUCCESS(rv, rv);
--- a/netwerk/protocol/res/ExtensionProtocolHandler.h
+++ b/netwerk/protocol/res/ExtensionProtocolHandler.h
@@ -23,28 +23,20 @@ public:
   NS_FORWARD_NSIPROTOCOLHANDLER(SubstitutingProtocolHandler::)
   NS_FORWARD_NSISUBSTITUTINGPROTOCOLHANDLER(SubstitutingProtocolHandler::)
 
   ExtensionProtocolHandler() : SubstitutingProtocolHandler("moz-extension") {}
 
 protected:
   ~ExtensionProtocolHandler() {}
 
-  bool ResolveSpecialCases(const nsACString& aHost, const nsACString& aPath, nsACString& aResult) override
-  {
-    // Create a special about:blank-like moz-extension://foo/_blank.html for all
-    // registered extensions. We can't just do this as a substitution because
-    // substitutions can only match on host.
-    if (SubstitutingProtocolHandler::HasSubstitution(aHost) && aPath.EqualsLiteral("/_blank.html")) {
-      aResult.AssignLiteral("about:blank");
-      return true;
-    }
-
-    return false;
-  }
+  bool ResolveSpecialCases(const nsACString& aHost,
+                           const nsACString& aPath,
+                           const nsACString& aPathname,
+                           nsACString& aResult) override;
 
   virtual nsresult SubstituteChannel(nsIURI* uri, nsILoadInfo* aLoadInfo, nsIChannel** result) override;
 };
 
 } // namespace net
 } // namespace mozilla
 
 #endif /* ExtensionProtocolHandler_h___ */
--- a/netwerk/protocol/res/SubstitutingProtocolHandler.cpp
+++ b/netwerk/protocol/res/SubstitutingProtocolHandler.cpp
@@ -338,43 +338,43 @@ SubstitutingProtocolHandler::HasSubstitu
 
 nsresult
 SubstitutingProtocolHandler::ResolveURI(nsIURI *uri, nsACString &result)
 {
   nsresult rv;
 
   nsAutoCString host;
   nsAutoCString path;
+  nsAutoCString pathname;
+
+  nsCOMPtr<nsIURL> url = do_QueryInterface(uri);
+  if (!url) {
+    return NS_ERROR_MALFORMED_URI;
+  }
 
   rv = uri->GetAsciiHost(host);
   if (NS_FAILED(rv)) return rv;
 
   rv = uri->GetPath(path);
   if (NS_FAILED(rv)) return rv;
 
-  if (ResolveSpecialCases(host, path, result)) {
+  rv = url->GetFilePath(pathname);
+  if (NS_FAILED(rv)) return rv;
+
+  if (ResolveSpecialCases(host, path, pathname, result)) {
     return NS_OK;
   }
 
   nsCOMPtr<nsIURI> baseURI;
   rv = GetSubstitution(host, getter_AddRefs(baseURI));
   if (NS_FAILED(rv)) return rv;
 
   // Unescape the path so we can perform some checks on it.
-  nsCOMPtr<nsIURL> url = do_QueryInterface(uri);
-  if (!url) {
-    return NS_ERROR_MALFORMED_URI;
-  }
-
-  nsAutoCString unescapedPath;
-  rv = url->GetFilePath(unescapedPath);
-  if (NS_FAILED(rv)) return rv;
-
-  NS_UnescapeURL(unescapedPath);
-  if (unescapedPath.FindChar('\\') != -1) {
+  NS_UnescapeURL(pathname);
+  if (pathname.FindChar('\\') != -1) {
     return NS_ERROR_MALFORMED_URI;
   }
 
   // Some code relies on an empty path resolving to a file rather than a
   // directory.
   NS_ASSERTION(path.CharAt(0) == '/', "Path must begin with '/'");
   if (path.Length() == 1) {
     rv = baseURI->GetSpec(result);
--- a/netwerk/protocol/res/SubstitutingProtocolHandler.h
+++ b/netwerk/protocol/res/SubstitutingProtocolHandler.h
@@ -50,17 +50,20 @@ protected:
   virtual nsresult GetSubstitutionInternal(const nsACString& aRoot, nsIURI** aResult)
   {
     *aResult = nullptr;
     return NS_ERROR_NOT_AVAILABLE;
   }
 
   // Override this in the subclass to check for special case when resolving URIs
   // _before_ checking substitutions.
-  virtual bool ResolveSpecialCases(const nsACString& aHost, const nsACString& aPath, nsACString& aResult)
+  virtual bool ResolveSpecialCases(const nsACString& aHost,
+                                   const nsACString& aPath,
+                                   const nsACString& aPathname,
+                                   nsACString& aResult)
   {
     return false;
   }
 
   // Override this in the subclass to check for special case when opening
   // channels.
   virtual nsresult SubstituteChannel(nsIURI* uri, nsILoadInfo* aLoadInfo, nsIChannel** result)
   {
--- a/netwerk/protocol/res/nsResProtocolHandler.cpp
+++ b/netwerk/protocol/res/nsResProtocolHandler.cpp
@@ -61,26 +61,27 @@ NS_IMPL_QUERY_INTERFACE(nsResProtocolHan
 NS_IMPL_ADDREF_INHERITED(nsResProtocolHandler, SubstitutingProtocolHandler)
 NS_IMPL_RELEASE_INHERITED(nsResProtocolHandler, SubstitutingProtocolHandler)
 
 nsresult
 nsResProtocolHandler::GetSubstitutionInternal(const nsACString& root, nsIURI **result)
 {
     nsAutoCString uri;
 
-    if (!ResolveSpecialCases(root, NS_LITERAL_CSTRING("/"), uri)) {
+    if (!ResolveSpecialCases(root, NS_LITERAL_CSTRING("/"), NS_LITERAL_CSTRING("/"), uri)) {
         return NS_ERROR_NOT_AVAILABLE;
     }
 
     return NS_NewURI(result, uri);
 }
 
 bool
 nsResProtocolHandler::ResolveSpecialCases(const nsACString& aHost,
                                           const nsACString& aPath,
+                                          const nsACString& aPathname,
                                           nsACString& aResult)
 {
     if (aHost.Equals("") || aHost.Equals(kAPP)) {
         aResult.Assign(mAppURI);
     } else if (aHost.Equals(kGRE)) {
         aResult.Assign(mGREURI);
     } else {
         return false;
--- a/netwerk/protocol/res/nsResProtocolHandler.h
+++ b/netwerk/protocol/res/nsResProtocolHandler.h
@@ -47,17 +47,19 @@ public:
     {
         return mozilla::SubstitutingProtocolHandler::ResolveURI(aResURI, aResult);
     }
 
 protected:
     nsresult GetSubstitutionInternal(const nsACString& aRoot, nsIURI** aResult) override;
     virtual ~nsResProtocolHandler() {}
 
-    bool ResolveSpecialCases(const nsACString& aHost, const nsACString& aPath,
+    bool ResolveSpecialCases(const nsACString& aHost,
+                             const nsACString& aPath,
+                             const nsACString& aPathname,
                              nsACString& aResult) override;
 
 private:
     nsCString mAppURI;
     nsCString mGREURI;
 };
 
 #endif /* nsResProtocolHandler_h___ */
--- a/taskcluster/taskgraph/task/test.py
+++ b/taskcluster/taskgraph/task/test.py
@@ -136,16 +136,22 @@ class TestTask(base.Task):
     def load_transforms(cls, transforms_cfg):
         """Load the transforms specified in kind.yml"""
         transforms = []
         for path in transforms_cfg:
             transform = find_object(path)
             transforms.append(transform)
         return transforms
 
+    @classmethod
+    def from_json(cls, task_dict):
+        test_task = cls(kind=task_dict['attributes']['kind'],
+                        task=task_dict)
+        return test_task
+
     def __init__(self, kind, task):
         self.dependencies = task['dependencies']
         super(TestTask, self).__init__(kind, task['label'], task['attributes'], task['task'])
 
     def get_dependencies(self, taskgraph):
         return [(label, name) for name, label in self.dependencies.items()]
 
     def optimize(self):
--- a/testing/mozbase/mozfile/mozfile/mozfile.py
+++ b/testing/mozbase/mozfile/mozfile/mozfile.py
@@ -137,16 +137,18 @@ def rmtree(dir):
     return remove(dir)
 
 
 def _call_windows_retry(func, args=(), retry_max=5, retry_delay=0.5):
     """
     It's possible to see spurious errors on Windows due to various things
     keeping a handle to the directory open (explorer, virus scanners, etc)
     So we try a few times if it fails with a known error.
+    retry_delay is multiplied by the number of failed attempts to increase
+    the likelihood of success in subsequent attempts.
     """
     retry_count = 0
     while True:
         try:
             func(*args)
         except OSError as e:
             # Error codes are defined in:
             # http://docs.python.org/2/library/errno.html#module-errno
@@ -155,34 +157,34 @@ def _call_windows_retry(func, args=(), r
 
             if retry_count == retry_max:
                 raise
 
             retry_count += 1
 
             print '%s() failed for "%s". Reason: %s (%s). Retrying...' % \
                     (func.__name__, args, e.strerror, e.errno)
-            time.sleep(retry_delay)
+            time.sleep(retry_count * retry_delay)
         else:
             # If no exception has been thrown it should be done
             break
 
 
 def remove(path):
     """Removes the specified file, link, or directory tree.
 
     This is a replacement for shutil.rmtree that works better under
     windows. It does the following things:
 
      - check path access for the current user before trying to remove
      - retry operations on some known errors due to various things keeping
        a handle on file paths - like explorer, virus scanners, etc. The
        known errors are errno.EACCES and errno.ENOTEMPTY, and it will
-       retry up to 5 five times with a delay of 0.5 seconds between each
-       attempt.
+       retry up to 5 five times with a delay of (failed_attempts * 0.5) seconds
+       between each attempt.
 
     Note that no error will be raised if the given path does not exists.
 
     :param path: path to be removed
     """
 
     import shutil
 
--- a/testing/mozharness/mozharness/base/python.py
+++ b/testing/mozharness/mozharness/base/python.py
@@ -609,48 +609,52 @@ class ResourceMonitoringMixin(object):
             })
 
             for phase in rm.phases.keys():
                 phase_duration = rm.phases[phase][1] - rm.phases[phase][0]
                 subtests = [
                     {
                         'name': 'time',
                         'value': phase_duration,
-                    },
-                    {
+                    }
+                ]
+                cpu_percent = rm.aggregate_cpu_percent(phase=phase,
+                                                       per_cpu=False)
+                if cpu_percent is not None:
+                    subtests.append({
                         'name': 'cpu_percent',
                         'value': rm.aggregate_cpu_percent(phase=phase,
                                                           per_cpu=False),
-                    }
-                ]
+                    })
+
                 # We don't report I/O during each step because measured I/O
                 # is system I/O and that I/O can be delayed (e.g. writes will
                 # buffer before being flushed and recorded in our metrics).
                 suites.append({
                     'name': '%s.%s' % (perfherder_name, phase),
                     'subtests': subtests,
                 })
 
             data = {
                 'framework': {'name': 'job_resource_usage'},
                 'suites': suites,
             }
 
-            try:
-                schema_path = os.path.join(external_tools_path,
-                                           'performance-artifact-schema.json')
-                with open(schema_path, 'rb') as fh:
-                    schema = json.load(fh)
+            schema_path = os.path.join(external_tools_path,
+                                       'performance-artifact-schema.json')
+            with open(schema_path, 'rb') as fh:
+                schema = json.load(fh)
 
-                self.info('Validating Perfherder data against %s' % schema_path)
-                jsonschema.validate(data, schema)
-            except Exception:
-                self.exception('error while validating Perfherder data; ignoring')
-            else:
-                self.info('PERFHERDER_DATA: %s' % json.dumps(data))
+            # this will throw an exception that causes the job to fail if the
+            # perfherder data is not valid -- please don't change this
+            # behaviour, otherwise people will inadvertently break this
+            # functionality
+            self.info('Validating Perfherder data against %s' % schema_path)
+            jsonschema.validate(data, schema)
+            self.info('PERFHERDER_DATA: %s' % json.dumps(data))
 
         log_usage('Total resource usage', duration, cpu_percent, cpu_times, io)
 
         # Print special messages so usage shows up in Treeherder.
         if cpu_percent:
             self._tinderbox_print('CPU usage<br/>{:,.1f}%'.format(
                                   cpu_percent))
 
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/custom-elements/v0/instantiating/extensions-to-document-interface/create-element-interface-type-is-a-local-name.html.ini
@@ -0,0 +1,6 @@
+[create-element-interface-type-is-a-local-name.html]
+  type: testharness
+  [Test Document.createElement() creates custom element of type, specified by localName argument, if an element definition with matching localName, namespace, and type is not registered]
+    expected: FAIL
+  [Test Document.createElementNS() creates custom element of type, specified by localName argument, if an element definition with matching localName, namespace, and type is not registered]
+    expected: FAIL
--- a/testing/web-platform/meta/custom-elements/v0/instantiating/extensions-to-document-interface/create-element-is-attribute.html.ini
+++ b/testing/web-platform/meta/custom-elements/v0/instantiating/extensions-to-document-interface/create-element-is-attribute.html.ini
@@ -1,8 +1,11 @@
 [create-element-is-attribute.html]
   type: testharness
   [Test Document.createElement() sets the element's IS attribute value to type, if type is not the same as localName]
     expected: FAIL
-
+  [Test Document.createElement() sets the element's IS attribute value to type, if type is not the same as localName and an element definition with matching localName, namespace, and type is not registered]
+    expected: FAIL
   [Test Document.createElementNS() sets the element's IS attribute value to type, if type is not the same as localName]
     expected: FAIL
+  [Test Document.createElementNS() sets the element's IS attribute value to type, if type is not the same as localNameand and an element definition with matching localName, namespace, and type is not registered ]
+    expected: FAIL
 
--- a/testing/web-platform/meta/custom-elements/v0/instantiating/extensions-to-document-interface/create-element-namespace.html.ini
+++ b/testing/web-platform/meta/custom-elements/v0/instantiating/extensions-to-document-interface/create-element-namespace.html.ini
@@ -1,5 +1,6 @@
 [create-element-namespace.html]
   type: testharness
   [Document.createElement() sets custom element namespace to HTML Namespace. Custom element is extending standard HTML tag]
     expected: FAIL
-
+  [Document.createElement() sets custom element namespace to HTML Namespace. Document.createElement() is called with standard HTML tag name and type without registered custom element of such type]
+    expected: FAIL
--- a/testing/web-platform/meta/custom-elements/v0/instantiating/extensions-to-document-interface/created-callback-create-element-ns.html.ini
+++ b/testing/web-platform/meta/custom-elements/v0/instantiating/extensions-to-document-interface/created-callback-create-element-ns.html.ini
@@ -1,5 +1,6 @@
 [created-callback-create-element-ns.html]
   type: testharness
   [Test Document.createElementNS() enqueues created callback for custom elements that are extensions of HTML5 elements]
     expected: FAIL
-
+  [Test Document.createElementNS() with typeExtension argument enqueues created callback]
+    expected: FAIL
--- a/testing/web-platform/meta/custom-elements/v0/instantiating/extensions-to-document-interface/created-callback-create-element.html.ini
+++ b/testing/web-platform/meta/custom-elements/v0/instantiating/extensions-to-document-interface/created-callback-create-element.html.ini
@@ -1,5 +1,6 @@
 [created-callback-create-element.html]
   type: testharness
   [Test Document.createElement() enqueues created callback for custom elements that are extensions of HTML5 elements]
     expected: FAIL
-
+  [Test Document.createElement() with typeExtension argument enqueues created callback]
+    expected: FAIL
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/custom-elements/v0/instantiating/unchanged-attribute.html.ini
@@ -0,0 +1,5 @@
+[unchanged-attribute.html]
+  type: testharness
+  [After a custom element is instantiated, changing the value of the is attribute must not affect this element's custom element type.]
+    expected: FAIL
+
--- a/toolkit/components/extensions/Extension.jsm
+++ b/toolkit/components/extensions/Extension.jsm
@@ -655,27 +655,58 @@ GlobalManager = {
 
 
     let extension = this.extensionMap.get(id);
     let uri = contentWindow.document.documentURIObject;
     let incognito = PrivateBrowsingUtils.isContentWindowPrivate(contentWindow);
 
     let context = new ExtensionContext(extension, {type, contentWindow, uri, docShell, incognito});
     inject(extension, context);
+    if (type == "background") {
+      this._initializeBackgroundPage(contentWindow);
+    }
 
     let eventHandler = docShell.chromeEventHandler;
     let listener = event => {
       if (event.target != docShell.contentViewer.DOMDocument) {
         return;
       }
       eventHandler.removeEventListener("unload", listener, true);
       context.unload();
     };
     eventHandler.addEventListener("unload", listener, true);
   },
+
+  _initializeBackgroundPage(contentWindow) {
+    // Override the `alert()` method inside background windows;
+    // we alias it to console.log().
+    // See: https://bugzilla.mozilla.org/show_bug.cgi?id=1203394
+    let alertDisplayedWarning = false;
+    let alertOverwrite = text => {
+      if (!alertDisplayedWarning) {
+        let consoleWindow = Services.wm.getMostRecentWindow("devtools:webconsole");
+        if (!consoleWindow) {
+          let {require} = Cu.import("resource://devtools/shared/Loader.jsm", {});
+          require("devtools/client/framework/devtools-browser");
+          let hudservice = require("devtools/client/webconsole/hudservice");
+          hudservice.toggleBrowserConsole().catch(Cu.reportError);
+        } else {
+          // the Browser Console was already open
+          consoleWindow.focus();
+        }
+
+        contentWindow.console.warn("alert() is not supported in background windows; please use console.log instead.");
+
+        alertDisplayedWarning = true;
+      }
+
+      contentWindow.console.log(text);
+    };
+    Cu.exportFunction(alertOverwrite, contentWindow, {defineAs: "alert"});
+  },
 };
 
 // All moz-extension URIs use a machine-specific UUID rather than the
 // extension's own ID in the host component. This makes it more
 // difficult for web pages to detect whether a user has a given add-on
 // installed (by trying to load a moz-extension URI referring to a
 // web_accessible_resource from the extension). getExtensionUUID
 // returns the UUID for a given add-on ID.
--- a/toolkit/components/extensions/ExtensionManagement.jsm
+++ b/toolkit/components/extensions/ExtensionManagement.jsm
@@ -156,25 +156,27 @@ var Service = {
     let handler = Services.io.getProtocolHandler("moz-extension");
     handler.QueryInterface(Ci.nsISubstitutingProtocolHandler);
     handler.setSubstitution(uuid, uri);
 
     this.uuidMap.set(uuid, extension);
     this.aps.setAddonLoadURICallback(extension.id, this.checkAddonMayLoad.bind(this, extension));
     this.aps.setAddonLocalizeCallback(extension.id, extension.localize.bind(extension));
     this.aps.setAddonCSP(extension.id, extension.manifest.content_security_policy);
+    this.aps.setBackgroundPageUrlCallback(uuid, this.generateBackgroundPageUrl.bind(this, extension));
   },
 
   // Called when an extension is unloaded.
   shutdownExtension(uuid) {
     let extension = this.uuidMap.get(uuid);
     this.uuidMap.delete(uuid);
     this.aps.setAddonLoadURICallback(extension.id, null);
     this.aps.setAddonLocalizeCallback(extension.id, null);
     this.aps.setAddonCSP(extension.id, null);
+    this.aps.setBackgroundPageUrlCallback(uuid, null);
 
     let handler = Services.io.getProtocolHandler("moz-extension");
     handler.QueryInterface(Ci.nsISubstitutingProtocolHandler);
     handler.setSubstitution(uuid, null);
   },
 
   // Return true if the given URI can be loaded from arbitrary web
   // content. The manifest.json |web_accessible_resources| directive
@@ -195,16 +197,31 @@ var Service = {
 
   // Checks whether a given extension can load this URI (typically via
   // an XML HTTP request). The manifest.json |permissions| directive
   // determines this.
   checkAddonMayLoad(extension, uri) {
     return extension.whiteListedHosts.matchesIgnoringPath(uri);
   },
 
+  generateBackgroundPageUrl(extension) {
+    let background_scripts = extension.manifest.background &&
+      extension.manifest.background.scripts;
+    if (!background_scripts) {
+      return;
+    }
+    let html = "<!DOCTYPE html>\n<body>\n";
+    for (let script of background_scripts) {
+      script = script.replace(/"/g, "&quot;");
+      html += `<script src="${script}"></script>\n`;
+    }
+    html += "</body>\n</html>\n";
+    return "data:text/html;charset=utf-8," + encodeURIComponent(html);
+  },
+
   // Finds the add-on ID associated with a given moz-extension:// URI.
   // This is used to set the addonId on the originAttributes for the
   // nsIPrincipal attached to the URI.
   extensionURIToAddonID(uri) {
     let uuid = uri.host;
     let extension = this.uuidMap.get(uuid);
     return extension ? extension.id : undefined;
   },
--- a/toolkit/components/extensions/ExtensionUtils.jsm
+++ b/toolkit/components/extensions/ExtensionUtils.jsm
@@ -947,17 +947,17 @@ function promiseDocumentReady(doc) {
     }, true);
   });
 }
 
 /*
  * Messaging primitives.
  */
 
-var nextPortId = 1;
+let gNextPortId = 1;
 
 // Abstraction for a Port object in the extension API. Each port has a unique ID.
 function Port(context, messageManager, name, id, sender) {
   this.context = context;
   this.messageManager = messageManager;
   this.name = name;
   this.id = id;
   this.listenerName = `Extension:Port-${this.id}`;
@@ -1157,17 +1157,18 @@ Messenger.prototype = {
       MessageChannel.addListener(this.messageManagers, "Extension:Message", listener);
       return () => {
         MessageChannel.removeListener(this.messageManagers, "Extension:Message", listener);
       };
     }).api();
   },
 
   connect(messageManager, name, recipient) {
-    let portId = nextPortId++;
+    // TODO(robwu): Use a process ID instead of the process type. bugzil.la/1287626
+    let portId = `${gNextPortId++}-${Services.appinfo.processType}`;
     let port = new Port(this.context, messageManager, name, portId, null);
     let msg = {name, portId};
     // TODO: Disconnect the port if no response?
     this._sendMessage(messageManager, "Extension:Connect", msg, recipient);
     return port.api();
   },
 
   onConnect(name) {
--- a/toolkit/components/extensions/ext-backgroundPage.js
+++ b/toolkit/components/extensions/ext-backgroundPage.js
@@ -7,35 +7,34 @@ XPCOMUtils.defineLazyModuleGetter(this, 
                                   "resource://gre/modules/AddonManager.jsm");
 
 // WeakMap[Extension -> BackgroundPage]
 var backgroundPagesMap = new WeakMap();
 
 // Responsible for the background_page section of the manifest.
 function BackgroundPage(options, extension) {
   this.extension = extension;
-  this.scripts = options.scripts || [];
   this.page = options.page || null;
+  this.isGenerated = !!options.scripts;
   this.contentWindow = null;
   this.chromeWebNav = null;
   this.webNav = null;
   this.context = null;
 }
 
 BackgroundPage.prototype = {
   build() {
     let chromeWebNav = Services.appShell.createWindowlessBrowser(true);
     this.chromeWebNav = chromeWebNav;
 
     let url;
     if (this.page) {
       url = this.extension.baseURI.resolve(this.page);
-    } else {
-      // TODO: Chrome uses "_generated_background_page.html" for this.
-      url = this.extension.baseURI.resolve("_blank.html");
+    } else if (this.isGenerated) {
+      url = this.extension.baseURI.resolve("_generated_background_page.html");
     }
 
     if (!this.extension.isExtensionURL(url)) {
       this.extension.manifestError("Background page must be a file within the extension");
       url = this.extension.baseURI.resolve("_blank.html");
     }
 
     let system = Services.scriptSecurityManager.getSystemPrincipal();
@@ -65,59 +64,24 @@ BackgroundPage.prototype = {
 
     if (this.extension.addonData.instanceID) {
       AddonManager.getAddonByInstanceID(this.extension.addonData.instanceID)
                   .then(addon => addon.setDebugGlobal(window));
     }
 
     // TODO: Right now we run onStartup after the background page
     // finishes. See if this is what Chrome does.
+    // TODO(robwu): This implementation of onStartup is wrong, see
+    // https://bugzilla.mozilla.org/show_bug.cgi?id=1247435#c1
     let loadListener = event => {
-      // Override the `alert()` method inside background windows;
-      // we alias it to console.log().
-      // See: https://bugzilla.mozilla.org/show_bug.cgi?id=1203394
-      let alertDisplayedWarning = false;
-      let alertOverwrite = text => {
-        if (!alertDisplayedWarning) {
-          let consoleWindow = Services.wm.getMostRecentWindow("devtools:webconsole");
-          if (!consoleWindow) {
-            let {require} = Cu.import("resource://devtools/shared/Loader.jsm", {});
-            require("devtools/client/framework/devtools-browser");
-            let hudservice = require("devtools/client/webconsole/hudservice");
-            hudservice.toggleBrowserConsole().catch(Cu.reportError);
-          } else {
-            // the Browser Console was already open
-            consoleWindow.focus();
-          }
-
-          this.contentWindow.console.warn("alert() is not supported in background windows; please use console.log instead.");
-
-          alertDisplayedWarning = true;
-        }
-
-        window.console.log(text);
-      };
-      Components.utils.exportFunction(alertOverwrite, window, {
-        defineAs: "alert",
-      });
       if (event.target != window.document) {
         return;
       }
       event.currentTarget.removeEventListener("load", loadListener, true);
 
-      if (this.scripts) {
-        let doc = window.document;
-        for (let script of this.scripts) {
-          let tag = doc.createElement("script");
-          tag.setAttribute("src", script);
-          tag.async = false;
-          doc.body.appendChild(tag);
-        }
-      }
-
       if (this.extension.onStartup) {
         this.extension.onStartup();
       }
     };
     browser.addEventListener("load", loadListener, true);
   },
 
   shutdown() {
--- a/toolkit/components/extensions/test/mochitest/mochitest.ini
+++ b/toolkit/components/extensions/test/mochitest/mochitest.ini
@@ -55,16 +55,18 @@ skip-if = buildapp == 'b2g' # runat != d
 [test_ext_idle.html]
 [test_ext_localStorage.html]
 [test_ext_onmessage_removelistener.html]
 [test_ext_notifications.html]
 [test_ext_permission_xhr.html]
 skip-if = buildapp == 'b2g' # JavaScript error: jar:remoteopenfile:///data/local/tmp/generated-extension.xpi!/content.js, line 46: NS_ERROR_ILLEGAL_VALUE:
 [test_ext_runtime_connect.html]
 skip-if = (os == 'android' || buildapp == 'b2g') # port.sender.tab is undefined on b2g. Bug 1258975 on android.
+[test_ext_runtime_connect_twoway.html]
+skip-if = (os == 'android' || buildapp == 'b2g') # port.sender.tab is undefined on b2g. Bug 1258975 on android.
 [test_ext_runtime_connect2.html]
 skip-if = (os == 'android' || buildapp == 'b2g') # port.sender.tab is undefined on b2g. Bug 1258975 on android.
 [test_ext_runtime_disconnect.html]
 [test_ext_runtime_getPlatformInfo.html]
 [test_ext_runtime_id.html]
 [test_ext_runtime_sendMessage.html]
 [test_ext_sandbox_var.html]
 [test_ext_sendmessage_reply.html]
@@ -80,16 +82,19 @@ skip-if = os == 'android' # Android does
 [test_ext_background_runtime_connect_params.html]
 [test_ext_cookies.html]
 [test_ext_bookmarks.html]
 skip-if = (os == 'android' || buildapp == 'b2g') # unimplemented api. Bug 1258975 on android.
 [test_ext_alarms.html]
 [test_ext_background_window_properties.html]
 [test_ext_background_sub_windows.html]
 [test_ext_background_api_injection.html]
+[test_ext_background_generated_url.html]
+[test_ext_background_generated_reload.html]
+[test_ext_background_generated_load_events.html]
 [test_ext_i18n.html]
 skip-if = (os == 'android') # Bug 1258975 on android.
 [test_ext_web_accessible_resources.html]
 skip-if = (os == 'android') # Bug 1258975 on android.
 [test_ext_webrequest.html]
 skip-if = (os == 'android' || buildapp == 'b2g') # webrequest api uninplemented (bug 1199504). Bug 1258975 on android.
 [test_ext_webnavigation.html]
 skip-if = (os == 'android' || buildapp == 'b2g') # needs TabManager which is not yet implemented. Bug 1258975 on android.
new file mode 100644
--- /dev/null
+++ b/toolkit/components/extensions/test/mochitest/test_ext_background_generated_load_events.html
@@ -0,0 +1,47 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Test load events in _generated_background_page.html</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="text/javascript" src="/tests/SimpleTest/SpawnTask.js"></script>
+  <script type="text/javascript" src="/tests/SimpleTest/ExtensionTestUtils.js"></script>
+  <script type="text/javascript" src="head.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css">
+</head>
+<body>
+
+<script>
+"use strict";
+
+/* eslint-disable mozilla/balanced-listeners */
+
+add_task(function* test_DOMContentLoaded_in_generated_background_page() {
+  function backgroundScript() {
+    function reportListener(event) {
+      browser.test.sendMessage("eventname", event.type);
+    }
+    document.addEventListener("DOMContentLoaded", reportListener);
+    window.addEventListener("load", reportListener);
+  }
+  let extension = ExtensionTestUtils.loadExtension({
+    manifest: {
+      background: {
+        scripts: ["bg.js"],
+      },
+      web_accessible_resources: ["_generated_background_page.html"],
+    },
+    files: {
+      "bg.js": `(${backgroundScript})();`,
+    },
+  });
+
+  yield extension.startup();
+  is("DOMContentLoaded", yield extension.awaitMessage("eventname"));
+  is("load", yield extension.awaitMessage("eventname"));
+
+  yield extension.unload();
+});
+
+</script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/toolkit/components/extensions/test/mochitest/test_ext_background_generated_reload.html
@@ -0,0 +1,50 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Test reload of _generated_background_page.html</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="text/javascript" src="/tests/SimpleTest/SpawnTask.js"></script>
+  <script type="text/javascript" src="/tests/SimpleTest/ExtensionTestUtils.js"></script>
+  <script type="text/javascript" src="head.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css">
+</head>
+<body>
+
+<script>
+"use strict";
+
+add_task(function* test_reload_generated_background_page() {
+  function backgroundScript() {
+    if (location.hash !== "#firstrun") {
+      browser.test.sendMessage("first run");
+      location.hash = "#firstrun";
+      browser.test.assertEq("#firstrun", location.hash);
+      location.reload();
+    } else {
+      browser.test.notifyPass("second run");
+    }
+  }
+  let extension = ExtensionTestUtils.loadExtension({
+    manifest: {
+      background: {
+        scripts: ["bg.js"],
+      },
+    },
+    files: {
+      "bg.js": `(${backgroundScript})();`,
+    },
+  });
+
+  yield extension.startup();
+  info("Waiting for first message");
+  yield extension.awaitMessage("first run");
+  info("Waiting for second message");
+  yield extension.awaitFinish("second run");
+  info("Received both messages");
+
+  yield extension.unload();
+});
+
+</script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/toolkit/components/extensions/test/mochitest/test_ext_background_generated_url.html
@@ -0,0 +1,47 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Test _generated_background_page.html</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="text/javascript" src="/tests/SimpleTest/SpawnTask.js"></script>
+  <script type="text/javascript" src="/tests/SimpleTest/ExtensionTestUtils.js"></script>
+  <script type="text/javascript" src="head.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css">
+</head>
+<body>
+
+<script>
+"use strict";
+
+add_task(function* test_url_of_generated_background_page() {
+  function backgroundScript() {
+    const EXPECTED_URL = browser.runtime.getURL("/_generated_background_page.html");
+    browser.test.assertEq(EXPECTED_URL, location.href);
+    browser.test.sendMessage("script done", EXPECTED_URL);
+  }
+  let extension = ExtensionTestUtils.loadExtension({
+    manifest: {
+      background: {
+        scripts: ["bg.js"],
+      },
+      web_accessible_resources: ["_generated_background_page.html"],
+    },
+    files: {
+      "bg.js": `(${backgroundScript})();`,
+    },
+  });
+
+  yield extension.startup();
+  const EXPECTED_URL = yield extension.awaitMessage("script done");
+
+  let win = window.open(EXPECTED_URL);
+  ok(win, "Should open new tab at URL: " + EXPECTED_URL);
+  yield extension.awaitMessage("script done");
+  win.close();
+
+  yield extension.unload();
+});
+
+</script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/toolkit/components/extensions/test/mochitest/test_ext_runtime_connect_twoway.html
@@ -0,0 +1,127 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>WebExtension test</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="text/javascript" src="/tests/SimpleTest/SpawnTask.js"></script>
+  <script type="text/javascript" src="/tests/SimpleTest/ExtensionTestUtils.js"></script>
+  <script type="text/javascript" src="head.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css">
+</head>
+<body>
+
+<script>
+"use strict";
+
+add_task(function* test_connect_bidirectionally_and_postMessage() {
+  function backgroundScript() {
+    let onConnectCount = 0;
+    browser.runtime.onConnect.addListener(port => {
+      // 3. onConnect by connect() from CS.
+      browser.test.assertEq("from-cs", port.name);
+      browser.test.assertEq(1, ++onConnectCount,
+          "BG onConnect should be called once");
+
+      let tabId = port.sender.tab.id;
+      browser.test.assertTrue(tabId, "content script must have a tab ID");
+
+      let port2;
+      let postMessageCount1 = 0;
+      port.onMessage.addListener(msg => {
+        // 11. port.onMessage by port.postMessage in CS.
+        browser.test.assertEq("from CS to port", msg);
+        browser.test.assertEq(1, ++postMessageCount1,
+          "BG port.onMessage should be called once");
+
+        // 12. should trigger port2.onMessage in CS.
+        port2.postMessage("from BG to port2");
+      });
+
+      // 4. Should trigger onConnect in CS.
+      port2 = browser.tabs.connect(tabId, {name: "from-bg"});
+      let postMessageCount2 = 0;
+      port2.onMessage.addListener(msg => {
+        // 7. onMessage by port2.postMessage in CS.
+        browser.test.assertEq("from CS to port2", msg);
+        browser.test.assertEq(1, ++postMessageCount2,
+          "BG port2.onMessage should be called once");
+
+        // 8. Should trigger port.onMessage in CS.
+        port.postMessage("from BG to port");
+      });
+    });
+
+    // 1. Notify test runner to create a new tab.
+    browser.test.sendMessage("ready");
+  }
+
+  function contentScript() {
+    let onConnectCount = 0;
+    let port;
+    browser.runtime.onConnect.addListener(port2 => {
+      // 5. onConnect by connect() from BG.
+      browser.test.assertEq("from-bg", port2.name);
+      browser.test.assertEq(1, ++onConnectCount,
+        "CS onConnect should be called once");
+
+      let postMessageCount2 = 0;
+      port2.onMessage.addListener(msg => {
+        // 12. port2.onMessage by port2.postMessage in BG.
+        browser.test.assertEq("from BG to port2", msg);
+        browser.test.assertEq(1, ++postMessageCount2,
+          "CS port2.onMessage should be called once");
+
+        // TODO(robwu): Do not explicitly disconnect, it should not be a problem
+        // if we keep the ports open. However, not closing the ports causes the
+        // test to fail with NS_ERROR_NOT_INITIALIZED in ExtensionUtils.jsm, in
+        // Port.prototype.disconnect (nsIMessageSender.sendAsyncMessage).
+        port.disconnect();
+        port2.disconnect();
+        browser.test.notifyPass("ping pong done");
+      });
+      // 6. should trigger port2.onMessage in BG.
+      port2.postMessage("from CS to port2");
+    });
+
+    // 2. should trigger onConnect in BG.
+    port = browser.runtime.connect({name: "from-cs"});
+    let postMessageCount1 = 0;
+    port.onMessage.addListener(msg => {
+      // 9. onMessage by port.postMessage in BG.
+      browser.test.assertEq("from BG to port", msg);
+      browser.test.assertEq(1, ++postMessageCount1,
+        "CS port.onMessage should be called once");
+
+      // 10. should trigger port.onMessage in BG.
+      port.postMessage("from CS to port");
+    });
+  }
+
+  let extensionData = {
+    background: `(${backgroundScript})();`,
+    manifest: {
+      content_scripts: [{
+        js: ["contentscript.js"],
+        matches: ["http://mochi.test/*/file_sample.html"],
+      }],
+    },
+    files: {
+      "contentscript.js": `(${contentScript})();`,
+    },
+  };
+
+  let extension = ExtensionTestUtils.loadExtension(extensionData);
+  yield extension.startup();
+  info("extension loaded");
+
+  yield extension.awaitMessage("ready");
+
+  let win = window.open("file_sample.html");
+  yield extension.awaitFinish("ping pong done");
+  win.close();
+
+  yield extension.unload();
+  info("extension unloaded");
+});
+</script>
+</body>
deleted file mode 100644
--- a/toolkit/components/microformats/Microformats.js
+++ /dev/null
@@ -1,1859 +0,0 @@
-/* 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/. */
-
-this.EXPORTED_SYMBOLS = ["Microformats", "adr", "tag", "hCard", "hCalendar", "geo"];
-
-this.Microformats = {
-  /* When a microformat is added, the name is placed in this list */
-  list: [],
-  /* Custom iterator so that microformats can be enumerated as */
-  /* for (i in Microformats) */
-  __iterator__: function* () {
-    for (let i=0; i < this.list.length; i++) {
-      yield this.list[i];
-    }
-  },
-  /**
-   * Retrieves microformats objects of the given type from a document
-   *
-   * @param  name          The name of the microformat (required)
-   * @param  rootElement   The DOM element at which to start searching (required)
-   * @param  options       Literal object with the following options:
-   *                       recurseExternalFrames - Whether or not to search child frames
-   *                       that reference external pages (with a src attribute)
-   *                       for microformats (optional - defaults to true)
-   *                       showHidden -  Whether or not to add hidden microformat
-   *                       (optional - defaults to false)
-   *                       debug - Whether or not we are in debug mode (optional
-   *                       - defaults to false)
-   * @param  targetArray  An array of microformat objects to which is added the results (optional)
-   * @return A new array of microformat objects or the passed in microformat
-   *         object array with the new objects added
-   */
-  get: function(name, rootElement, options, targetArray) {
-    function isAncestor(haystack, needle) {
-      var parent = needle;
-      while (parent = parent.parentNode) {
-        /* We need to check parentNode because defaultView.frames[i].frameElement */
-        /* isn't a real DOM node */
-        if (parent == needle.parentNode) {
-          return true;
-        }
-      }
-      return false;
-    }
-    if (!Microformats[name] || !rootElement) {
-      return undefined;
-    }
-    targetArray = targetArray || [];
-
-    /* Root element might not be the document - we need the document's default view */
-    /* to get frames and to check their ancestry */
-    var defaultView = rootElement.defaultView || rootElement.ownerDocument.defaultView;
-    var rootDocument = rootElement.ownerDocument || rootElement;
-
-    /* If recurseExternalFrames is undefined or true, look through all child frames for microformats */
-    if (!options || !options.hasOwnProperty("recurseExternalFrames") || options.recurseExternalFrames) {
-      if (defaultView && defaultView.frames.length > 0) {
-        for (let i=0; i < defaultView.frames.length; i++) {
-          if (isAncestor(rootDocument, defaultView.frames[i].frameElement)) {
-            Microformats.get(name, defaultView.frames[i].document, options, targetArray);
-          }
-        }
-      }
-    }
-
-    /* Get the microformat nodes for the document */
-    var microformatNodes = [];
-    if (Microformats[name].className) {
-      microformatNodes = Microformats.getElementsByClassName(rootElement,
-                                        Microformats[name].className);
-      /* alternateClassName is for cases where a parent microformat is inferred by the children */
-      /* If we find alternateClassName, the entire document becomes the microformat */
-      if ((microformatNodes.length == 0) && Microformats[name].alternateClassName) {
-        var altClass = Microformats.getElementsByClassName(rootElement, Microformats[name].alternateClassName);
-        if (altClass.length > 0) {
-          microformatNodes.push(rootElement);
-        }
-      }
-    } else if (Microformats[name].attributeValues) {
-      microformatNodes =
-        Microformats.getElementsByAttribute(rootElement,
-                                            Microformats[name].attributeName,
-                                            Microformats[name].attributeValues);
-
-    }
-
-
-    function isVisible(node, checkChildren) {
-      if (node.getBoundingClientRect) {
-        var box = node.getBoundingClientRect();
-      } else {
-        box = node.ownerDocument.getBoxObjectFor(node);
-      }
-      /* If the parent has is an empty box, double check the children */
-      if ((box.height == 0) || (box.width == 0)) {
-        if (checkChildren && node.childNodes.length > 0) {
-          for(let i=0; i < node.childNodes.length; i++) {
-            if (node.childNodes[i].nodeType == Components.interfaces.nsIDOMNode.ELEMENT_NODE) {
-              /* For performance reasons, we only go down one level */
-              /* of children */
-              if (isVisible(node.childNodes[i], false)) {
-                return true;
-              }
-            }
-          }
-        }
-        return false
-      }
-      return true;
-    }
-
-    /* Create objects for the microformat nodes and put them into the microformats */
-    /* array */
-    for (let i = 0; i < microformatNodes.length; i++) {
-      /* If showHidden undefined or false, don't add microformats to the list that aren't visible */
-      if (!options || !options.hasOwnProperty("showHidden") || !options.showHidden) {
-        if (microformatNodes[i].ownerDocument) {
-          if (!isVisible(microformatNodes[i], true)) {
-            continue;
-          }
-        }
-      }
-      try {
-        if (options && options.debug) {
-          /* Don't validate in the debug case so that we don't get errors thrown */
-          /* in the debug case, we want all microformats, even if they are invalid */
-          targetArray.push(new Microformats[name].mfObject(microformatNodes[i], false));
-        } else {
-          targetArray.push(new Microformats[name].mfObject(microformatNodes[i], true));
-        }
-      } catch (ex) {
-        /* Creation of individual object probably failed because it is invalid. */
-        /* This isn't a problem, because the page might have invalid microformats */
-      }
-    }
-    return targetArray;
-  },
-  /**
-   * Counts microformats objects of the given type from a document
-   *
-   * @param  name          The name of the microformat (required)
-   * @param  rootElement   The DOM element at which to start searching (required)
-   * @param  options       Literal object with the following options:
-   *                       recurseExternalFrames - Whether or not to search child frames
-   *                       that reference external pages (with a src attribute)
-   *                       for microformats (optional - defaults to true)
-   *                       showHidden -  Whether or not to add hidden microformat
-   *                       (optional - defaults to false)
-   *                       debug - Whether or not we are in debug mode (optional
-   *                       - defaults to false)
-   * @return The new count
-   */
-  count: function(name, rootElement, options) {
-    var mfArray = Microformats.get(name, rootElement, options);
-    if (mfArray) {
-      return mfArray.length;
-    }
-    return 0;
-  },
-  /**
-   * Returns true if the passed in node is a microformat. Does NOT return true
-   * if the passed in node is a child of a microformat.
-   *
-   * @param  node          DOM node to check
-   * @return true if the node is a microformat, false if it is not
-   */
-  isMicroformat: function(node) {
-    for (let i in Microformats)
-    {
-      if (Microformats[i].className) {
-        if (Microformats.matchClass(node, Microformats[i].className)) {
-            return true;
-        }
-      } else {
-        var attribute;
-        if (attribute = node.getAttribute(Microformats[i].attributeName)) {
-          var attributeList = Microformats[i].attributeValues.split(" ");
-          for (let j=0; j < attributeList.length; j++) {
-            if (attribute.match("(^|\\s)" + attributeList[j] + "(\\s|$)")) {
-              return true;
-            }
-          }
-        }
-      }
-    }
-    return false;
-  },
-  /**
-   * This function searches a given nodes ancestors looking for a microformat
-   * and if it finds it, returns it. It does NOT include self, so if the passed
-   * in node is a microformat, it will still search ancestors for a microformat.
-   *
-   * @param  node          DOM node to check
-   * @return If the node is contained in a microformat, it returns the parent
-   *         DOM node, otherwise returns null
-   */
-  getParent: function(node) {
-    var xpathExpression;
-    var xpathResult;
-
-    xpathExpression = "ancestor::*[";
-    for (let i=0; i < Microformats.list.length; i++) {
-      var mfname = Microformats.list[i];
-      if (i != 0) {
-        xpathExpression += " or ";
-      }
-      if (Microformats[mfname].className) {
-        xpathExpression += "contains(concat(' ', @class, ' '), ' " + Microformats[mfname].className + " ')";
-      } else {
-        var attributeList = Microformats[mfname].attributeValues.split(" ");
-        for (let j=0; j < attributeList.length; j++) {
-          if (j != 0) {
-            xpathExpression += " or ";
-          }
-          xpathExpression += "contains(concat(' ', @" + Microformats[mfname].attributeName + ", ' '), ' " + attributeList[j] + " ')";
-        }
-      }
-    }
-    xpathExpression += "][1]";
-    xpathResult = (node.ownerDocument || node).evaluate(xpathExpression, node, null,  Components.interfaces.nsIDOMXPathResult.FIRST_ORDERED_NODE_TYPE, null);
-    if (xpathResult.singleNodeValue) {
-      xpathResult.singleNodeValue.microformat = mfname;
-      return xpathResult.singleNodeValue;
-    }
-    return null;
-  },
-  /**
-   * If the passed in node is a microformat, this function returns a space
-   * separated list of the microformat names that correspond to this node
-   *
-   * @param  node          DOM node to check
-   * @return If the node is a microformat, a space separated list of microformat
-   *         names, otherwise returns nothing
-   */
-  getNamesFromNode: function(node) {
-    var microformatNames = [];
-    var xpathExpression;
-    var xpathResult;
-    for (let i in Microformats)
-    {
-      if (Microformats[i]) {
-        if (Microformats[i].className) {
-          if (Microformats.matchClass(node, Microformats[i].className)) {
-            microformatNames.push(i);
-            continue;
-          }
-        } else if (Microformats[i].attributeValues) {
-          var attribute;
-          if (attribute = node.getAttribute(Microformats[i].attributeName)) {
-            var attributeList = Microformats[i].attributeValues.split(" ");
-            for (let j=0; j < attributeList.length; j++) {
-              /* If we match any attribute, we've got a microformat */
-              if (attribute.match("(^|\\s)" + attributeList[j] + "(\\s|$)")) {
-                microformatNames.push(i);
-                break;
-              }
-            }
-          }
-        }
-      }
-    }
-    return microformatNames.join(" ");
-  },
-  /**
-   * Outputs the contents of a microformat object for debug purposes.
-   *
-   * @param  microformatObject JavaScript object that represents a microformat
-   * @return string containing a visual representation of the contents of the microformat
-   */
-  debug: function debug(microformatObject) {
-    function dumpObject(item, indent)
-    {
-      if (!indent) {
-        indent = "";
-      }
-      var toreturn = "";
-      var testArray = [];
-
-      for (let i in item)
-      {
-        if (testArray[i]) {
-          continue;
-        }
-        if (typeof item[i] == "object") {
-          if ((i != "node") && (i != "resolvedNode")) {
-            if (item[i] && item[i].semanticType) {
-              toreturn += indent + item[i].semanticType + " [" + i + "] { \n";
-            } else {
-              toreturn += indent + "object " + i + " { \n";
-            }
-            toreturn += dumpObject(item[i], indent + "\t");
-            toreturn += indent + "}\n";
-          }
-        } else if ((typeof item[i] != "function") && (i != "semanticType")) {
-          if (item[i]) {
-            toreturn += indent + i + "=" + item[i] + "\n";
-          }
-        }
-      }
-      if (!toreturn && item) {
-        toreturn = item.toString();
-      }
-      return toreturn;
-    }
-    return dumpObject(microformatObject);
-  },
-  add: function add(microformat, microformatDefinition) {
-    /* We always replace an existing definition with the new one */
-    if (!Microformats[microformat]) {
-      Microformats.list.push(microformat);
-    }
-    Microformats[microformat] = microformatDefinition;
-    microformatDefinition.mfObject.prototype.debug =
-      function(microformatObject) {
-        return Microformats.debug(microformatObject)
-      };
-  },
-  remove: function remove(microformat) {
-    if (Microformats[microformat]) {
-      var list = Microformats.list;
-      var index = list.indexOf(microformat, 1);
-      if (index != -1) {
-        list.splice(index, 1);
-      }
-      delete Microformats[microformat];
-    }
-  },
-  /* All parser specific functions are contained in this object */
-  parser: {
-    /**
-     * Uses the microformat patterns to decide what the correct text for a
-     * given microformat property is. This includes looking at things like
-     * abbr, img/alt, area/alt and value excerpting.
-     *
-     * @param  propnode   The DOMNode to check
-     * @param  parentnode The parent node of the property. If it is a subproperty,
-     *                    this is the parent property node. If it is not, this is the
-     *                    microformat node.
-     & @param  datatype   HTML/text - whether to use innerHTML or innerText - defaults to text
-     * @return A string with the value of the property
-     */
-    defaultGetter: function(propnode, parentnode, datatype) {
-      function collapseWhitespace(instring) {
-        /* Remove new lines, carriage returns and tabs */
-        outstring = instring.replace(/[\n\r\t]/gi, ' ');
-        /* Replace any double spaces with single spaces */
-        outstring = outstring.replace(/\s{2,}/gi, ' ');
-        /* Remove any double spaces that are left */
-        outstring = outstring.replace(/\s{2,}/gi, '');
-        /* Remove any spaces at the beginning */
-        outstring = outstring.replace(/^\s+/, '');
-        /* Remove any spaces at the end */
-        outstring = outstring.replace(/\s+$/, '');
-        return outstring;
-      }
-
-
-      if (((((propnode.localName.toLowerCase() == "abbr") || (propnode.localName.toLowerCase() == "html:abbr")) && !propnode.namespaceURI) ||
-         ((propnode.localName.toLowerCase() == "abbr") && (propnode.namespaceURI == "http://www.w3.org/1999/xhtml"))) && (propnode.hasAttribute("title"))) {
-        return propnode.getAttribute("title");
-      } else if ((propnode.nodeName.toLowerCase() == "img") && (propnode.hasAttribute("alt"))) {
-        return propnode.getAttribute("alt");
-      } else if ((propnode.nodeName.toLowerCase() == "area") && (propnode.hasAttribute("alt"))) {
-        return propnode.getAttribute("alt");
-      } else if ((propnode.nodeName.toLowerCase() == "textarea") ||
-                 (propnode.nodeName.toLowerCase() == "select") ||
-                 (propnode.nodeName.toLowerCase() == "input")) {
-        return propnode.value;
-      } else {
-        var values = Microformats.getElementsByClassName(propnode, "value");
-        /* Verify that values are children of the propnode */
-        for (let i = values.length-1; i >= 0; i--) {
-          if (values[i].parentNode != propnode) {
-            values.splice(i,1);
-          }
-        }
-        if (values.length > 0) {
-          var value = "";
-          for (let j=0;j<values.length;j++) {
-            value += Microformats.parser.defaultGetter(values[j], propnode, datatype);
-          }
-          return collapseWhitespace(value);
-        }
-        var s;
-        if (datatype == "HTML") {
-          s = propnode.innerHTML;
-        } else {
-          if (propnode.innerText) {
-            s = propnode.innerText;
-          } else {
-            s = propnode.textContent;
-          }
-        }
-        /* If we are processing a value node, don't remove whitespace now */
-        /* (we'll do it later) */
-        if (!Microformats.matchClass(propnode, "value")) {
-          s = collapseWhitespace(s);
-        }
-        if (s.length > 0) {
-          return s;
-        }
-      }
-      return undefined;
-    },
-    /**
-     * Used to specifically retrieve a date in a microformat node.
-     * After getting the default text, it normalizes it to an ISO8601 date.
-     *
-     * @param  propnode   The DOMNode to check
-     * @param  parentnode The parent node of the property. If it is a subproperty,
-     *                    this is the parent property node. If it is not, this is the
-     *                    microformat node.
-     * @return A string with the normalized date.
-     */
-    dateTimeGetter: function(propnode, parentnode) {
-      var date = Microformats.parser.textGetter(propnode, parentnode);
-      if (date) {
-        return Microformats.parser.normalizeISO8601(date);
-      }
-      return undefined;
-    },
-    /**
-     * Used to specifically retrieve a URI in a microformat node. This includes
-     * looking at an href/img/object/area to get the fully qualified URI.
-     *
-     * @param  propnode   The DOMNode to check
-     * @param  parentnode The parent node of the property. If it is a subproperty,
-     *                    this is the parent property node. If it is not, this is the
-     *                    microformat node.
-     * @return A string with the fully qualified URI.
-     */
-    uriGetter: function(propnode, parentnode) {
-      var pairs = {"a":"href", "img":"src", "object":"data", "area":"href"};
-      var name = propnode.nodeName.toLowerCase();
-      if (pairs.hasOwnProperty(name)) {
-        return propnode[pairs[name]];
-      }
-      return Microformats.parser.textGetter(propnode, parentnode);
-    },
-    /**
-     * Used to specifically retrieve a telephone number in a microformat node.
-     * Basically this is to handle the face that telephone numbers use value
-     * as the name as one of their subproperties, but value is also used for
-     * value excerpting (http://microformats.org/wiki/hcard#Value_excerpting)
-
-     * @param  propnode   The DOMNode to check
-     * @param  parentnode The parent node of the property. If it is a subproperty,
-     *                    this is the parent property node. If it is not, this is the
-     *                    microformat node.
-     * @return A string with the telephone number
-     */
-    telGetter: function(propnode, parentnode) {
-      var pairs = {"a":"href", "object":"data", "area":"href"};
-      var name = propnode.nodeName.toLowerCase();
-      if (pairs.hasOwnProperty(name)) {
-        var protocol;
-        if (propnode[pairs[name]].indexOf("tel:") == 0) {
-          protocol = "tel:";
-        }
-        if (propnode[pairs[name]].indexOf("fax:") == 0) {
-          protocol = "fax:";
-        }
-        if (propnode[pairs[name]].indexOf("modem:") == 0) {
-          protocol = "modem:";
-        }
-        if (protocol) {
-          if (propnode[pairs[name]].indexOf('?') > 0) {
-            return unescape(propnode[pairs[name]].substring(protocol.length, propnode[pairs[name]].indexOf('?')));
-          } else {
-            return unescape(propnode[pairs[name]].substring(protocol.length));
-          }
-        }
-      }
-     /* Special case - if this node is a value, use the parent node to get all the values */
-      if (Microformats.matchClass(propnode, "value")) {
-        return Microformats.parser.textGetter(parentnode, parentnode);
-      } else {
-        /* Virtual case */
-        if (!parentnode && (Microformats.getElementsByClassName(propnode, "type").length > 0)) {
-          var tempNode = propnode.cloneNode(true);
-          var typeNodes = Microformats.getElementsByClassName(tempNode, "type");
-          for (let i=0; i < typeNodes.length; i++) {
-            typeNodes[i].parentNode.removeChild(typeNodes[i]);
-          }
-          return Microformats.parser.textGetter(tempNode);
-        }
-        return Microformats.parser.textGetter(propnode, parentnode);
-      }
-    },
-    /**
-     * Used to specifically retrieve an email address in a microformat node.
-     * This includes at an href, as well as removing subject if specified and
-     * the mailto prefix.
-     *
-     * @param  propnode   The DOMNode to check
-     * @param  parentnode The parent node of the property. If it is a subproperty,
-     *                    this is the parent property node. If it is not, this is the
-     *                    microformat node.
-     * @return A string with the email address.
-     */
-    emailGetter: function(propnode, parentnode) {
-      if ((propnode.nodeName.toLowerCase() == "a") || (propnode.nodeName.toLowerCase() == "area")) {
-        var mailto = propnode.href;
-        /* IO Service won't fully parse mailto, so we do it manually */
-        if (mailto.indexOf('?') > 0) {
-          return unescape(mailto.substring("mailto:".length, mailto.indexOf('?')));
-        } else {
-          return unescape(mailto.substring("mailto:".length));
-        }
-      } else {
-        /* Special case - if this node is a value, use the parent node to get all the values */
-        /* If this case gets executed, per the value design pattern, the result */
-        /* will be the EXACT email address with no extra parsing required */
-        if (Microformats.matchClass(propnode, "value")) {
-          return Microformats.parser.textGetter(parentnode, parentnode);
-        } else {
-          /* Virtual case */
-          if (!parentnode && (Microformats.getElementsByClassName(propnode, "type").length > 0)) {
-            var tempNode = propnode.cloneNode(true);
-            var typeNodes = Microformats.getElementsByClassName(tempNode, "type");
-            for (let i=0; i < typeNodes.length; i++) {
-              typeNodes[i].parentNode.removeChild(typeNodes[i]);
-            }
-            return Microformats.parser.textGetter(tempNode);
-          }
-          return Microformats.parser.textGetter(propnode, parentnode);
-        }
-      }
-    },
-    /**
-     * Used when a caller needs the text inside a particular DOM node.
-     * It calls defaultGetter to handle all the subtleties of getting
-     * text from a microformat.
-     *
-     * @param  propnode   The DOMNode to check
-     * @param  parentnode The parent node of the property. If it is a subproperty,
-     *                    this is the parent property node. If it is not, this is the
-     *                    microformat node.
-     * @return A string with just the text including all tags.
-     */
-    textGetter: function(propnode, parentnode) {
-      return Microformats.parser.defaultGetter(propnode, parentnode, "text");
-    },
-    /**
-     * Used when a caller needs the HTML inside a particular DOM node.
-     *
-     * @param  propnode   The DOMNode to check
-     * @param  parentnode The parent node of the property. If it is a subproperty,
-     *                    this is the parent property node. If it is not, this is the
-     *                    microformat node.
-     * @return An emulated string object that also has a new function called toHTML
-     */
-    HTMLGetter: function(propnode, parentnode) {
-      /* This is so we can have a string that behaves like a string */
-      /* but also has a new function that can return the HTML that corresponds */
-      /* to the string. */
-      function mfHTML(value) {
-        this.valueOf = function() {return value ? value.valueOf() : "";}
-        this.toString = function() {return value ? value.toString() : "";}
-      }
-      mfHTML.prototype = new String;
-      mfHTML.prototype.toHTML = function() {
-        return Microformats.parser.defaultGetter(propnode, parentnode, "HTML");
-      }
-      return new mfHTML(Microformats.parser.defaultGetter(propnode, parentnode, "text"));
-    },
-    /**
-     * Internal parser API used to determine which getter to call based on the
-     * datatype specified in the microformat definition.
-     *
-     * @param  prop       The microformat property in the definition
-     * @param  propnode   The DOMNode to check
-     * @param  parentnode The parent node of the property. If it is a subproperty,
-     *                    this is the parent property node. If it is not, this is the
-     *                    microformat node.
-     * @return A string with the property value.
-     */
-    datatypeHelper: function(prop, node, parentnode) {
-      var result;
-      var datatype = prop.datatype;
-      switch (datatype) {
-        case "dateTime":
-          result = Microformats.parser.dateTimeGetter(node, parentnode);
-          break;
-        case "anyURI":
-          result = Microformats.parser.uriGetter(node, parentnode);
-          break;
-        case "email":
-          result = Microformats.parser.emailGetter(node, parentnode);
-          break;
-        case "tel":
-          result = Microformats.parser.telGetter(node, parentnode);
-          break;
-        case "HTML":
-          result = Microformats.parser.HTMLGetter(node, parentnode);
-          break;
-        case "float":
-          var asText = Microformats.parser.textGetter(node, parentnode);
-          if (!isNaN(asText)) {
-            result = parseFloat(asText);
-          }
-          break;
-        case "custom":
-          result = prop.customGetter(node, parentnode);
-          break;
-        case "microformat":
-          try {
-            result = new Microformats[prop.microformat].mfObject(node, true);
-          } catch (ex) {
-            /* There are two reasons we get here, one because the node is not */
-            /* a microformat and two because the node is a microformat and */
-            /* creation failed. If the node is not a microformat, we just fall */
-            /* through and use the default getter since there are some cases */
-            /* (location in hCalendar) where a property can be either a microformat */
-            /* or a string. If creation failed, we break and simply don't add the */
-            /* microformat property to the parent microformat */
-            if (ex != "Node is not a microformat (" + prop.microformat + ")") {
-              break;
-            }
-          }
-          if (result != undefined) {
-            if (prop.microformat_property) {
-              result = result[prop.microformat_property];
-            }
-            break;
-          }
-        default:
-          result = Microformats.parser.textGetter(node, parentnode);
-          break;
-      }
-      /* This handles the case where one property implies another property */
-      /* For instance, org by itself is actually org.organization-name */
-      if (prop.values && (result != undefined)) {
-        var validType = false;
-        for (let value in prop.values) {
-          if (result.toLowerCase() == prop.values[value]) {
-            result = result.toLowerCase();
-            validType = true;
-            break;
-          }
-        }
-        if (!validType) {
-          return undefined;
-        }
-      }
-      return result;
-    },
-    newMicroformat: function(object, in_node, microformat, validate) {
-      /* check to see if we are even valid */
-      if (!Microformats[microformat]) {
-        throw("Invalid microformat - " + microformat);
-      }
-      if (in_node.ownerDocument) {
-        if (Microformats[microformat].attributeName) {
-          if (!(in_node.hasAttribute(Microformats[microformat].attributeName))) {
-            throw("Node is not a microformat (" + microformat + ")");
-          }
-        } else {
-          if (!Microformats.matchClass(in_node, Microformats[microformat].className)) {
-            throw("Node is not a microformat (" + microformat + ")");
-          }
-        }
-      }
-      var node = in_node;
-      if ((Microformats[microformat].className) && in_node.ownerDocument) {
-        node = Microformats.parser.preProcessMicroformat(in_node);
-      }
-
-      for (let i in Microformats[microformat].properties) {
-        object.__defineGetter__(i, Microformats.parser.getMicroformatPropertyGenerator(node, microformat, i, object));
-      }
-
-      /* The node in the object should be the original node */
-      object.node = in_node;
-      /* we also store the node that has been "resolved" */
-      object.resolvedNode = node;
-      object.semanticType = microformat;
-      if (validate) {
-        Microformats.parser.validate(node, microformat);
-      }
-    },
-    getMicroformatPropertyGenerator: function getMicroformatPropertyGenerator(node, name, property, microformat)
-    {
-      return function() {
-        var result = Microformats.parser.getMicroformatProperty(node, name, property);
-//        delete microformat[property];
-//        microformat[property] = result;
-        return result;
-      };
-    },
-    getPropertyInternal: function getPropertyInternal(propnode, parentnode, propobj, propname, mfnode) {
-      var result;
-      if (propobj.subproperties) {
-        for (let subpropname in propobj.subproperties) {
-          var subpropnodes;
-          var subpropobj = propobj.subproperties[subpropname];
-          if (subpropobj.rel == true) {
-            subpropnodes = Microformats.getElementsByAttribute(propnode, "rel", subpropname);
-          } else {
-            subpropnodes = Microformats.getElementsByClassName(propnode, subpropname);
-          }
-          var resultArray = [];
-          var subresult;
-          for (let i = 0; i < subpropnodes.length; i++) {
-            subresult = Microformats.parser.getPropertyInternal(subpropnodes[i], propnode,
-                                                                subpropobj,
-                                                                subpropname, mfnode);
-            if (subresult != undefined) {
-              resultArray.push(subresult);
-              /* If we're not a plural property, don't bother getting more */
-              if (!subpropobj.plural) {
-                break;
-              }
-            }
-          }
-          if (resultArray.length == 0) {
-            subresult = Microformats.parser.getPropertyInternal(propnode, null,
-                                                                subpropobj,
-                                                                subpropname, mfnode);
-            if (subresult != undefined) {
-              resultArray.push(subresult);
-            }
-          }
-          if (resultArray.length > 0) {
-            result = result || {};
-            if (subpropobj.plural) {
-              result[subpropname] = resultArray;
-            } else {
-              result[subpropname] = resultArray[0];
-            }
-          }
-        }
-      }
-      if (!parentnode || (!result && propobj.subproperties)) {
-        if (propobj.virtual) {
-          if (propobj.virtualGetter) {
-            result = propobj.virtualGetter(mfnode || propnode);
-          } else {
-            result = Microformats.parser.datatypeHelper(propobj, propnode);
-          }
-        }
-      } else if (!result) {
-        result = Microformats.parser.datatypeHelper(propobj, propnode, parentnode);
-      }
-      return result;
-    },
-    getMicroformatProperty: function getMicroformatProperty(in_mfnode, mfname, propname) {
-      var mfnode = in_mfnode;
-      /* If the node has not been preprocessed, the requested microformat */
-      /* is a class based microformat and the passed in node is not the */
-      /* entire document, preprocess it. Preprocessing the node involves */
-      /* creating a duplicate of the node and taking care of things like */
-      /* the include and header design patterns */
-      if (!in_mfnode.origNode && Microformats[mfname].className && in_mfnode.ownerDocument) {
-        mfnode = Microformats.parser.preProcessMicroformat(in_mfnode);
-      }
-      /* propobj is the corresponding property object in the microformat */
-      var propobj;
-      /* If there is a corresponding property in the microformat, use it */
-      if (Microformats[mfname].properties[propname]) {
-        propobj = Microformats[mfname].properties[propname];
-      } else {
-        /* If we didn't get a property, bail */
-        return undefined;
-      }
-      /* Query the correct set of nodes (rel or class) based on the setting */
-      /* in the property */
-      var propnodes;
-      if (propobj.rel == true) {
-        propnodes = Microformats.getElementsByAttribute(mfnode, "rel", propname);
-      } else {
-        propnodes = Microformats.getElementsByClassName(mfnode, propname);
-      }
-      for (let i=propnodes.length-1; i >= 0; i--) {
-        /* The reason getParent is not used here is because this code does */
-        /* not apply to attribute based microformats, plus adr and geo */
-        /* when contained in hCard are a special case */
-        var parentnode;
-        var node = propnodes[i];
-        var xpathExpression = "";
-        for (let j=0; j < Microformats.list.length; j++) {
-          /* Don't treat adr or geo in an hCard as a microformat in this case */
-          if ((mfname == "hCard") && ((Microformats.list[j] == "adr") || (Microformats.list[j] == "geo"))) {
-            continue;
-          }
-          if (Microformats[Microformats.list[j]].className) {
-            if (xpathExpression.length == 0) {
-              xpathExpression = "ancestor::*[";
-            } else {
-              xpathExpression += " or ";
-            }
-            xpathExpression += "contains(concat(' ', @class, ' '), ' " + Microformats[Microformats.list[j]].className + " ')";
-          }
-        }
-        xpathExpression += "][1]";
-        var xpathResult = (node.ownerDocument || node).evaluate(xpathExpression, node, null,  Components.interfaces.nsIDOMXPathResult.FIRST_ORDERED_NODE_TYPE, null);
-        if (xpathResult.singleNodeValue) {
-          xpathResult.singleNodeValue.microformat = mfname;
-          parentnode = xpathResult.singleNodeValue;
-        }
-        /* If the propnode is not a child of the microformat, and */
-        /* the property belongs to the parent microformat as well, */
-        /* remove it. */
-        if (parentnode != mfnode) {
-          var mfNameString = Microformats.getNamesFromNode(parentnode);
-          var mfNames = mfNameString.split(" ");
-          var j;
-          for (j=0; j < mfNames.length; j++) {
-            /* If this property is in the parent microformat, remove the node  */
-            if (Microformats[mfNames[j]].properties[propname]) {
-              propnodes.splice(i,1);
-              break;
-            }
-          }
-        }
-      }
-      if (propnodes.length > 0) {
-        var resultArray = [];
-        for (let i = 0; i < propnodes.length; i++) {
-          var subresult = Microformats.parser.getPropertyInternal(propnodes[i],
-                                                                  mfnode,
-                                                                  propobj,
-                                                                  propname);
-          if (subresult != undefined) {
-            resultArray.push(subresult);
-            /* If we're not a plural property, don't bother getting more */
-            if (!propobj.plural) {
-              return resultArray[0];
-            }
-          }
-        }
-        if (resultArray.length > 0) {
-          return resultArray;
-        }
-      } else {
-        /* If we didn't find any class nodes, check to see if this property */
-        /* is virtual and if so, call getPropertyInternal again */
-        if (propobj.virtual) {
-          return Microformats.parser.getPropertyInternal(mfnode, null,
-                                                         propobj, propname);
-        }
-      }
-      return undefined;
-    },
-    /**
-     * Internal parser API used to resolve includes and headers. Includes are
-     * resolved by simply cloning the node and replacing it in a clone of the
-     * original DOM node. Headers are resolved by creating a span and then copying
-     * the innerHTML and the class name.
-     *
-     * @param  in_mfnode The node to preProcess.
-     * @return If the node had includes or headers, a cloned node otherwise
-     *         the original node. You can check to see if the node was cloned
-     *         by looking for .origNode in the new node.
-     */
-    preProcessMicroformat: function preProcessMicroformat(in_mfnode) {
-      var mfnode;
-      if ((in_mfnode.nodeName.toLowerCase() == "td") && (in_mfnode.hasAttribute("headers"))) {
-        mfnode = in_mfnode.cloneNode(true);
-        mfnode.origNode = in_mfnode;
-        var headers = in_mfnode.getAttribute("headers").split(" ");
-        for (let i = 0; i < headers.length; i++) {
-          var tempNode = in_mfnode.ownerDocument.createElement("span");
-          var headerNode = in_mfnode.ownerDocument.getElementById(headers[i]);
-          if (headerNode) {
-            tempNode.innerHTML = headerNode.innerHTML;
-            tempNode.className = headerNode.className;
-            mfnode.appendChild(tempNode);
-          }
-        }
-      } else {
-        mfnode = in_mfnode;
-      }
-      var includes = Microformats.getElementsByClassName(mfnode, "include");
-      if (includes.length > 0) {
-        /* If we didn't clone, clone now */
-        if (!mfnode.origNode) {
-          mfnode = in_mfnode.cloneNode(true);
-          mfnode.origNode = in_mfnode;
-        }
-        includes = Microformats.getElementsByClassName(mfnode, "include");
-        var includeId;
-        var include_length = includes.length;
-        for (let i = include_length -1; i >= 0; i--) {
-          if (includes[i].nodeName.toLowerCase() == "a") {
-            includeId = includes[i].getAttribute("href").substr(1);
-          }
-          if (includes[i].nodeName.toLowerCase() == "object") {
-            includeId = includes[i].getAttribute("data").substr(1);
-          }
-          if (in_mfnode.ownerDocument.getElementById(includeId)) {
-            includes[i].parentNode.replaceChild(in_mfnode.ownerDocument.getElementById(includeId).cloneNode(true), includes[i]);
-          }
-        }
-      }
-      return mfnode;
-    },
-    validate: function validate(mfnode, mfname) {
-      var error = "";
-      if (Microformats[mfname].validate) {
-        return Microformats[mfname].validate(mfnode);
-      } else if (Microformats[mfname].required) {
-        for (let i=0;i<Microformats[mfname].required.length;i++) {
-          if (!Microformats.parser.getMicroformatProperty(mfnode, mfname, Microformats[mfname].required[i])) {
-            error += "Required property " + Microformats[mfname].required[i] + " not specified\n";
-          }
-        }
-        if (error.length > 0) {
-          throw(error);
-        }
-        return true;
-      }
-      return undefined;
-    },
-    /* This function normalizes an ISO8601 date by adding punctuation and */
-    /* ensuring that hours and seconds have values */
-    normalizeISO8601: function normalizeISO8601(string)
-    {
-      var dateArray = string.match(/(\d\d\d\d)(?:-?(\d\d)(?:-?(\d\d)(?:[T ](\d\d)(?::?(\d\d)(?::?(\d\d)(?:\.(\d+))?)?)?(?:([-+Z])(?:(\d\d)(?::?(\d\d))?)?)?)?)?)?/);
-
-      var dateString;
-      var tzOffset = 0;
-      if (!dateArray) {
-        return undefined;
-      }
-      if (dateArray[1]) {
-        dateString = dateArray[1];
-        if (dateArray[2]) {
-          dateString += "-" + dateArray[2];
-          if (dateArray[3]) {
-            dateString += "-" + dateArray[3];
-            if (dateArray[4]) {
-              dateString += "T" + dateArray[4];
-              if (dateArray[5]) {
-                dateString += ":" + dateArray[5];
-              } else {
-                dateString += ":" + "00";
-              }
-              if (dateArray[6]) {
-                dateString += ":" + dateArray[6];
-              } else {
-                dateString += ":" + "00";
-              }
-              if (dateArray[7]) {
-                dateString += "." + dateArray[7];
-              }
-              if (dateArray[8]) {
-                dateString += dateArray[8];
-                if ((dateArray[8] == "+") || (dateArray[8] == "-")) {
-                  if (dateArray[9]) {
-                    dateString += dateArray[9];
-                    if (dateArray[10]) {
-                      dateString += dateArray[10];
-                    }
-                  }
-                }
-              }
-            }
-          }
-        }
-      }
-      return dateString;
-    }
-  },
-  /**
-   * Converts an ISO8601 date into a JavaScript date object, honoring the TZ
-   * offset and Z if present to convert the date to local time
-   * NOTE: I'm using an extra parameter on the date object for this function.
-   * I set date.time to true if there is a date, otherwise date.time is false.
-   *
-   * @param  string ISO8601 formatted date
-   * @return JavaScript date object that represents the ISO date.
-   */
-  dateFromISO8601: function dateFromISO8601(string) {
-    var dateArray = string.match(/(\d\d\d\d)(?:-?(\d\d)(?:-?(\d\d)(?:[T ](\d\d)(?::?(\d\d)(?::?(\d\d)(?:\.(\d+))?)?)?(?:([-+Z])(?:(\d\d)(?::?(\d\d))?)?)?)?)?)?/);
-
-    var date = new Date(dateArray[1], 0, 1);
-    date.time = false;
-
-    if (dateArray[2]) {
-      date.setMonth(dateArray[2] - 1);
-    }
-    if (dateArray[3]) {
-      date.setDate(dateArray[3]);
-    }
-    if (dateArray[4]) {
-      date.setHours(dateArray[4]);
-      date.time = true;
-      if (dateArray[5]) {
-        date.setMinutes(dateArray[5]);
-        if (dateArray[6]) {
-          date.setSeconds(dateArray[6]);
-          if (dateArray[7]) {
-            date.setMilliseconds(Number("0." + dateArray[7]) * 1000);
-          }
-        }
-      }
-    }
-    if (dateArray[8]) {
-      if (dateArray[8] == "-") {
-        if (dateArray[9] && dateArray[10]) {
-          date.setHours(date.getHours() + parseInt(dateArray[9], 10));
-          date.setMinutes(date.getMinutes() + parseInt(dateArray[10], 10));
-        }
-      } else if (dateArray[8] == "+") {
-        if (dateArray[9] && dateArray[10]) {
-          date.setHours(date.getHours() - parseInt(dateArray[9], 10));
-          date.setMinutes(date.getMinutes() - parseInt(dateArray[10], 10));
-        }
-      }
-      /* at this point we have the time in gmt */
-      /* convert to local if we had a Z - or + */
-      if (dateArray[8]) {
-        var tzOffset = date.getTimezoneOffset();
-        if (tzOffset < 0) {
-          date.setMinutes(date.getMinutes() + tzOffset);
-        } else if (tzOffset > 0) {
-          date.setMinutes(date.getMinutes() - tzOffset);
-        }
-      }
-    }
-    return date;
-  },
-  /**
-   * Converts a Javascript date object into an ISO 8601 formatted date
-   * NOTE: I'm using an extra parameter on the date object for this function.
-   * If date.time is NOT true, this function only outputs the date.
-   *
-   * @param  date        Javascript Date object
-   * @param  punctuation true if the date should have -/:
-   * @return string with the ISO date.
-   */
-  iso8601FromDate: function iso8601FromDate(date, punctuation) {
-    var string = date.getFullYear().toString();
-    if (punctuation) {
-      string += "-";
-    }
-    string += (date.getMonth() + 1).toString().replace(/\b(\d)\b/g, '0$1');
-    if (punctuation) {
-      string += "-";
-    }
-    string += date.getDate().toString().replace(/\b(\d)\b/g, '0$1');
-    if (date.time) {
-      string += "T";
-      string += date.getHours().toString().replace(/\b(\d)\b/g, '0$1');
-      if (punctuation) {
-        string += ":";
-      }
-      string += date.getMinutes().toString().replace(/\b(\d)\b/g, '0$1');
-      if (punctuation) {
-        string += ":";
-      }
-      string += date.getSeconds().toString().replace(/\b(\d)\b/g, '0$1');
-      if (date.getMilliseconds() > 0) {
-        if (punctuation) {
-          string += ".";
-        }
-        string += date.getMilliseconds().toString();
-      }
-    }
-    return string;
-  },
-  simpleEscape: function simpleEscape(s)
-  {
-    s = s.replace(/\&/g, '%26');
-    s = s.replace(/\#/g, '%23');
-    s = s.replace(/\+/g, '%2B');
-    s = s.replace(/\-/g, '%2D');
-    s = s.replace(/\=/g, '%3D');
-    s = s.replace(/\'/g, '%27');
-    s = s.replace(/\,/g, '%2C');
-//    s = s.replace(/\r/g, '%0D');
-//    s = s.replace(/\n/g, '%0A');
-    s = s.replace(/ /g, '+');
-    return s;
-  },
-  /**
-   * Not intended for external consumption. Microformat implementations might use it.
-   *
-   * Retrieve elements matching all classes listed in a space-separated string.
-   * I had to implement my own because I need an Array, not an nsIDomNodeList
-   *
-   * @param  rootElement      The DOM element at which to start searching (optional)
-   * @param  className        A space separated list of classenames
-   * @return microformatNodes An array of DOM Nodes, each representing a
-                              microformat in the document.
-   */
-  getElementsByClassName: function getElementsByClassName(rootNode, className)
-  {
-    var returnElements = [];
-
-    if ((rootNode.ownerDocument || rootNode).getElementsByClassName) {
-    /* Firefox 3 - native getElementsByClassName */
-      var col = rootNode.getElementsByClassName(className);
-      for (let i = 0; i < col.length; i++) {
-        returnElements[i] = col[i];
-      }
-    } else if ((rootNode.ownerDocument || rootNode).evaluate) {
-    /* Firefox 2 and below - XPath */
-      var xpathExpression;
-      xpathExpression = ".//*[contains(concat(' ', @class, ' '), ' " + className + " ')]";
-      var xpathResult = (rootNode.ownerDocument || rootNode).evaluate(xpathExpression, rootNode, null, 0, null);
-
-      var node;
-      while (node = xpathResult.iterateNext()) {
-        returnElements.push(node);
-      }
-    } else {
-    /* Slow fallback for testing */
-      className = className.replace(/\-/g, "\\-");
-      var elements = rootNode.getElementsByTagName("*");
-      for (let i=0;i<elements.length;i++) {
-        if (elements[i].className.match("(^|\\s)" + className + "(\\s|$)")) {
-          returnElements.push(elements[i]);
-        }
-      }
-    }
-    return returnElements;
-  },
-  /**
-   * Not intended for external consumption. Microformat implementations might use it.
-   *
-   * Retrieve elements matching an attribute and an attribute list in a space-separated string.
-   *
-   * @param  rootElement      The DOM element at which to start searching (optional)
-   * @param  atributeName     The attribute name to match against
-   * @param  attributeValues  A space separated list of attribute values
-   * @return microformatNodes An array of DOM Nodes, each representing a
-                              microformat in the document.
-   */
-  getElementsByAttribute: function getElementsByAttribute(rootNode, attributeName, attributeValues)
-  {
-    var attributeList = attributeValues.split(" ");
-
-    var returnElements = [];
-
-    if ((rootNode.ownerDocument || rootNode).evaluate) {
-    /* Firefox 3 and below - XPath */
-      /* Create an XPath expression based on the attribute list */
-      var xpathExpression = ".//*[";
-      for (let i = 0; i < attributeList.length; i++) {
-        if (i != 0) {
-          xpathExpression += " or ";
-        }
-        xpathExpression += "contains(concat(' ', @" + attributeName + ", ' '), ' " + attributeList[i] + " ')";
-      }
-      xpathExpression += "]";
-
-      var xpathResult = (rootNode.ownerDocument || rootNode).evaluate(xpathExpression, rootNode, null, 0, null);
-
-      var node;
-      while (node = xpathResult.iterateNext()) {
-        returnElements.push(node);
-      }
-    } else {
-    /* Need Slow fallback for testing */
-    }
-    return returnElements;
-  },
-  matchClass: function matchClass(node, className) {
-    var classValue = node.getAttribute("class");
-    return (classValue && classValue.match("(^|\\s)" + className + "(\\s|$)"));
-  }
-};
-
-/* MICROFORMAT DEFINITIONS BEGIN HERE */
-
-this.adr = function adr(node, validate) {
-  if (node) {
-    Microformats.parser.newMicroformat(this, node, "adr", validate);
-  }
-}
-
-adr.prototype.toString = function() {
-  var address_text = "";
-  var start_parens = false;
-  if (this["street-address"]) {
-    address_text += this["street-address"][0];
-  } else if (this["extended-address"]) {
-    address_text += this["extended-address"];
-  }
-  if (this["locality"]) {
-    if (this["street-address"] || this["extended-address"]) {
-      address_text += " (";
-      start_parens = true;
-    }
-    address_text += this["locality"];
-  }
-  if (this["region"]) {
-    if ((this["street-address"] || this["extended-address"]) && (!start_parens)) {
-      address_text += " (";
-      start_parens = true;
-    } else if (this["locality"]) {
-      address_text += ", ";
-    }
-    address_text += this["region"];
-  }
-  if (this["country-name"]) {
-    if ((this["street-address"] || this["extended-address"]) && (!start_parens)) {
-      address_text += " (";
-      start_parens = true;
-      address_text += this["country-name"];
-    } else if ((!this["locality"]) && (!this["region"])) {
-      address_text += this["country-name"];
-    } else if (((!this["locality"]) && (this["region"])) || ((this["locality"]) && (!this["region"]))) {
-      address_text += ", ";
-      address_text += this["country-name"];
-    }
-  }
-  if (start_parens) {
-    address_text += ")";
-  }
-  return address_text;
-}
-
-var adr_definition = {
-  mfObject: adr,
-  className: "adr",
-  properties: {
-    "type" : {
-      plural: true,
-      values: ["work", "home", "pref", "postal", "dom", "intl", "parcel"]
-    },
-    "post-office-box" : {
-    },
-    "street-address" : {
-      plural: true
-    },
-    "extended-address" : {
-    },
-    "locality" : {
-    },
-    "region" : {
-    },
-    "postal-code" : {
-    },
-    "country-name" : {
-    }
-  },
-  validate: function(node) {
-    var xpathExpression = "count(descendant::*[" +
-                                              "contains(concat(' ', @class, ' '), ' post-office-box ')" +
-                                              " or contains(concat(' ', @class, ' '), ' street-address ')" +
-                                              " or contains(concat(' ', @class, ' '), ' extended-address ')" +
-                                              " or contains(concat(' ', @class, ' '), ' locality ')" +
-                                              " or contains(concat(' ', @class, ' '), ' region ')" +
-                                              " or contains(concat(' ', @class, ' '), ' postal-code ')" +
-                                              " or contains(concat(' ', @class, ' '), ' country-name')" +
-                                              "])";
-    var xpathResult = (node.ownerDocument || node).evaluate(xpathExpression, node, null,  Components.interfaces.nsIDOMXPathResult.ANY_TYPE, null).numberValue;
-    if (xpathResult == 0) {
-      throw("Unable to create microformat");
-    }
-    return true;
-  }
-};
-
-Microformats.add("adr", adr_definition);
-
-this.hCard = function hCard(node, validate) {
-  if (node) {
-    Microformats.parser.newMicroformat(this, node, "hCard", validate);
-  }
-}
-hCard.prototype.toString = function() {
-  if (this.resolvedNode) {
-    /* If this microformat has an include pattern, put the */
-    /* organization-name in parenthesis after the fn to differentiate */
-    /* them. */
-    var fns = Microformats.getElementsByClassName(this.node, "fn");
-    if (fns.length === 0) {
-      if (this.fn) {
-        if (this.org && this.org[0]["organization-name"] && (this.fn != this.org[0]["organization-name"])) {
-          return this.fn + " (" + this.org[0]["organization-name"] + ")";
-        }
-      }
-    }
-  }
-  return this.fn;
-}
-
-var hCard_definition = {
-  mfObject: hCard,
-  className: "vcard",
-  required: ["fn"],
-  properties: {
-    "adr" : {
-      plural: true,
-      datatype: "microformat",
-      microformat: "adr"
-    },
-    "agent" : {
-      plural: true,
-      datatype: "microformat",
-      microformat: "hCard"
-    },
-    "bday" : {
-      datatype: "dateTime"
-    },
-    "class" : {
-    },
-    "category" : {
-      plural: true,
-      datatype: "microformat",
-      microformat: "tag",
-      microformat_property: "tag"
-    },
-    "email" : {
-      subproperties: {
-        "type" : {
-          plural: true,
-          values: ["internet", "x400", "pref"]
-        },
-        "value" : {
-          datatype: "email",
-          virtual: true
-        }
-      },
-      plural: true
-    },
-    "fn" : {
-      required: true
-    },
-    "geo" : {
-      datatype: "microformat",
-      microformat: "geo"
-    },
-    "key" : {
-      plural: true
-    },
-    "label" : {
-      plural: true
-    },
-    "logo" : {
-      plural: true,
-      datatype: "anyURI"
-    },
-    "mailer" : {
-      plural: true
-    },
-    "n" : {
-      subproperties: {
-        "honorific-prefix" : {
-          plural: true
-        },
-        "given-name" : {
-          plural: true
-        },
-        "additional-name" : {
-          plural: true
-        },
-        "family-name" : {
-          plural: true
-        },
-        "honorific-suffix" : {
-          plural: true
-        }
-      },
-      virtual: true,
-      /*  Implied "n" Optimization */
-      /* http://microformats.org/wiki/hcard#Implied_.22n.22_Optimization */
-      virtualGetter: function(mfnode) {
-        var fn = Microformats.parser.getMicroformatProperty(mfnode, "hCard", "fn");
-        var orgs = Microformats.parser.getMicroformatProperty(mfnode, "hCard", "org");
-        var given_name = [];
-        var family_name = [];
-        if (fn && (!orgs || (orgs.length > 1) || (fn != orgs[0]["organization-name"]))) {
-          var fns = fn.split(" ");
-          if (fns.length === 2) {
-            if (fns[0].charAt(fns[0].length-1) == ',') {
-              given_name[0] = fns[1];
-              family_name[0] = fns[0].substr(0, fns[0].length-1);
-            } else if (fns[1].length == 1) {
-              given_name[0] = fns[1];
-              family_name[0] = fns[0];
-            } else if ((fns[1].length == 2) && (fns[1].charAt(fns[1].length-1) == '.')) {
-              given_name[0] = fns[1];
-              family_name[0] = fns[0];
-            } else {
-              given_name[0] = fns[0];
-              family_name[0] = fns[1];
-            }
-            return {"given-name" : given_name, "family-name" : family_name};
-          }
-        }
-        return undefined;
-      }
-    },
-    "nickname" : {
-      plural: true,
-      virtual: true,
-      /* Implied "nickname" Optimization */
-      /* http://microformats.org/wiki/hcard#Implied_.22nickname.22_Optimization */
-      virtualGetter: function(mfnode) {
-        var fn = Microformats.parser.getMicroformatProperty(mfnode, "hCard", "fn");
-        var orgs = Microformats.parser.getMicroformatProperty(mfnode, "hCard", "org");
-        var given_name;
-        var family_name;
-        if (fn && (!orgs || (orgs.length) > 1 || (fn != orgs[0]["organization-name"]))) {
-          var fns = fn.split(" ");
-          if (fns.length === 1) {
-            return [fns[0]];
-          }
-        }
-        return undefined;
-      }
-    },
-    "note" : {
-      plural: true,
-      datatype: "HTML"
-    },
-    "org" : {
-      subproperties: {
-        "organization-name" : {
-          virtual: true
-        },
-        "organization-unit" : {
-          plural: true
-        }
-      },
-      plural: true
-    },
-    "photo" : {
-      plural: true,
-      datatype: "anyURI"
-    },
-    "rev" : {
-      datatype: "dateTime"
-    },
-    "role" : {
-      plural: true
-    },
-    "sequence" : {
-    },
-    "sort-string" : {
-    },
-    "sound" : {
-      plural: true
-    },
-    "title" : {
-      plural: true
-    },
-    "tel" : {
-      subproperties: {
-        "type" : {
-          plural: true,
-          values: ["msg", "home", "work", "pref", "voice", "fax", "cell", "video", "pager", "bbs", "car", "isdn", "pcs"]
-        },
-        "value" : {
-          datatype: "tel",
-          virtual: true
-        }
-      },
-      plural: true
-    },
-    "tz" : {
-    },
-    "uid" : {
-      datatype: "anyURI"
-    },
-    "url" : {
-      plural: true,
-      datatype: "anyURI"
-    }
-  }
-};
-
-Microformats.add("hCard", hCard_definition);
-
-this.hCalendar = function hCalendar(node, validate) {
-  if (node) {
-    Microformats.parser.newMicroformat(this, node, "hCalendar", validate);
-  }
-}
-hCalendar.prototype.toString = function() {
-  if (this.resolvedNode) {
-    /* If this microformat has an include pattern, put the */
-    /* dtstart in parenthesis after the summary to differentiate */
-    /* them. */
-    var summaries = Microformats.getElementsByClassName(this.node, "summary");
-    if (summaries.length === 0) {
-      if (this.summary) {
-        if (this.dtstart) {
-          return this.summary + " (" + Microformats.dateFromISO8601(this.dtstart).toLocaleString() + ")";
-        }
-      }
-    }
-  }
-  if (this.dtstart) {
-    return this.summary;
-  }
-  return undefined;
-}
-
-var hCalendar_definition = {
-  mfObject: hCalendar,
-  className: "vevent",
-  required: ["summary", "dtstart"],
-  properties: {
-    "category" : {
-      plural: true,
-      datatype: "microformat",
-      microformat: "tag",
-      microformat_property: "tag"
-    },
-    "class" : {
-      values: ["public", "private", "confidential"]
-    },
-    "description" : {
-      datatype: "HTML"
-    },
-    "dtstart" : {
-      datatype: "dateTime"
-    },
-    "dtend" : {
-      datatype: "dateTime"
-    },
-    "dtstamp" : {
-      datatype: "dateTime"
-    },
-    "duration" : {
-    },
-    "geo" : {
-      datatype: "microformat",
-      microformat: "geo"
-    },
-    "location" : {
-      datatype: "microformat",
-      microformat: "hCard"
-    },
-    "status" : {
-      values: ["tentative", "confirmed", "cancelled"]
-    },
-    "summary" : {},
-    "transp" : {
-      values: ["opaque", "transparent"]
-    },
-    "uid" : {
-      datatype: "anyURI"
-    },
-    "url" : {
-      datatype: "anyURI"
-    },
-    "last-modified" : {
-      datatype: "dateTime"
-    },
-    "rrule" : {
-      subproperties: {
-        "interval" : {
-          virtual: true,
-          /* This will only be called in the virtual case */
-          virtualGetter: function(mfnode) {
-            return Microformats.hCalendar.properties.rrule.retrieve(mfnode, "interval");
-          }
-        },
-        "freq" : {
-          virtual: true,
-          /* This will only be called in the virtual case */
-          virtualGetter: function(mfnode) {
-            return Microformats.hCalendar.properties.rrule.retrieve(mfnode, "freq");
-          }
-        },
-        "bysecond" : {
-          virtual: true,
-          /* This will only be called in the virtual case */
-          virtualGetter: function(mfnode) {
-            return Microformats.hCalendar.properties.rrule.retrieve(mfnode, "bysecond");
-          }
-        },
-        "byminute" : {
-          virtual: true,
-          /* This will only be called in the virtual case */
-          virtualGetter: function(mfnode) {
-            return Microformats.hCalendar.properties.rrule.retrieve(mfnode, "byminute");
-          }
-        },
-        "byhour" : {
-          virtual: true,
-          /* This will only be called in the virtual case */
-          virtualGetter: function(mfnode) {
-            return Microformats.hCalendar.properties.rrule.retrieve(mfnode, "byhour");
-          }
-        },
-        "bymonthday" : {
-          virtual: true,
-          /* This will only be called in the virtual case */
-          virtualGetter: function(mfnode) {
-            return Microformats.hCalendar.properties.rrule.retrieve(mfnode, "bymonthday");
-          }
-        },
-        "byyearday" : {
-          virtual: true,
-          /* This will only be called in the virtual case */
-          virtualGetter: function(mfnode) {
-            return Microformats.hCalendar.properties.rrule.retrieve(mfnode, "byyearday");
-          }
-        },
-        "byweekno" : {
-          virtual: true,
-          /* This will only be called in the virtual case */
-          virtualGetter: function(mfnode) {
-            return Microformats.hCalendar.properties.rrule.retrieve(mfnode, "byweekno");
-          }
-        },
-        "bymonth" : {
-          virtual: true,
-          /* This will only be called in the virtual case */
-          virtualGetter: function(mfnode) {
-            return Microformats.hCalendar.properties.rrule.retrieve(mfnode, "bymonth");
-          }
-        },
-        "byday" : {
-          virtual: true,
-          /* This will only be called in the virtual case */
-          virtualGetter: function(mfnode) {
-            return Microformats.hCalendar.properties.rrule.retrieve(mfnode, "byday");
-          }
-        },
-        "until" : {
-          virtual: true,
-          /* This will only be called in the virtual case */
-          virtualGetter: function(mfnode) {
-            return Microformats.hCalendar.properties.rrule.retrieve(mfnode, "until");
-          }
-        },
-        "count" : {
-          virtual: true,
-          /* This will only be called in the virtual case */
-          virtualGetter: function(mfnode) {
-            return Microformats.hCalendar.properties.rrule.retrieve(mfnode, "count");
-          }
-        }
-      },
-      retrieve: function(mfnode, property) {
-        var value = Microformats.parser.textGetter(mfnode);
-        var rrule;
-        rrule = value.split(';');
-        for (let i=0; i < rrule.length; i++) {
-          if (rrule[i].match(property)) {
-            return rrule[i].split('=')[1];
-          }
-        }
-        return undefined;
-      }
-    }
-  }
-};
-
-Microformats.add("hCalendar", hCalendar_definition);
-
-this.geo = function geo(node, validate) {
-  if (node) {
-    Microformats.parser.newMicroformat(this, node, "geo", validate);
-  }
-}
-geo.prototype.toString = function() {
-  if (this.latitude != undefined) {
-    if (!isFinite(this.latitude) || (this.latitude > 360) || (this.latitude < -360)) {
-      return undefined;
-    }
-  }
-  if (this.longitude != undefined) {
-    if (!isFinite(this.longitude) || (this.longitude > 360) || (this.longitude < -360)) {
-      return undefined;
-    }
-  }
-
-  if ((this.latitude != undefined) && (this.longitude != undefined)) {
-    var s;
-    if ((this.node.localName.toLowerCase() == "abbr") || (this.node.localName.toLowerCase() == "html:abbr")) {
-      s = this.node.textContent;
-    }
-
-    if (s) {
-      return s;
-    }
-
-    /* check if geo is contained in a vcard */
-    var xpathExpression = "ancestor::*[contains(concat(' ', @class, ' '), ' vcard ')]";
-    var xpathResult = this.node.ownerDocument.evaluate(xpathExpression, this.node, null,  Components.interfaces.nsIDOMXPathResult.FIRST_ORDERED_NODE_TYPE, null);
-    if (xpathResult.singleNodeValue) {
-      var hcard = new hCard(xpathResult.singleNodeValue);
-      if (hcard.fn) {
-        return hcard.fn;
-      }
-    }
-    /* check if geo is contained in a vevent */
-    xpathExpression = "ancestor::*[contains(concat(' ', @class, ' '), ' vevent ')]";
-    xpathResult = this.node.ownerDocument.evaluate(xpathExpression, this.node, null,  Components.interfaces.nsIDOMXPathResult.FIRST_ORDERED_NODE_TYPE, xpathResult);
-    if (xpathResult.singleNodeValue) {
-      var hcal = new hCalendar(xpathResult.singleNodeValue);
-      if (hcal.summary) {
-        return hcal.summary;
-      }
-    }
-    if (s) {
-      return s;
-    } else {
-      return this.latitude + ", " + this.longitude;
-    }
-  }
-  return undefined;
-}
-
-var geo_definition = {
-  mfObject: geo,
-  className: "geo",
-  required: ["latitude","longitude"],
-  properties: {
-    "latitude" : {
-      datatype: "float",
-      virtual: true,
-      /* This will only be called in the virtual case */
-      virtualGetter: function(mfnode) {
-        var value = Microformats.parser.textGetter(mfnode);
-        var latlong;
-        if (value.match(';')) {
-          latlong = value.split(';');
-          if (latlong[0]) {
-            if (!isNaN(latlong[0])) {
-              return parseFloat(latlong[0]);
-            }
-          }
-        }
-        return undefined;
-      }
-    },
-    "longitude" : {
-      datatype: "float",
-      virtual: true,
-      /* This will only be called in the virtual case */
-      virtualGetter: function(mfnode) {
-        var value = Microformats.parser.textGetter(mfnode);
-        var latlong;
-        if (value.match(';')) {
-          latlong = value.split(';');
-          if (latlong[1]) {
-            if (!isNaN(latlong[1])) {
-              return parseFloat(latlong[1]);
-            }
-          }
-        }
-        return undefined;
-      }
-    }
-  },
-  validate: function(node) {
-    var latitude = Microformats.parser.getMicroformatProperty(node, "geo", "latitude");
-    var longitude = Microformats.parser.getMicroformatProperty(node, "geo", "longitude");
-    if (latitude != undefined) {
-      if (!isFinite(latitude) || (latitude > 360) || (latitude < -360)) {
-        throw("Invalid latitude");
-      }
-    } else {
-      throw("No latitude specified");
-    }
-    if (longitude != undefined) {
-      if (!isFinite(longitude) || (longitude > 360) || (longitude < -360)) {
-        throw("Invalid longitude");
-      }
-    } else {
-      throw("No longitude specified");
-    }
-    return true;
-  }
-};
-
-Microformats.add("geo", geo_definition);
-
-this.tag = function tag(node, validate) {
-  if (node) {
-    Microformats.parser.newMicroformat(this, node, "tag", validate);
-  }
-}
-tag.prototype.toString = function() {
-  return this.tag;
-}
-
-var tag_definition = {
-  mfObject: tag,
-  attributeName: "rel",
-  attributeValues: "tag",
-  properties: {
-    "tag" : {
-      virtual: true,
-      virtualGetter: function(mfnode) {
-        if (mfnode.href) {
-          var ioService = Components.classes["@mozilla.org/network/io-service;1"].
-                                     getService(Components.interfaces.nsIIOService);
-          var uri = ioService.newURI(mfnode.href, null, null);
-          var url_array = uri.path.split("/");
-          for(let i=url_array.length-1; i > 0; i--) {
-            if (url_array[i] !== "") {
-              var tag
-              if (tag = Microformats.tag.validTagName(url_array[i].replace(/\+/g, ' '))) {
-                try {
-                  return decodeURIComponent(tag);
-                } catch (ex) {
-                  return unescape(tag);
-                }
-              }
-            }
-          }
-        }
-        return null;
-      }
-    },
-    "link" : {
-      virtual: true,
-      datatype: "anyURI"
-    },
-    "text" : {
-      virtual: true
-    }
-  },
-  validTagName: function(tag)
-  {
-    var returnTag = tag;
-    if (tag.indexOf('?') != -1) {
-      if (tag.indexOf('?') === 0) {
-        return false;
-      } else {
-        returnTag = tag.substr(0, tag.indexOf('?'));
-      }
-    }
-    if (tag.indexOf('#') != -1) {
-      if (tag.indexOf('#') === 0) {
-        return false;
-      } else {
-        returnTag = tag.substr(0, tag.indexOf('#'));
-      }
-    }
-    if (tag.indexOf('.html') != -1) {
-      if (tag.indexOf('.html') == tag.length - 5) {
-        return false;
-      }
-    }
-    return returnTag;
-  },
-  validate: function(node) {
-    var tag = Microformats.parser.getMicroformatProperty(node, "tag", "tag");
-    if (!tag) {
-      if (node.href) {
-        var url_array = node.getAttribute("href").split("/");
-        for(let i=url_array.length-1; i > 0; i--) {
-          if (url_array[i] !== "") {
-            throw("Invalid tag name (" + url_array[i] + ")");
-          }
-        }
-      } else {
-        throw("No href specified on tag");
-      }
-    }
-    return true;
-  }
-};
-
-Microformats.add("tag", tag_definition);
--- a/toolkit/components/microformats/moz.build
+++ b/toolkit/components/microformats/moz.build
@@ -1,13 +1,12 @@
 # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # 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/.
 
 EXTRA_JS_MODULES += [
-    'microformat-shiv.js',
-    'Microformats.js',
+    'microformat-shiv.js'
 ]
 
 with Files('**'):
     BUG_COMPONENT = ('Toolkit', 'Microformats')
deleted file mode 100644
--- a/toolkit/components/microformats/tests/.eslintrc
+++ /dev/null
@@ -1,5 +0,0 @@
-{
-  "extends": [
-    "../../../../testing/mochitest/mochitest.eslintrc"
-  ]
-}
deleted file mode 100644
--- a/toolkit/components/microformats/tests/geo.html
+++ /dev/null
@@ -1,8 +0,0 @@
-<html>
-  <body>
-    <span class="geo" id="01-geo-abbr-latlong" >
-      <abbr class="latitude" title="37.77">Northern</abbr>
-      <abbr class="longitude" title="-122.41">California</abbr>
-    </span>
-  </body>
-</html>
deleted file mode 100644
--- a/toolkit/components/microformats/tests/mochitest.ini
+++ /dev/null
@@ -1,14 +0,0 @@
-[DEFAULT]
-skip-if = buildapp == 'b2g'
-support-files = geo.html
-
-[test_Microformats.html]
-[test_Microformats_add.html]
-[test_Microformats_adr.html]
-[test_Microformats_count.html]
-[test_Microformats_geo.html]
-[test_Microformats_getters.html]
-[test_Microformats_hCalendar.html]
-[test_Microformats_hCard.html]
-[test_Microformats_negative.html]
-[test_framerecursion.html]
deleted file mode 100644
--- a/toolkit/components/microformats/tests/test_Microformats.html
+++ /dev/null
@@ -1,301 +0,0 @@
-<html>
-<head>
-  <title>Testing Microformats.js</title>
-  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
-  <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
-  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"></link>
-  <style type="text/css">
-    /*vcards weee! */
-    .floated div {
-      float:left;
-    }
-  </style>
-
-</head>
-<body>
-<div id="content" style="display: none">
-  <div id="test_1">
-    <div class="vcard" id="vcard1_node">
-     <a class="url fn" href="http://tantek.com/">Tantek Çelik</a>
-     <div id="vcard1_org" class="org">Technorati</div>
-    </div>  
-    <div class="vcard" id="vcard2_node">
-     <a class="url" href="http://tantek.com/">Tantek Çelik</a>
-     <div id="vcard2_org" class="org">Technorati</div>
-    </div>
-  </div>
-  <div class="vcard" id="outer_vcard">
-    <div class="vcard" id="middle_vcard">
-      <div class="vcard" id="inner_vcard">
-        <span class="fn">Inner User</span>
-      </div>
-      <span class="fn">Middle User</span>
-    </div>
-    <span class="fn">Outer User</span>
-  </div>
-  <div class="vcard" id="outer_vcard2">
-    <div class="vevent" id="middle_vevent2">
-      <div class="vcard" id="inner_vcard2">
-        <span class="fn">Inner User</span>
-      </div>
-      <span class="summary">Middle Event</span>
-    </div>
-    <span class="fn">Outer User</span>
-  </div>
-  <div class="vevent" id="outer_vevent3">
-    <div class="vcard" id="middle_vcard3">
-      <div class="vevent" id="inner_vevent3">
-        <span class="summary">Inner Event</span>
-      </div>
-      <span class="fn">Middle User</span>
-    </div>
-    <span class="summary">Outer Event</span>
-  </div>
-
-  <div class="vevent" id="outer_vevent4">
-    <a rel="tag" id="inner_tag4" href="http://www.example.com/inner_tag">
-      Inner tag
-    </a>
-    <span class="summary">Outer Event</span>
-  </div>
-
-  <a rel="tag" id="outer_tag5" href="http://www.example.com/outer_tag">
-    <div class="vevent" id="inner_vevent5">
-      <span class="summary">Inner Event</span>
-    </div>
-  </a>
-  
-  <div class="vcard" id="value_test">
-    <span class="fn">
-      <span class="value">John</span>
-      <span><span class="value">Middle</span></span>
-      <span class="value">Doe</span>
-    </span>
-  </div>
-
-  <div class="vcard" id="nested_vcard1">
-    <div class="agent vcard" id="nested_vcard2">
-      <div class="agent vcard" id="nested_vcard3">
-        <span class="fn">Bob Smith</span>
-        <span class="title">Office Assistant</span>
-      </div>
-      <span class="fn">Jack Jones</span>
-      <span class="title">Executive Assistant</span>
-    </div>
-    <span class="fn">John Doe</span>
-    <span class="title">CEO</span>
-  </div>
-
-  <div class="vevent" id="date_vcal">
-    <span class="description">Mozilla's Birthday</span>
-    <span class="dtstart">1998-01-22</span>
-  </div>
-
-  <div class="vevent" id="vcal_vcard">
-    <div class="vcard">
-      <span class="description">
-        <span class="fn org">Mozilla</span>'s Birthday
-      </span>
-    </div>
-    <span class="dtstart">1998-01-22</span>
-  </div>
-
-  <div id="geo_vcard" class="vcard">
-    <span class="fn">John Doe</span>
-    <span class="geo" id="ill_geo5">abc;def</span>
-  </div>
-  <div id="loc_vevent" class="vevent">
-    <span class="summary">Party</span>
-    <span class="location">The White House</span>
-  </div>
-  <div id="loc_vcard_vevent" class="vevent">
-    <span class="summary">Party</span>
-    <span class="location vcard"><span class="fn">The White House</span></span>
-  </div>
-  
-  <div class="vcard" id="valuespace_1">
-    <span class="fn">
-      <span class="value">John</span>
-      <span class="value"> </span>
-      <span class="value">Doe</span>
-    </span>
-  </div>
-  <div class="vcard" id="valuespace_2">
-    <span class="fn">
-      <span class="value">John</span>
-      <span class="value">    </span>
-      <span class="value">Doe</span>
-    </span>
-  </div>
-  <div class="vcard" id="valuespace_3">
-    <span class="fn">
-      <span class="value">  John</span>
-      <span class="value">    </span>
-      <span class="value">Doe  </span>
-    </span>
-  </div>
-  <div class="vcard" id="valuespace_4">
-    <span class="fn">
-      <span class="value">John    </span>
-      <span class="value">    Doe</span>
-    </span>
-  </div>
-  <div class="vcard" id="valuespace_5">
-    <span class="fn">
-      <span class="value">    John</span>
-      <span class="value">    Doe    </span>
-    </span>
-  </div>
-  <div class="vcard" id="valuespace_6">
-    <span class="fn">
-      <span class="value">John
-      </span>
-      <span class="value">
-      Doe</span>
-      </span>
-  </div>
-  <div class="vcard" id="valuespace_7">
-    <span class="fn">
-      <span class="value">John</span>
-      <span class="value">Doe</span>
-    </span>
-  </div>
- 
-  <span id="austin" class="location">Austin - Sixth Street</span>
-  <table summary="Timetable">
-    <thead>
-      <tr>
-        <th id="location-1"><a href="#austin" class="include"></a>New York</th>
-      </tr>
-    </thead>
-    <tbody>
-      <tr>
-        <th id="time-1"><abbr title="2008-01-01" class="dtstart">Jan 1, 2008</abbr></th>
-        <td id="nested_header_include" class="vevent" headers="time-1 location-1"><div class="summary">New Years Party</div></td>
-      </tr>
-    </tbody>
-  </table>
-
-</div>
-
-<div id="float_test">
-  <div class="vcard floated" id="one">
-    <div class="fn">John Doe</div>
-  </div>
-  <div class="vcard" id="two">
-    <div class="fn">John Smith</div>
-  </div>
-</div>
-
-<pre id="test">
-<script class="testbody" type="application/javascript;version=1.8">
-
-test_Microformats();
-test_hCard();
-
-function test_Microformats() {
-  let { Microformats, hCard, hCalendar } = SpecialPowers.Cu.import("resource://gre/modules/Microformats.js");
-
-  ok(Microformats, "Check global access to Microformats");
-  var hCards = Microformats.get("hCard", document.getElementById("test_1"), {showHidden: true});
-  is(hCards.length, 1, "Check Microformats.get");
-  is(Microformats.count("hCard", document.getElementById("test_1"),  {showHidden: true}), 1, "Check Microformats.count");
-  is(Microformats.count("hCard", document.getElementById("test_1"),  {showHidden: true, debug: true}), 2, "Check Microformats.count (debug)");
-  ok(Microformats.isMicroformat(document.getElementById("vcard1_node")), "Check isMicroformat");
-  is(SpecialPowers.unwrap(Microformats.getParent(document.getElementById("vcard1_org"))), document.getElementById("vcard1_node"), "Check getParent");
-  is(Microformats.getNamesFromNode(document.getElementById("vcard1_node")), "hCard", "Check getNamesFromNode");
-  
-  var hCardArray1 = Microformats.get("hCard", document.getElementById("test_1"), {showHidden: true});
-  is(hCardArray1.length, 1, "Check showHidden=true");
-  var hCardArray2 = Microformats.get("hCard", document.getElementById("test_1"), {showHidden: false});
-  is(hCardArray2.length, 0, "Check showHidden=false");
-  
-  var inner_parent = Microformats.getParent(document.getElementById("inner_vcard"));
-  is(inner_parent.id, "middle_vcard", "GetParent gets correct ancestor 1");
-
-  var inner_parent = Microformats.getParent(document.getElementById("inner_vcard2"));
-  is(inner_parent.id, "middle_vevent2", "GetParent gets correct ancestor 2");
-
-  var inner_parent = Microformats.getParent(document.getElementById("middle_vevent2"));
-  is(inner_parent.id, "outer_vcard2", "GetParent gets correct ancestor 2a");
-
-  var inner_parent = Microformats.getParent(document.getElementById("inner_vevent3"));
-  is(inner_parent.id, "middle_vcard3", "GetParent gets correct ancestor 3");
-
-  var inner_parent = Microformats.getParent(document.getElementById("middle_vcard3"));
-  is(inner_parent.id, "outer_vevent3", "GetParent gets correct ancestor 3a");
-
-  var inner_parent = Microformats.getParent(document.getElementById("inner_tag4"));
-  is(inner_parent.id, "outer_vevent4", "GetParent gets correct ancestor 4");
-
-  var inner_parent = Microformats.getParent(document.getElementById("inner_vevent5"));
-  is(inner_parent.id, "outer_tag5", "GetParent gets correct ancestor 5");
-  
-  var valueCard = new hCard(document.getElementById("value_test"));
-
-  is(valueCard.fn, "JohnDoe", "value_test");
-
-  var nestCard1 = new hCard(document.getElementById("nested_vcard1"));
-  var nestCard2 = new hCard(document.getElementById("nested_vcard2"));
-  var nestCard3 = new hCard(document.getElementById("nested_vcard3"));
-
-  is(nestCard1.fn, "John Doe", "nesting (fn) 1");
-  is(String(nestCard1.title), "CEO", "nesting (title) 1");
-  is(nestCard2.fn, "Jack Jones", "nesting (fn) 2");
-  is(String(nestCard2.title), "Executive Assistant", "nesting (title) 2");
-  is(nestCard3.fn, "Bob Smith", "nesting (fn) 3");
-  is(String(nestCard3.title), "Office Assistant", "nesting (title) 3");
-  is(nestCard1.agent[0].agent[0].fn, "Bob Smith", "nesting all");
-  
-  var dateCal = new hCalendar(document.getElementById("date_vcal"));
-  jsdate = Microformats.dateFromISO8601(dateCal.dtstart);
-  origdate = Microformats.iso8601FromDate(jsdate, true);
-  is(dateCal.dtstart, origdate, "date round trip");
-
-  var dateCal = new hCalendar(document.getElementById("vcal_vcard"));
-  is(String(dateCal.description), "Mozilla's Birthday", "vcard in vcal");
-
-  is(Microformats.count("hCard", document.getElementById("float_test")), 2, "Check Microformats.count for floated div");
-
-  var hcard = new hCard(document.getElementById("geo_vcard"));
-  ok(!hcard.geo, "Check if invalid geo does not exist");
-  var hcal = new hCalendar(document.getElementById("loc_vevent"));
-  is(hcal.location, "The White House", "Check if non vcard location works");
-  var hcal = new hCalendar(document.getElementById("loc_vcard_vevent"));
-  is(hcal.location.fn, "The White House", "Check if vcard location works");
-
-  var nestedCal = new hCalendar(document.getElementById("nested_header_include"));
-  is(nestedCal.dtstart, "2008-01-01", "nested_header_include - dtstart");
-  is(nestedCal.location, "Austin - Sixth Street", "nested_header_include - location");
-  is(nestedCal.summary, "New Years Party", "nested_header_include - summary");
-
-  var valueCard = new hCard(document.getElementById("valuespace_1"));
-  is(valueCard.fn, "John Doe", "valuespace_1");
-  var valueCard = new hCard(document.getElementById("valuespace_2"));
-  is(valueCard.fn, "John Doe", "valuespace_2");
-  var valueCard = new hCard(document.getElementById("valuespace_3"));
-  is(valueCard.fn, "John Doe", "valuespace_3");
-  var valueCard = new hCard(document.getElementById("valuespace_4"));
-  is(valueCard.fn, "John Doe", "valuespace_4");
-  var valueCard = new hCard(document.getElementById("valuespace_5"));
-  is(valueCard.fn, "John Doe", "valuespace_5");
-  var valueCard = new hCard(document.getElementById("valuespace_6"));
-  is(valueCard.fn, "John Doe", "valuespace_6");
-  var valueCard = new hCard(document.getElementById("valuespace_7"));
-  is(valueCard.fn, "JohnDoe", "valuespace_7");
-
-}
-
-function test_hCard() {
-  var Microformats = SpecialPowers.Cu.import("resource://gre/modules/Microformats.js").Microformats;
-  
-  var hCards = Microformats.get("hCard", document.getElementById("test_1"),  {showHidden: true}); 
-  
-  is(hCards[0].fn, "Tantek Çelik", "Check for fn on test vcard");
-  is(String(hCards[0].url), "http://tantek.com/", "Check for url on test vcard");
-}
-
-</script>
-</pre>
-</body>
-</html>
deleted file mode 100644
--- a/toolkit/components/microformats/tests/test_Microformats_add.html
+++ /dev/null
@@ -1,238 +0,0 @@
-<html>
-<head>
-  <title>Testing Mixed Up Microformat APIs</title>
-  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
-  <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
-  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"></link>
-</head>
-<body id="contentbody">
-
-  <!-- Parsing tests are well covered, we're going to do some data type stuff
-       here -->
-  <div id="test1">
-    <p class="mftest" id="mftest-version1">
-      <span class="title"><b>Can Really be Anything, even HTML</b></span>
-      <span class="htmlval"><i>foo</i></span>
-      <span class="htmlval">what about just text?</span>
-      <span class="tel">011-23-45-7867-897890</span>
-      <span class="uri">http://www.mozilla.org </span>
-      <span class="email">mailto:joe@nowhere.org</span>
-      <span class="geo">
-        <span class="latitude">0.0</span>
-        <span class="longitude">0.0</span>
-      </span>
-      <span class="date">20080401T235900</span>
-    </p>
-  </div>
-
-  <!-- This one is invalid until we add the new version of the hTest MF -->
-  <div id="test2">
-    <span class="mftest" id="mftest-version2">
-      <abbr class="title" title="<b>Can Really be Anything, even HTML</b>"/>
-      <span class="uri" title="http://www.mozilla.org"/>
-      <abbr class="email" title="joe@nowhere.org">joe's email</ABBR>
-      <span class="geo">
-        <span class="latitude">0.0</span>
-        <span class="longitude">0.0"</span>
-      </span>
-      <!-- Throw a zulu in there! -->
-      <span class="date" title="20080401T235900Z"/>
-    </span>
-  </div>
-
-  <!-- This one is invalid in both versions (no title)-->
-  <div id="test3">
-    <div class="mftest">
-      <abbr class="htmlval" title="what about just text?">more values</abbr>  
-      <span class="uri">http://foo.com</span>
-    </div>
-  </div>
-
-  <!-- Contains an invalid geo -->
-  <div id="test4">
-    <span class="mftest">
-      <abbr class="title" title="<b>Can Really be Anything, even HTML</b>"/>
-      <abbr class="htmlval" title="<html><body>foo</body></html>">
-        An HTML document!
-      </abbr>
-      <abbr class="htmlval" title="what about just text?">more values</abbr>
-      <span class="tel" title="011-23-45-7867-897890"/>
-      <span class="uri" title="http://www.mozilla.org"/>
-      <abbr class="email" title="joe@nowhere.org">joe's email</ABBR>
-      <span class="geo">
-        <span class="latitude">659</span>
-        <span class="longitude">-362.5</span>
-      </span>
-      <span class="date" title="20080401T235900"/>
-    </span>
-  </div>
-
-  <!-- Contains an invalid date -->
-  <div id="test5">
-    <span class="mftest">
-      <abbr class="title htmlval" title="another test">thehtmlvaltodoescapeme</abbr>
-      <abbr class="date" title="200311T032Z">invalid date</abbr>
-    </span>
-  </div>
-
-  <!-- Ok, the test, here we go -->
-  <pre id="test">
-  <script class="testbody" type="text/javascript">
-
-  function hTest(node, validate) {
-    var Microformats = SpecialPowers.Cu.import("resource://gre/modules/Microformats.js").Microformats;
-    if (node)
-      Microformats.parser.newMicroformat(this, node, "hTest", validate);
-  }
-
-  hTest.prototype.toString = function () {
-    return("This is a test");
-  }
-
-  // Define a new microformat to add to the Microformats collection
-  var hTest_definition = {
-    mfObject: hTest,
-    className: "mftest",
-    required: ["title", "htmlval"],
-    properties: {
-      "title" : {},
-      "htmlval" : {
-        plural: true,
-        datatype: "HTML"
-      },
-      "tel" : {
-        datatype: "tel",
-      },
-      "uri" : {
-        dataypte: "anyURI"
-      },
-      "email" : {
-        datatype: "email"
-      },
-      "geo" : {
-        datatype: "microformat",
-        microformat: "geo"
-      },
-      "date" : {
-        datatype: "dateTime",
-      }
-    }
-  };
-
-  // Define another version of this microformat to overwrite it - this one
-  // removes the requirement to have a htmlval and also removes the tel prop.
-  var hTest_definition2 = {
-    mfObject: hTest,
-    className: "mftest",
-    required: ["title"],
-    properties: {
-      "title" : {},
-      "htmlval" : {
-        plural: true,
-        datatype: "HTML"
-      },
-      "uri" : {
-        dataypte: "anyURI"
-      },
-      "email" : {
-        datatype: "email"
-      },
-      "geo" : {
-        datatype: "microformat",
-        microformat: "geo"
-      },
-      "date" : {
-        datatype: "dateTime",
-      }
-    }
-  };
-  test_MicroformatsAPI();
-
-  function doTest3_4_and5() {
-    var Microformats = SpecialPowers.Cu.import("resource://gre/modules/Microformats.js").Microformats;
-    ok(Microformats, "Make sure we still have a microformats object");
-
-    var mfs = Microformats.get("hTest",
-                               document.getElementById("test3"),
-                               {recurseExternalFrames: true});
-
-    is(mfs.length, 0, "Check hTest 3 is invalid");
-
-    mfs = Microformats.get("hTest",
-                            document.getElementById("test4"),
-                            {recurseExternalFrames: true});
-
-    is(mfs.length, 1, "Check hTest 4 is valid");
-    // Test 4 is a valid hTest, but it's embedded Geo is invalid,
-    // test that assumption
-    ok(!(mfs.geo), "Ensure that the test 4 geo is reporting as invalid");
-
-    mfs = Microformats.get("hTest",
-                            document.getElementById("test5"),
-                            {recurseExternalFrames: true});
-    is(mfs.length, 1, "Check hTest 5 is valid");
-
-    try {
-      var jsDate = new Date(Microformats.parser.dateFromISO8601(mfs[0].date));
-      ok(false, "Invalid JS Date should throw");
-    } catch (ex) {
-      ok(true, "Check that getting invalid jsdate throws");
-    }
-  }
-
-  function test_MicroformatsAPI() {
-    var Microformats = SpecialPowers.Cu.import("resource://gre/modules/Microformats.js").Microformats;
-
-    // Make sure they aren't microformats yet
-    is(Microformats.isMicroformat(document.getElementById("mftest-version1")),
-       false, "Check that the mfTest microformat does not exist yet");
-
-    try {
-      Microformats.add("hTest", hTest_definition);
-    } catch (ex) {
-      ok(false, "Threw while adding hTest definition 1");
-    }
-
-    ok(Microformats.isMicroformat(document.getElementById("mftest-version1")),
-       "Check that the mfTest microformat now exists");
-
-    var mf = Microformats.get("hTest", document.getElementById("test1"),
-                              {recurseExternalFrames: true});
-
-    is(mf.length, 1, "Check that test1 is a valid microformat");
-    is(mf[0].title, "Can Really be Anything, even HTML", "Check test1 title");
-
-    is(mf[0].geo.latitude, 0.0, "Check test1 geo");
-
-    // Make sure that our test2 doesn't pass validation until we add
-    // version 2 of the hTest microformat
-    var mf2 = Microformats.get("hTest", document.getElementById("test2"), {});
-    is(mf2.length, 0, "Check that the mfTest microformat version 2 is not a MF");
-
-    doTest3_4_and5(false);
-
-    // Ok, add the version 2 hTest
-    try {
-      Microformats.add("hTest", hTest_definition2);
-    } catch (ex) {
-      ok(false, "Threw while adding hTest definition 2");
-    }
-
-    // The old version's microformat is still valid
-    mf = Microformats.get("hTest", document.getElementById("test1"),
-                          {recurseExternalFrames: true});
-
-    ok(mf.length, 1, "Check that test1 is a valid microformat");
-
-    // Verify that the version 2 microformat is now also considered valid
-    mf2 = Microformats.get("hTest", document.getElementById("test2"), {});
-
-    ok(mf2.length, 1, "Check that the mfTest microformat version 2 is now valid");
-    doTest3_4_and5(true);
-
-    Microformats.remove("hTest");
-  }
-
-  </script>
-</body>
-</html>
deleted file mode 100644
--- a/toolkit/components/microformats/tests/test_Microformats_adr.html
+++ /dev/null
@@ -1,40 +0,0 @@
-<html>
-<head>
-  <meta http-equiv="Content-type" content="text/html; charset=utf-8" />
-  <title>Testing Microformats.js (adr)</title>
-  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
-  <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
-  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"></link>
-</head>
-<body>
-<div id="content" style="display: none">
-  <div class="adr" id="01-extended-address">
-    <span class="extended-address">Park Bench</span>
-  </div>
-</div>
-<pre id="test">
-<script class="testbody" type="text/javascript">
-
-test_Microformats();
-test_adr();
-
-function test_Microformats() {
-  var Microformats = SpecialPowers.Cu.import("resource://gre/modules/Microformats.js").Microformats;
-
-  ok(Microformats, "Check global access to Microformats");
-}
-
-function test_adr() {
-  var adr = SpecialPowers.Cu.import("resource://gre/modules/Microformats.js").adr;
-
-  var address;
-
-  address = new adr(document.getElementById("01-extended-address"));
-
-  is(address.toString(), "Park Bench", "01-extended-address");
-}
-
-</script>
-</pre>
-</body>
-</html>
deleted file mode 100644
--- a/toolkit/components/microformats/tests/test_Microformats_count.html
+++ /dev/null
@@ -1,154 +0,0 @@
-<html>
-<head>
-  <title>Testing Mixed Up Microformat APIs</title>
-  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
-  <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
-  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"></link>
-</head>
-<body id="contentbody">
-  <!-- hCard -->
-  <p class="vcard" id="23-abbr-title-everything">
-<!-- perhaps the most annoying test ever -->
-    <abbr class="fn" title="John Doe">foo</abbr>
-    <span class="n">
-      <abbr class="honorific-prefix" title="Mister">Mr.</abbr>
-      <abbr class="given-name" title="Jonathan">John</abbr>
-      <abbr class="additional-name" title="John">J</abbr>
-      <abbr class="family-name" title="Doe-Smith">Doe</abbr>
-      <abbr class="honorific-suffix" title="Medical Doctor">M.D</abbr>
-    </span>
-    <abbr class="nickname" title="JJ">jj</abbr>
-    <abbr class="bday" title="2006-04-04">April 4, 2006</abbr>
-    <span class="adr">
-      <abbr class="post-office-box" title="Box 1234">B. 1234</abbr>
-      <abbr class="extended-address" title="Suite 100">Ste. 100</abbr>
-      <abbr class="street-address" title="123 Fake Street">123 Fake St.</abbr>
-      <abbr class="locality" title="San Francisco">San Fran</abbr>
-      <abbr class="region" title="California">CA</abbr>
-      <abbr class="postal-code" title="12345-6789">12345</abbr>
-      <abbr class="country-name" title="United States of America">USA</abbr>
-      <abbr class="type" title="work">workplace</abbr>
-    </span>
-    <abbr class="tel" title="415.555.1234">1234</abbr>
-    <abbr class="tel-type-value" title="work">workplace</abbr>
-<!--       mailer  -->
-    <abbr class="tz" title="-0700">Pacific Time</abbr>
-    <span class="geo">
-      <abbr class="latitude" title="37.77">Northern</abbr>
-      <abbr class="longitude" title="-122.41">California</abbr>
-    </span>
-    <abbr class="title" title="President">pres.</abbr> and
-    <abbr class="role" title="Chief">cat wrangler</abbr>
-<!--       <span class="agent"></span> -->
-    <span class="org">
-      <abbr class="organization-name" title="Intellicorp">foo</abbr>
-      <abbr class="organization-unit" title="Intelligence">bar</abbr>
-    </span>
-<!--       <abbr class="category" title=""></abbr> -->
-    <abbr class="note" title="this is a note">this is not a note</abbr>
-<!--       <abbr class="rev" title=""></abbr>  (revision datetime) -->
-<!--       <abbr class="sort-string" title=""></abbr> -->
-    <abbr class="uid" title="abcdefghijklmnopqrstuvwxyz">alpha</abbr>
-    <abbr class="class" title="public">pub</abbr>
-<!--       <abbr class="key" title=""></abbr> -->
-  </p>
-
-  <!-- hCalendar -->
-  <frameset>
-    <frame id="frame1">
-      <div>
-        <span class="notAMicroformat" id="notme">
-          <abbr> class="foo">I am not a microformat</abbr>
-          <abbr> class="title">Foolish title, not a format</abbr>
-        </span>
-      </div>
-    </frame>
-    <frame id="frame3">
-      <span class="geo" id="02-geo-abbr-latlong" >
-        <abbr class="latitude" title="75.77">Far Northern</abbr>
-        <abbr class="longitude" title="-122.41">Canada</abbr>
-      </span>
-    <frame id="frame2">
-      <div class="vcalendar">
-        <span class="vevent" id="15-calendar-xml-lang">
-          <a class="url" href="http://www.web2con.com/">
-          <span class="summary">Web 2.0 Conference</span>: 
-          <abbr class="dtstart" title="2005-10-05">October 5</abbr>-
-          <abbr class="dtend" title="2005-10-08">7</abbr>,
-          at the <span class="location">Argent Hotel, San Francisco, CA</span>
-          </a>
-        </span>
-      </div>
-    </frame>
-  </frameset>
-
-  <!-- Put the test code before the iframe just to be
-    extra-sure it's been evaluated before the iframe onload fires -->
-  <script class="testbody" type="text/javascript">
-  // Called from the onload of the iframe
-  function test_MicroformatsAPI() {
-    // I'm going to try to do this without getting XPConnect priv's, make sure
-    // we throw
-    try {
-      Components.utils.import("resource://gre/modules/Microformats.js");
-      ok(false, "Should not execute this code");
-    } catch(ex) {
-      ok(true, "Expected exception");
-    }
-
-    // Gonna do things the right way
-    var Microformats = SpecialPowers.Cu.import("resource://gre/modules/Microformats.js").Microformats;
-
-    // Test Microformat frame recursion - start with counting
-    var count = Microformats.count("adr",
-                                   document.getElementById("contentbody"),
-                                   {recurseExternalFrames: false}, // do not recurse frames
-                                   0);    // current count
-    is(count, 2, "No frame recursion, finds 2 adr's (none in frames)");
-
-    // Try it with frame recursion
-    count = Microformats.count("adr",
-                               document.getElementById("contentbody"),
-                               {recurseExternalFrames: true},
-                               count);
-    // Should still find 2
-    is(count, 2, "Only 2 adr nodes, even when recursing frames");
-
-    // Since "recurseExternalFrames" only affects the external frames, the microformat
-    // in the <frameset> will always be counted.
-    count = Microformats.count("geo",
-                              document.getElementById("contentbody"),
-                              {recurseExternalFrames: false},
-                              0);
-    is(count, 2, "Should find two geo if we don't recurse external frames");
-
-    count = Microformats.count("geo",
-                              document.getElementById("contentbody"),
-                              {recurseExternalFrames: true},
-                              0);
-    is(count, 3, "Three geos,one outside, one in a frameset, and one in iframe");
-
-    count = Microformats.count("hCalendar",
-                               document.getElementById("contentbody"),
-                               {recurseExternalFrames: true},
-                               0);
-    is(count, 1, "One hCalendar");
-
-    count = Microformats.count("hCard",
-                               document.getElementById("contentbody"),
-                               {recurseExternalFrames: true});
-    is(count, 1, "One hCard");
-  }
-  </script>
-
-  <!-- Geo Fire the test from here so we know this is loaded at the outset -->
-  <iframe src="geo.html" onload="test_MicroformatsAPI();">
-  </iframe>
-
-  <!-- adr -->
-  <div class="adr" id="01-extended-address">
-    <span class="extended-address">Park Bench</span>
-  </div>
-
-</body>
-</html>
deleted file mode 100644
--- a/toolkit/components/microformats/tests/test_Microformats_geo.html
+++ /dev/null
@@ -1,218 +0,0 @@
-<html>
-<head>
-  <meta http-equiv="Content-type" content="text/html; charset=utf-8" />
-  <title>Testing Microformats.js (geo)</title>
-  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
-  <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
-  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"></link>
-</head>
-<body>
-<div id="content" style="display: none">
-
-    <span class="geo" id="01-geo-basic">
-      <span class="latitude">37.77</span>
-      <span class="longitude">-122.411</span>
-    </span>
-
-
-    <span class="geo" id="01-geo-abbr-latlong" >
-      <abbr class="latitude" title="37.77">Northern</abbr>
-      <abbr class="longitude" title="-122.41">California</abbr>
-    </span>
-
-    <abbr class="geo" id="01-geo-abbr" title="30.267991;-97.739568">Paradise</abbr>
-
-    <span class="vcard">
-    <span class="fn org">John Doe</span>
-    <abbr class="geo" id="02-geo-vcard-01" title="37.77;-122.41"></abbr>
-    </span>
-
-    <span class="vcard">
-    <span class="fn org">John Doe</span>
-    <abbr class="geo" id="02-geo-vcard-02" title="37.77;-122.41">Northern California</abbr>
-    </span>
-
-    <span class="vevent">
-      <span class="summary">SXSW Interactive (South by Southwest)</span>
-      <ABBR title="20080307" class="dtstart">Friday, March 7, 2008</ABBR>
-      -
-      <ABBR title="20080311" class="dtend">Tuesday, March 11, 2008</ABBR>
-      <abbr class="geo"  id="02-geo-vevent-01" title="30.2622;-97.7399"></abbr>
-    </span>
-
-    <span class="vevent">
-      <span class="summary">SXSW Interactive (South by Southwest)</span>
-      <ABBR title="20080307" class="dtstart">Friday, March 7, 2008</ABBR>
-      -
-      <ABBR title="20080311" class="dtend">Tuesday, March 11, 2008</ABBR>
-      <abbr class="geo"  id="02-geo-vevent-02" title="30.2622;-97.7399">Convention Center</abbr>
-    </span>
-
-<h3>Legal geos</h3>
-<ul>
-<li><span class="geo" id="legal_geo1"><span class="latitude">0</span>,<span class="longitude">0</span></span></li>
-<li><span class="geo" id="legal_geo2"><span class="latitude">0.0</span>,<span class="longitude">0.0</span></span></li>
-<li><span class="geo" id="legal_geo3"><span class="latitude">0.</span>,<span class="longitude">0.</span></span></li>
-</ul>
-<h3>Illegal geos</h3>
-
-<ul>
-<li><span class="geo" id="ill_geo1"><span class="latitude">abc</span>,<span class="longitude">def</span></span></li>
-<li><span class="geo" id="ill_geo2"><span class="latitude">12.s2</span>,<span class="longitude">1d.23</span></span></li>
-<li><span class="geo" id="ill_geo3"><span class="latitude">999.99</span>,<span class="longitude">999</span></span></li>
-<li><span class="geo" id="ill_geo4"><span class="latitude">-181</span>,<span class="longitude">-361</span></span></li>
-<li><span class="geo" id="ill_geo5">abc;def</span></li>
-<li><span class="geo" id="ill_geo6">12.s2;1d.23</span></li>
-<li><span class="geo" id="ill_geo7">999.99;999</span></li>
-<li><span class="geo" id="ill_geo8">-181;-361</span></li>
-<li><span class="geo" id="ill_geo9">-18;;-31</span></li>
-<li><ABBR title="+23.70000;+90.30000" class="extra_geo">Dhaka, Bangladesh</ABBR></li>
-<li><span class="geo" id="zero_geo">0;0</span></li>
-</ul>
-    
-    
-    
-    
-    
-    
-</div>
-<pre id="test">
-<script class="testbody" type="text/javascript">
-
-test_Microformats();
-test_geo();
-
-function test_Microformats() {
-  var Microformats = SpecialPowers.Cu.import("resource://gre/modules/Microformats.js").Microformats;
-
-  ok(Microformats, "Check global access to Microformats");
-}
-
-function test_geo() {
-  var geo = SpecialPowers.Cu.import("resource://gre/modules/Microformats.js").geo;
-
-  var Geo;
-
-  Geo = new geo(document.getElementById("01-geo-basic"));
-
-  is(Geo.latitude, 37.77, "01-geo-basic - latitude");
-  is(Geo.longitude, -122.411, "01-geo-basic - longitude");
-
-  Geo = new geo(document.getElementById("01-geo-abbr-latlong"));
-
-  is(Geo.latitude, 37.77, "02-geo-abbr-latlong - latitude");
-  is(Geo.longitude, -122.41, "02-geo-abbr-latlong - longitude");
-
-  Geo = new geo(document.getElementById("01-geo-abbr"));
-
-  is(Geo.latitude, 30.267991, "01-geo-abbr - latitude");
-  is(Geo.longitude, -97.739568, "01-geo-abbr - longitude");
-
-  Geo = new geo(document.getElementById("02-geo-vcard-01"));
-
-  is(Geo.toString(), "John Doe", "02-geo-vcard-01");
-
-  Geo = new geo(document.getElementById("02-geo-vcard-02"));
-
-  is(Geo.toString(), "Northern California", "02-geo-vcard-02");
-
-  Geo = new geo(document.getElementById("02-geo-vevent-01"));
-
-  is(Geo.toString(), "SXSW Interactive (South by Southwest)", "02-geo-vevent-01");
-
-  Geo = new geo(document.getElementById("02-geo-vevent-02"));
-
-  is(Geo.toString(), "Convention Center", "02-geo-vevent-02");
-
-  Geo = new geo(document.getElementById("legal_geo1"));
-
-  is(Geo.latitude, 0, "legal_geo1 - lat");
-  is(Geo.longitude, 0, "legal_geo1 - long");
-
-  Geo = new geo(document.getElementById("legal_geo2"));
-
-  is(Geo.latitude, 0, "legal_geo2 - lat");
-  is(Geo.longitude, 0, "legal_geo2 - long");
-
-  Geo = new geo(document.getElementById("legal_geo3"));
-
-  is(Geo.latitude, 0, "legal_geo3 - lat");
-  is(Geo.longitude, 0, "legal_geo3 - long");
-
-
-
-
-  try {
-    Geo = new geo(document.getElementById("ill_geo1"), true);
-    ok(0, "ill_geo1 - should have been caught as invalid geo");
-  } catch (ex) {
-    ok(1, "ill_geo1 - caught invalid geo");
-  }
-  try {
-    Geo = new geo(document.getElementById("ill_geo2"), true);
-    ok(0, "ill_geo2 - should have been caught as invalid geo");
-  } catch (ex) {
-    ok(1, "ill_geo2 - caught invalid geo");
-  }
-  try {
-    Geo = new geo(document.getElementById("ill_geo3"), true);
-    ok(0, "ill_geo3 - should have been caught as invalid geo");
-  } catch (ex) {
-    ok(1, "ill_geo3 - caught invalid geo");
-  }
-  try {
-    Geo = new geo(document.getElementById("ill_geo4"), true);
-    ok(0, "ill_geo4 - should have been caught as invalid geo");
-  } catch (ex) {
-    ok(1, "ill_geo4 - caught invalid geo");
-  }
-  try {
-    Geo = new geo(document.getElementById("ill_geo5"), true);
-    ok(0, "ill_geo5 - should have been caught as invalid geo");
-  } catch (ex) {
-    ok(1, "ill_geo5 - caught invalid geo");
-  }
-  try {
-    Geo = new geo(document.getElementById("ill_geo6"), true);
-    ok(0, "ill_geo6 - should have been caught as invalid geo");
-  } catch (ex) {
-    ok(1, "ill_geo6 - caught invalid geo");
-  }
-  try {
-    Geo = new geo(document.getElementById("ill_geo7"), true);
-    ok(0, "ill_geo7 - should have been caught as invalid geo");
-  } catch (ex) {
-    ok(1, "ill_geo7 - caught invalid geo");
-  }
-  try {
-    Geo = new geo(document.getElementById("ill_geo8"), true);
-    ok(0, "ill_geo8 - should have been caught as invalid geo");
-  } catch (ex) {
-    ok(1, "ill_geo8 - caught invalid geo");
-  }
-  try {
-    Geo = new geo(document.getElementById("ill_geo9"), true);
-    ok(0, "ill_geo9 - should have been caught as invalid geo");
-  } catch (ex) {
-    ok(1, "ill_geo9 - caught invalid geo");
-  }
-  try {
-    Geo = new geo(document.getElementById("zero_geo"), true);
-    ok(1, "zero_geo - creation succeeded");
-  } catch (ex) {
-    ok(0, "zero_geo - creation failed");
-  }
-  try {
-    Geo = new geo(document.getElementById("extra_geo"), true);
-    ok(1, "extra_geo - creation succeeded");
-  } catch (ex) {
-    ok(0, "extra_geo - creation failed");
-  }
-}
-
-
-
-</script>
-</pre>
-</body>
-</html>
deleted file mode 100644
--- a/toolkit/components/microformats/tests/test_Microformats_getters.html
+++ /dev/null
@@ -1,156 +0,0 @@
-<html>
-<head>
-  <title>Testing Mixed Up Microformat APIs</title>
-  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
-  <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
-  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"></link>
-</head>
-<body id="contentbody">
-  <pre id="test">
-  <script class="testbody" type="text/javascript">
-
-  // Called from onload in iframe
-  function test_MicroformatsAPI() {
-    var Microformats = SpecialPowers.Cu.import("resource://gre/modules/Microformats.js").Microformats;
-
-    // Test that we can get them all
-    var mfs = [];
-    mfs = Microformats.get("adr",
-                           document.getElementById("content"),
-                           {showHidden: true});
-
-    is(mfs.length, 2, "Two adr's in our array");
-
-    mfs = Microformats.get("geo",
-                           document.getElementById("content"),
-                           {recurseExternalFrames: true});
-    is(mfs.length, 3, "Three geo's in our array");
-
-    mfs = Microformats.get("hCalendar",
-                            document.getElementById("content"),
-                            {recurseExternalFrames: false});
-    // Should get the hCalendar whether we recurseExternalFrames or not.
-    is(mfs.length, 2, "Two hCalendar returned not recursing frames");
-
-    mfs = Microformats.get("hCalendar",
-                           document.getElementById("content"),
-                           {recurseExternalFrames: true});
-    is(mfs.length, 2, "Two hCalendars returned recursing frames");
-
-    mfs = Microformats.get("hCard",
-                           document.getElementById("content"),
-                           {recurseExternalFrames: true},
-                           mfs);
-    is(mfs.length, 3, "Two hCalendars and one hCard");
-
-    mfs = Microformats.get("hCalendar", document.getElementById("secondnode"));
-
-    is(mfs[0].summary, "Pseudo Conference",
-       "Make sure we get the proper hCalendar from the second level node");
-    is(mfs.length, 1, "And we should only get one hCalendar not two from this node.");
-  }
-  </script>
-  </pre>
-  <div id="content">
-    <!-- hCard -->
-    <p class="vcard" id="23-abbr-title-everything">
-      <!-- perhaps the most annoying test ever -->
-      <abbr class="fn" title="John Doe">foo</abbr>
-      <span class="n">
-        <abbr class="honorific-prefix" title="Mister">Mr.</abbr>
-        <abbr class="given-name" title="Jonathan">John</abbr>
-        <abbr class="additional-name" title="John">J</abbr>
-        <abbr class="family-name" title="Doe-Smith">Doe</abbr>
-        <abbr class="honorific-suffix" title="Medical Doctor">M.D</abbr>
-      </span>
-      <abbr class="nickname" title="JJ">jj</abbr>
-      <abbr class="bday" title="2006-04-04">April 4, 2006</abbr>
-      <span class="adr">
-        <abbr class="post-office-box" title="Box 1234">B. 1234</abbr>
-        <abbr class="extended-address" title="Suite 100">Ste. 100</abbr>
-        <abbr class="street-address" title="123 Fake Street">123 Fake St.</abbr>
-        <abbr class="locality" title="San Francisco">San Fran</abbr>
-        <abbr class="region" title="California">CA</abbr>
-        <abbr class="postal-code" title="12345-6789">12345</abbr>
-        <abbr class="country-name" title="United States of America">USA</abbr>
-        <abbr class="type" title="work">workplace</abbr>
-      </span>
-      <abbr class="tel" title="415.555.1234">1234</abbr>
-      <abbr class="tel-type-value" title="work">workplace</abbr>
-  <!--       mailer  -->
-      <abbr class="tz" title="-0700">Pacific Time</abbr>
-      <span class="geo">
-        <abbr class="latitude" title="37.77">Northern</abbr>
-        <abbr class="longitude" title="-122.41">California</abbr>
-      </span>
-      <abbr class="title" title="President">pres.</abbr> and
-      <abbr class="role" title="Chief">cat wrangler</abbr>
-  <!--       <span class="agent"></span> -->
-      <span class="org">
-        <abbr class="organization-name" title="Intellicorp">foo</abbr>
-        <abbr class="organization-unit" title="Intelligence">bar</abbr>
-      </span>
-  <!--       <abbr class="category" title=""></abbr> -->
-      <abbr class="note" title="this is a note">this is not a note</abbr>
-  <!--       <abbr class="rev" title=""></abbr>  (revision datetime) -->
-  <!--       <abbr class="sort-string" title=""></abbr> -->
-      <abbr class="uid" title="abcdefghijklmnopqrstuvwxyz">alpha</abbr>
-      <abbr class="class" title="public">pub</abbr>
-  <!--       <abbr class="key" title=""></abbr> -->
-    </p>
-
-    <!-- hCalendar -->
-    <frameset>
-      <frame id="frame1">
-        <div>
-          <span class="notAMicroformat" id="notme">
-            <abbr> class="foo">I am not a microformat</abbr>
-            <abbr> class="title">Foolish title, not a format</abbr>
-          </span>
-        </div>
-      </frame>
-      <frame id="frame3">
-        <span class="geo" id="02-geo-abbr-latlong" >
-          <abbr class="latitude" title="75.77">Far Northern</abbr>
-          <abbr class="longitude" title="-122.41">Canada</abbr>
-        </span>
-      <frame id="frame2">
-        <div class="vcalendar">
-          <span class="vevent" id="15-calendar-xml-lang">
-            <a class="url" href="http://www.web2con.com/">
-            <span class="summary">Web 2.0 Conference</span>: 
-            <abbr class="dtstart" title="2005-10-05">October 5</abbr>-
-            <abbr class="dtend" title="2005-10-08">7</abbr>,
-            at the <span class="location">Argent Hotel, San Francisco, CA</span>
-            </a>
-          </span>
-        </div>
-      </frame>
-    </frameset>
-
-    <div id="secondnode">
-      <span>some interesting content</span>
-
-      <!-- Geo Fire test once we know this is loaded.-->
-      <iframe src="geo.html" onload="test_MicroformatsAPI();">
-      </iframe>
-
-      <!-- adr -->
-      <div class="adr" id="01-extended-address">
-        <span class="extended-address">Park Bench</span>
-      </div>
-
-      <div class="vcalendar">
-        <span class="vevent" id="16-calendar-xml-lang">
-          <a class="url" href="http://www.foo.com/">
-          <span class="summary">Pseudo Conference</span>: 
-          <abbr class="dtstart" title="2008-04-01">April 1</abbr>-
-          <abbr class="dtend" title="2008-04-03">April 3</abbr>,
-          at the <span class="location">Argent Hotel, San Francisco, CA</span>
-          </a>
-        </span>
-      </div>
-    </div>
-  </div>
-</body>
-</html>
deleted file mode 100644
--- a/toolkit/components/microformats/tests/test_Microformats_hCalendar.html
+++ /dev/null
@@ -1,306 +0,0 @@
-<html>
-<head>
-  <meta http-equiv="Content-type" content="text/html; charset=utf-8" />
-  <title>Testing Microformats.js (hCalendar)</title>
-  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
-  <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
-  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"></link>
-</head>
-<body>
-<div id="content" style="display: none">
-
-  From http://hg.microformats.org/tests
-
-  <div class="vevent" id="01-component-vevent-dtstart-date">
-    <div>Dates: <abbr class="dtstart" title="19970903">September 3, 1997</abbr></div>
-  </div>
-
-  <div class="vevent" id="02-component-vevent-dtstart-datetime">
-    <div>Dates: <abbr class="dtstart" title="19970903T163000Z">September 3, 1997, 16:30</abbr></div>
-  </div>
-
-  <div class="vevent" id="03-component-vevent-dtend-date">
-    <div>Dates: <abbr class="dtstart" title="19970903">September 3, 1997</abbr>
-    <abbr class="dtend" title="19970904">( all day )</abbr></div>
-  </div>
-
-  <div class="vevent" id="04-component-vevent-dtend-datetime">
-    <div>Date: <abbr class="dtstart" title="19970903T160000Z">September 3, 1997 at 4pm</abbr>
-    <abbr class="dtend" title="19970903T180000Z"> for 2 hours.</abbr></div>
-  </div>
-
-  <div class="vcalendar">
-    <span class="vevent" id="05-calendar-simple">
-      <a class="url" href="http://www.web2con.com/">
-        <span class="summary">Web 2.0 Conference</span>: 
-        <abbr class="dtstart" title="2005-10-05">October 5</abbr>-
-        <abbr class="dtend" title="2005-10-08">7</abbr>,
-        at the <span class="location">Argent Hotel, San Francisco, CA</span>
-      </a>
-    </span>
-  </div>
-
-  <p class="vevent" id="06-component-vevent-uri-relative">
-    <a class="url summary" href="/squidlist/calendar/12279/2006/1/15">Bad Movie Night - Gigli (blame mike spiegelman)</a>
-    <br />
-    <abbr class="dtstart" title="20060115T000000">Sun, Jan 15 : 8pm</abbr>
-    <br />
-  </p>
-
-  <div class="vevent" id="07-component-vevent-description-simple">
-    <div class="description">Project xyz Review Meeting Minutes</div>
-  </div>
-
-  <div class="aaa vevent" id="08-component-vevent-multiple-classes">
-    <a class="bbb url" href="http://www.web2con.com/">
-      <span class="ccc summary">Web 2.0 Conference</span>: 
-      <abbr class="ddd dtstart" title="2005-10-05">October 5</abbr>-
-      <abbr class="eee dtend" title="2005-10-08">7</abbr>,
-      at the <span class="fff location">Argent Hotel, San Francisco, CA</span>
-    </a>
-  </div>
-
-  <ul>
-    <li class="vevent" id="09-component-vevent-summary-in-img-alt">
-     <a class="url" href="http://conferences.oreillynet.com/et2006/">
-      <img style="display:block" class="summary" 
-       src="http://mochi.test:8888/tests/browser/microformats/test/picture.png"
-       alt="O'Reilly Emerging Technology Conference" />
-      <abbr class="dtstart" title="20060306">
-        3/6</abbr>-<abbr class="dtend" title="20060310">9</abbr>
-      @
-      <span class="location">
-         Manchester Grand Hyatt in San Diego, CA
-       </span>
-     </a>
-    </li>
-  </ul>
-
-  <div class="vcalendar">
-    <div class="vevent" id="10-component-vevent-entity">
-      <div class="summary">Cricket &amp; Tennis Centre</div>
-      <div class="description">Melbourne&apos;s Cricket &amp; Tennis Centres are in the heart of the city</div>
-    </div>
-  </div>
-
-  <p class="schedule vevent" id="11-component-vevent-summary-in-subelements">
-    <span class="summary">
-      <span style="font-weight:bold; color: #3E4876;">Welcome!</span>
-      <a href="/cs/web2005/view/e_spkr/1852">John Battelle</a>,
-      <a href="/cs/web2005/view/e_spkr/416">Tim O'Reilly</a>
-    </span>
-    <br />
-    <b>Time:</b>
-    <abbr class="dtstart" title="20051005T1630-0700">4:30pm</abbr>-
-    <abbr class="dtend" title="20051005T1645-0700">4:45pm
-    </abbr>
-  </p>
-
-  <p class="vevent" id="12-component-vevent-summary-url-in-same-class">
-    <a class="url summary" href="http://www.laughingsquid.com/squidlist/calendar/12377/2006/1/25">Art Reception for Tom Schultz and Felix Macnee</a>
-    <br />
-    <abbr class="dtstart" title="20060125T000000">Wed, Jan 25 : 6:00 pm - 9:00 pm</abbr>
-    <br />
-  </p>
-
-  <div class="vcalendar">
-    <div class="vevent" id="13-component-vevent-summary-url-property">
-      <span class="summary">
-        <a class="url" href="http://dps1.travelocity.com/dparcobrand.ctl?smls=Y&amp;Service=YHOE&amp;.intl=us&amp;aln_name=AA&amp;flt_num=1655&amp;dep_arp_name=&amp;arr_arp_name=&amp;dep_dt_dy_1=23&amp;dep_dt_mn_1=Jan&amp;dep_dt_yr_1=2006&amp;dep_tm_1=9:00am">ORD-SFO/AA 1655</a>
-      </span>
-    </div>
-  </div>
-
-  <div class="vcalendar">
-    <span class="vevent" id="15-calendar-xml-lang">
-      <a class="url" href="http://www.web2con.com/">
-      <span class="summary">Web 2.0 Conference</span>: 
-      <abbr class="dtstart" title="2005-10-05">October 5</abbr>-
-      <abbr class="dtend" title="2005-10-08">7</abbr>,
-      at the <span class="location">Argent Hotel, San Francisco, CA</span>
-      </a>
-    </span>
-  </div>
-
-    <div class="vcalendar">
-      <span class="vevent" id="16-calendar-force-outlook">
-        <a class="url" href="http://www.web2con.com/">
<