Merge m-c to m-i.
authorMs2ger <ms2ger@gmail.com>
Thu, 15 Nov 2012 10:14:44 +0100
changeset 113367 92a92a0d4f9d29109b54a00ec29c85c0182904f2
parent 113310 4df2bf1d77e93eecfb7a0183bdd58641e691b20a (current diff)
parent 113366 a9500b386854cf11cf9ffed05dc1921386bdb61d (diff)
child 113368 31fdd0d6bdd386c139e5f79d6599745e75862a09
push id23869
push useremorley@mozilla.com
push dateThu, 15 Nov 2012 16:18:16 +0000
treeherdermozilla-central@a37525d304d9 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone19.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge m-c to m-i.
browser/components/privatebrowsing/test/browser/global/browser_privatebrowsing_openLocationLastURL.js
browser/components/privatebrowsing/test/browser/global/browser_privatebrowsing_popupblocker.js
browser/components/privatebrowsing/test/browser/global/popup.html
dom/interfaces/core/nsIDOMCaretPosition.idl
--- a/accessible/src/base/AccEvent.cpp
+++ b/accessible/src/base/AccEvent.cpp
@@ -85,17 +85,17 @@ AccEvent::CreateXPCOMObject()
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // AccEvent cycle collection
 
 NS_IMPL_CYCLE_COLLECTION_NATIVE_CLASS(AccEvent)
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_NATIVE(AccEvent)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mAccessible)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mAccessible)
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NATIVE_BEGIN(AccEvent)
   NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mAccessible");
   cb.NoteXPCOMChild(static_cast<nsIAccessible*>(tmp->mAccessible));
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(AccEvent, AddRef)
--- a/accessible/src/base/NotificationController.cpp
+++ b/accessible/src/base/NotificationController.cpp
@@ -63,21 +63,19 @@ NS_IMPL_CYCLE_COLLECTION_NATIVE_CLASS(No
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_NATIVE(NotificationController)
   if (tmp->mDocument)
     tmp->Shutdown();
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NATIVE_BEGIN(NotificationController)
   NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mDocument");
   cb.NoteXPCOMChild(static_cast<nsIAccessible*>(tmp->mDocument.get()));
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSTARRAY_MEMBER(mHangingChildDocuments,
-                                                    DocAccessible)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSTARRAY_MEMBER(mContentInsertions,
-                                                    ContentInsertion)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSTARRAY_MEMBER(mEvents, AccEvent)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mHangingChildDocuments)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mContentInsertions)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEvents)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(NotificationController, AddRef)
 NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(NotificationController, Release)
 
 ////////////////////////////////////////////////////////////////////////////////
 // NotificationCollector: public
 
@@ -865,17 +863,17 @@ NotificationController::ContentInsertion
   }
 
   return haveToUpdate;
 }
 
 NS_IMPL_CYCLE_COLLECTION_NATIVE_CLASS(NotificationController::ContentInsertion)
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_NATIVE(NotificationController::ContentInsertion)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mContainer)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mContainer)
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NATIVE_BEGIN(NotificationController::ContentInsertion)
   NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mContainer");
   cb.NoteXPCOMChild(static_cast<nsIAccessible*>(tmp->mContainer.get()));
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(NotificationController::ContentInsertion,
--- a/accessible/src/base/nsAccessiblePivot.cpp
+++ b/accessible/src/base/nsAccessiblePivot.cpp
@@ -52,28 +52,28 @@ nsAccessiblePivot::nsAccessiblePivot(Acc
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // nsISupports
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(nsAccessiblePivot)
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsAccessiblePivot)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR_AMBIGUOUS(mRoot, nsIAccessible)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR_AMBIGUOUS(mPosition, nsIAccessible)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRoot)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPosition)
   uint32_t i, length = tmp->mObservers.Length();
   for (i = 0; i < length; ++i) {
     cb.NoteXPCOMChild(tmp->mObservers[i]);
   }
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsAccessiblePivot)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mRoot)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mPosition)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK_NSTARRAY(mObservers)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mRoot)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mPosition)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mObservers)
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsAccessiblePivot)
   NS_INTERFACE_MAP_ENTRY(nsIAccessiblePivot)
   NS_INTERFACE_MAP_ENTRY(nsAccessiblePivot)
   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIAccessiblePivot)
 NS_INTERFACE_MAP_END
 
--- a/accessible/src/generic/Accessible.cpp
+++ b/accessible/src/generic/Accessible.cpp
@@ -93,18 +93,18 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_
   uint32_t i, length = tmp->mChildren.Length();
   for (i = 0; i < length; ++i) {
     NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mChildren[i]");
     cb.NoteXPCOMChild(static_cast<nsIAccessible*>(tmp->mChildren[i].get()));
   }
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(Accessible, nsAccessNode)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mParent)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK_NSTARRAY(mChildren)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mParent)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mChildren)
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_IMPL_ADDREF_INHERITED(Accessible, nsAccessNode)
 NS_IMPL_RELEASE_INHERITED(Accessible, nsAccessNode)
 
 nsresult
 Accessible::QueryInterface(REFNSIID aIID, void** aInstancePtr)
 {
--- a/accessible/src/generic/DocAccessible.cpp
+++ b/accessible/src/generic/DocAccessible.cpp
@@ -118,39 +118,36 @@ DocAccessible::~DocAccessible()
 
 
 ////////////////////////////////////////////////////////////////////////////////
 // nsISupports
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(DocAccessible)
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(DocAccessible, Accessible)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mDocument)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NATIVE_MEMBER(mNotificationController,
-                                                  NotificationController)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocument)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNotificationController)
 
   if (tmp->mVirtualCursor) {
-    NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NATIVE_MEMBER(mVirtualCursor,
-                                                    nsAccessiblePivot)
+    NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mVirtualCursor)
   }
 
   uint32_t i, length = tmp->mChildDocuments.Length();
   for (i = 0; i < length; ++i) {
-    NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR_AMBIGUOUS(mChildDocuments[i],
-                                                         nsIAccessible)
+    NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChildDocuments[i])
   }
 
   CycleCollectorTraverseCache(tmp->mAccessibleCache, &cb);
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(DocAccessible, Accessible)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mDocument)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mNotificationController)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mVirtualCursor)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK_NSTARRAY(mChildDocuments)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocument)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mNotificationController)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mVirtualCursor)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mChildDocuments)
   tmp->mDependentIDsHash.Clear();
   tmp->mNodeToAccessibleMap.Clear();
   ClearCache(tmp->mAccessibleCache);
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(DocAccessible)
   NS_INTERFACE_MAP_ENTRY(nsIAccessibleDocument)
   NS_INTERFACE_MAP_ENTRY(nsIDocumentObserver)
--- a/accessible/src/xul/XULTreeAccessible.cpp
+++ b/accessible/src/xul/XULTreeAccessible.cpp
@@ -60,22 +60,22 @@ XULTreeAccessible::
 
 ////////////////////////////////////////////////////////////////////////////////
 // XULTreeAccessible: nsISupports and cycle collection implementation
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(XULTreeAccessible)
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(XULTreeAccessible,
                                                   Accessible)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mTree)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTree)
   CycleCollectorTraverseCache(tmp->mAccessibleCache, &cb);
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(XULTreeAccessible, Accessible)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mTree)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mTree)
   ClearCache(tmp->mAccessibleCache);
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(XULTreeAccessible)
 NS_INTERFACE_MAP_END_INHERITING(Accessible)
 
 NS_IMPL_ADDREF_INHERITED(XULTreeAccessible, Accessible)
 NS_IMPL_RELEASE_INHERITED(XULTreeAccessible, Accessible)
@@ -699,22 +699,22 @@ XULTreeItemAccessibleBase::
 
 ////////////////////////////////////////////////////////////////////////////////
 // XULTreeItemAccessibleBase: nsISupports implementation
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(XULTreeItemAccessibleBase)
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(XULTreeItemAccessibleBase,
                                                   Accessible)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mTree)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTree)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(XULTreeItemAccessibleBase,
                                                 Accessible)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mTree)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mTree)
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_INTERFACE_TABLE_HEAD_CYCLE_COLLECTION_INHERITED(XULTreeItemAccessibleBase)
   NS_INTERFACE_TABLE_INHERITED1(XULTreeItemAccessibleBase,
                                 XULTreeItemAccessibleBase)
 NS_INTERFACE_TABLE_TAIL_INHERITING(Accessible)
 NS_IMPL_ADDREF_INHERITED(XULTreeItemAccessibleBase, Accessible)
 NS_IMPL_RELEASE_INHERITED(XULTreeItemAccessibleBase, Accessible)
@@ -1103,22 +1103,22 @@ XULTreeItemAccessible::
 
 ////////////////////////////////////////////////////////////////////////////////
 // XULTreeItemAccessible: nsISupports implementation
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(XULTreeItemAccessible)
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(XULTreeItemAccessible,
                                                   XULTreeItemAccessibleBase)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mColumn)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mColumn)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(XULTreeItemAccessible,
                                                 XULTreeItemAccessibleBase)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mColumn)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mColumn)
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(XULTreeItemAccessible)
 NS_INTERFACE_MAP_END_INHERITING(XULTreeItemAccessibleBase)
 NS_IMPL_ADDREF_INHERITED(XULTreeItemAccessible, XULTreeItemAccessibleBase)
 NS_IMPL_RELEASE_INHERITED(XULTreeItemAccessible, XULTreeItemAccessibleBase)
 
 ////////////////////////////////////////////////////////////////////////////////
--- a/accessible/src/xul/XULTreeGridAccessible.cpp
+++ b/accessible/src/xul/XULTreeGridAccessible.cpp
@@ -473,24 +473,24 @@ XULTreeGridCellAccessible::
 
 ////////////////////////////////////////////////////////////////////////////////
 // XULTreeGridCellAccessible: nsISupports implementation
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(XULTreeGridCellAccessible)
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(XULTreeGridCellAccessible,
                                                   LeafAccessible)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mTree)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mColumn)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTree)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mColumn)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(XULTreeGridCellAccessible,
                                                 LeafAccessible)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mTree)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mColumn)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mTree)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mColumn)
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_INTERFACE_TABLE_HEAD_CYCLE_COLLECTION_INHERITED(XULTreeGridCellAccessible)
   NS_INTERFACE_TABLE_INHERITED2(XULTreeGridCellAccessible,
                                 nsIAccessibleTableCell,
                                 XULTreeGridCellAccessible)
 NS_INTERFACE_TABLE_TAIL_INHERITING(LeafAccessible)
 NS_IMPL_ADDREF_INHERITED(XULTreeGridCellAccessible, LeafAccessible)
--- a/browser/base/content/browser-social.js
+++ b/browser/base/content/browser-social.js
@@ -7,16 +7,17 @@ const PANEL_MIN_HEIGHT = 100;
 const PANEL_MIN_WIDTH = 330;
 
 let SocialUI = {
   // Called on delayed startup to initialize UI
   init: function SocialUI_init() {
     Services.obs.addObserver(this, "social:pref-changed", false);
     Services.obs.addObserver(this, "social:ambient-notification-changed", false);
     Services.obs.addObserver(this, "social:profile-changed", false);
+    Services.obs.addObserver(this, "social:recommend-info-changed", false);
     Services.obs.addObserver(this, "social:frameworker-error", false);
 
     Services.prefs.addObserver("social.sidebar.open", this, false);
     Services.prefs.addObserver("social.toast-notifications.enabled", this, false);
 
     gBrowser.addEventListener("ActivateSocialFeature", this._activationEventHandler, true, true);
 
     // Called when we enter DOM full-screen mode.
@@ -28,16 +29,17 @@ let SocialUI = {
     Social.init(this._providerReady.bind(this));
   },
 
   // Called on window unload
   uninit: function SocialUI_uninit() {
     Services.obs.removeObserver(this, "social:pref-changed");
     Services.obs.removeObserver(this, "social:ambient-notification-changed");
     Services.obs.removeObserver(this, "social:profile-changed");
+    Services.obs.removeObserver(this, "social:recommend-info-changed");
     Services.obs.removeObserver(this, "social:frameworker-error");
 
     Services.prefs.removeObserver("social.sidebar.open", this);
     Services.prefs.removeObserver("social.toast-notifications.enabled", this);
   },
 
   showProfile: function SocialUI_showProfile() {
     if (this.haveLoggedInUser())
@@ -69,16 +71,19 @@ let SocialUI = {
         SocialToolbar.updateButton();
         SocialMenu.populate();
         break;
       case "social:profile-changed":
         SocialToolbar.updateProfile();
         SocialShareButton.updateProfileInfo();
         SocialChatBar.update();
         break;
+      case "social:recommend-info-changed":
+        SocialShareButton.updateShareState();
+        break;
       case "social:frameworker-error":
         if (Social.provider) {
           Social.errorState = "frameworker-error";
           SocialSidebar.setSidebarErrorMessage("frameworker-error");
         }
         break;
       case "nsPref:changed":
         SocialSidebar.update();
@@ -249,18 +254,20 @@ let SocialChatBar = {
   update: function() {
     if (!this.isAvailable)
       this.chatbar.removeAll();
     else {
       this.chatbar.hidden = document.mozFullScreen;
     }
   },
   focus: function SocialChatBar_focus() {
+    if (!this.chatbar.selectedChat)
+      return;
     let commandDispatcher = gBrowser.ownerDocument.commandDispatcher;
-    commandDispatcher.advanceFocusIntoSubtree(this.chatbar);
+    commandDispatcher.advanceFocusIntoSubtree(this.chatbar.selectedChat);
   }
 }
 
 function sizeSocialPanelToContent(panel, iframe) {
   // FIXME: bug 764787: Maybe we can use nsIDOMWindowUtils.getRootBounds() here?
   let doc = iframe.contentDocument;
   if (!doc || !doc.body) {
     return;
@@ -448,101 +455,37 @@ let SocialFlyout = {
       panel.openPopup(anchor, "start_before", 0, yOffset, false, false);
       this.setUpProgressListener();
     }
     this.yOffset = yOffset;
   }
 }
 
 let SocialShareButton = {
-  // promptImages and promptMessages being null means we are yet to get the
-  // message back from the provider with the images and icons (or that we got
-  // the response but determined it was invalid.)
-  promptImages: null,
-  promptMessages: null,
-
   // Called once, after window load, when the Social.provider object is initialized
   init: function SSB_init() {
     this.updateButtonHiddenState();
     this.updateProfileInfo();
   },
 
   updateProfileInfo: function SSB_updateProfileInfo() {
     let profileRow = document.getElementById("unsharePopupHeader");
     let profile = Social.provider.profile;
-    this.promptImages = null;
-    this.promptMessages = null;
     if (profile && profile.displayName) {
       profileRow.hidden = false;
       let portrait = document.getElementById("socialUserPortrait");
       portrait.setAttribute("src", profile.portrait || "chrome://global/skin/icons/information-32.png");
       let displayName = document.getElementById("socialUserDisplayName");
       displayName.setAttribute("label", profile.displayName);
     } else {
       profileRow.hidden = true;
       this.updateButtonHiddenState();
       return;
     }
-    // XXX - this shouldn't be done as part of updateProfileInfo, but instead
-    // whenever we notice the provider has changed - but the concept of
-    // "provider changed" will only exist once bug 774520 lands. 
-    // get the recommend-prompt info.
-    let port = Social.provider.getWorkerPort();
-    if (port) {
-      port.onmessage = function(evt) {
-        if (evt.data.topic == "social.user-recommend-prompt-response") {
-          port.close();
-          this.acceptRecommendInfo(evt.data.data);
-          this.updateButtonHiddenState();
-          this.updateShareState();
-        }
-      }.bind(this);
-      port.postMessage({topic: "social.user-recommend-prompt"});
-    }
-  },
-
-  acceptRecommendInfo: function SSB_acceptRecommendInfo(data) {
-    // Accept *and validate* the user-recommend-prompt-response message.
-    let promptImages = {};
-    let promptMessages = {};
-    function reportError(reason) {
-      Cu.reportError("Invalid recommend data from provider: " + reason + ": sharing is disabled for this provider");
-      return false;
-    }
-    if (!data ||
-        !data.images || typeof data.images != "object" ||
-        !data.messages || typeof data.messages != "object") {
-      return reportError("data is missing valid 'images' or 'messages' elements");
-    }
-    for (let sub of ["share", "unshare"]) {
-      let url = data.images[sub];
-      if (!url || typeof url != "string" || url.length == 0) {
-        return reportError('images["' + sub + '"] is missing or not a non-empty string');
-      }
-      // resolve potentially relative URLs then check the scheme is acceptable.
-      url = Services.io.newURI(Social.provider.origin, null, null).resolve(url);
-      let uri = Services.io.newURI(url, null, null);
-      if (!uri.schemeIs("http") && !uri.schemeIs("https") && !uri.schemeIs("data")) {
-        return reportError('images["' + sub + '"] does not have a valid scheme');
-      }
-      promptImages[sub] = url;
-    }
-    for (let sub of ["shareTooltip", "unshareTooltip",
-                     "sharedLabel", "unsharedLabel", "unshareLabel",
-                     "portraitLabel", 
-                     "unshareConfirmLabel", "unshareConfirmAccessKey",
-                     "unshareCancelLabel", "unshareCancelAccessKey"]) {
-      if (typeof data.messages[sub] != "string" || data.messages[sub].length == 0) {
-        return reportError('messages["' + sub + '"] is not a valid string');
-      }
-      promptMessages[sub] = data.messages[sub];
-    }
-    this.promptImages = promptImages;
-    this.promptMessages = promptMessages;
-    return true;
+    this.updateShareState();
   },
 
   get shareButton() {
     return document.getElementById("share-button");
   },
   get unsharePopup() {
     return document.getElementById("unsharePopup");
   },
@@ -554,17 +497,17 @@ let SocialShareButton = {
   canSharePage: function SSB_canSharePage(aURI) {
     // We only allow sharing of http or https
     return aURI && (aURI.schemeIs('http') || aURI.schemeIs('https'));
   },
 
   updateButtonHiddenState: function SSB_updateButtonHiddenState() {
     let shareButton = this.shareButton;
     if (shareButton)
-      shareButton.hidden = !Social.uiVisible || this.promptImages == null ||
+      shareButton.hidden = !Social.uiVisible || Social.provider.recommendInfo == null ||
                            !SocialUI.haveLoggedInUser() ||
                            !this.canSharePage(gBrowser.currentURI);
   },
 
   onClick: function SSB_onClick(aEvent) {
     if (aEvent.button != 0)
       return;
 
@@ -578,26 +521,27 @@ let SocialShareButton = {
     function updateElement(id, attrs) {
       let el = document.getElementById(id);
       Object.keys(attrs).forEach(function(attr) {
         el.setAttribute(attr, attrs[attr]);
       });
     }
     let continueSharingButton = document.getElementById("unsharePopupContinueSharingButton");
     continueSharingButton.focus();
+    let recommendInfo = Social.provider.recommendInfo;
     updateElement("unsharePopupContinueSharingButton",
-                  {label: this.promptMessages.unshareCancelLabel,
-                   accesskey: this.promptMessages.unshareCancelAccessKey});
+                  {label: recommendInfo.messages.unshareCancelLabel,
+                   accesskey: recommendInfo.messages.unshareCancelAccessKey});
     updateElement("unsharePopupStopSharingButton",
-                  {label: this.promptMessages.unshareConfirmLabel,
-                  accesskey: this.promptMessages.unshareConfirmAccessKey});
+                  {label: recommendInfo.messages.unshareConfirmLabel,
+                  accesskey: recommendInfo.messages.unshareConfirmAccessKey});
     updateElement("socialUserPortrait",
-                  {"aria-label": this.promptMessages.portraitLabel});
+                  {"aria-label": recommendInfo.messages.portraitLabel});
     updateElement("socialUserRecommendedText",
-                  {value: this.promptMessages.unshareLabel});
+                  {value: recommendInfo.messages.unshareLabel});
   },
 
   sharePage: function SSB_sharePage() {
     this.unsharePopup.hidden = false;
 
     let uri = gBrowser.currentURI;
     if (!Social.isPageShared(uri)) {
       Social.sharePage(uri);
@@ -616,43 +560,44 @@ let SocialShareButton = {
   updateShareState: function SSB_updateShareState() {
     // we might have been called due to a location change, and the new location
     // might change the state of "can this url be shared"
     this.updateButtonHiddenState();
 
     let shareButton = this.shareButton;
     let currentPageShared = shareButton && !shareButton.hidden && Social.isPageShared(gBrowser.currentURI);
 
+    let recommendInfo = Social.provider ? Social.provider.recommendInfo : null;
     // Provide a11y-friendly notification of share.
     let status = document.getElementById("share-button-status");
     if (status) {
       // XXX - this should also be capable of reflecting that the page was
       // unshared (ie, it needs to manage three-states: (1) nothing done, (2)
       // shared, (3) shared then unshared)
       // Note that we *do* have an appropriate string from the provider for
-      // this (promptMessages['unsharedLabel'] but currently lack a way of
+      // this (recommendInfo.messages.unsharedLabel) but currently lack a way of
       // tracking this state)
-      let statusString = currentPageShared ?
-                           this.promptMessages['sharedLabel'] : "";
+      let statusString = currentPageShared && recommendInfo ?
+                           recommendInfo.messages.sharedLabel : "";
       status.setAttribute("value", statusString);
     }
 
     // Update the share button, if present
     if (!shareButton || shareButton.hidden)
       return;
 
     let imageURL;
     if (currentPageShared) {
       shareButton.setAttribute("shared", "true");
-      shareButton.setAttribute("tooltiptext", this.promptMessages['unshareTooltip']);
-      imageURL = this.promptImages["unshare"]
+      shareButton.setAttribute("tooltiptext", recommendInfo.messages.unshareTooltip);
+      imageURL = recommendInfo.images.unshare;
     } else {
       shareButton.removeAttribute("shared");
-      shareButton.setAttribute("tooltiptext", this.promptMessages['shareTooltip']);
-      imageURL = this.promptImages["share"]
+      shareButton.setAttribute("tooltiptext", recommendInfo.messages.shareTooltip);
+      imageURL = recommendInfo.images.share;
     }
     shareButton.src = imageURL;
   }
 };
 
 var SocialMenu = {
   populate: function SocialMenu_populate() {
     // This menu is only accessible through keyboard navigation.
--- a/browser/base/content/socialchat.xml
+++ b/browser/base/content/socialchat.xml
@@ -62,31 +62,16 @@
         ]]></body>
       </method>
     </implementation>
 
     <handlers>
       <handler event="focus" phase="capturing">
         this.parentNode.selectedChat = this;
       </handler>
-      <handler event="DOMContentLoaded"><![CDATA[
-        this.isActive = !this.minimized;
-        if (this._callback) this._callback(this.iframe.contentWindow);
-        let chatbox = this;
-        function chatActivity() {
-          chatbox.setAttribute("activity", true);
-          chatbox.parentNode.updateTitlebar(chatbox);
-        };
-        let iframeWindow = this.iframe.contentWindow;
-        iframeWindow.addEventListener("socialChatActivity", chatActivity);
-        iframeWindow.addEventListener("unload", function unload() {
-          iframeWindow.removeEventListener("unload", unload);
-          iframeWindow.removeEventListener("socialChatActivity", chatActivity);
-        });
-      ]]></handler>
       <handler event="DOMTitleChanged"><![CDATA[
         this.setAttribute('label', this.iframe.contentDocument.title);
         this.parentNode.updateTitlebar(this);
       ]]></handler>
       <handler event="DOMLinkAdded"><![CDATA[
         // much of this logic is from DOMLinkHandler in browser.js
         // this sets the presence icon for a chat user, we simply use favicon style updating
         let link = event.originalTarget;
@@ -274,18 +259,19 @@
           this.menuitemMap.set(aChatbox, menu);
           this.menupopup.appendChild(menu);
           this.nub.collapsed = false;
         ]]></body>
       </method>
 
       <method name="showChat">
         <parameter name="aChatbox"/>
+        <parameter name="aMode"/>
         <body><![CDATA[
-          if (aChatbox.minimized)
+          if ((aMode != "minimized") && aChatbox.minimized)
             aChatbox.minimized = false;
           if (this.selectedChat != aChatbox)
             this.selectedChat = aChatbox;
           if (!aChatbox.collapsed)
             return; // already showing - no more to do.
           this._showChat(aChatbox);
           // showing a collapsed chat might mean another needs to be collapsed
           // to make room...
@@ -338,41 +324,86 @@
           while (this.firstChild) {
             this._remove(this.firstChild);
           }
           // and the nub/popup must also die.
           this.nub.collapsed = true;
         ]]></body>
       </method>
 
+      <method name="initChatBox">
+        <!-- ideally this would be a method on the chatbox itself, but the
+             vagaries of XBL bindings means that in some edge cases the
+             chatbox methods don't exist yet when we need them to...
+        -->
+        <parameter name="aChatBox"/>
+        <parameter name="aProvider"/>
+        <parameter name="aURL"/>
+        <parameter name="aCallback"/>
+        <body><![CDATA[
+          // callbacks to be called when onload fires - more might be added
+          // if the same chat is requested before onload has fired.  Set back
+          // to null in DOMContentLoaded, so null means DOMContentLoaded has
+          // already fired and new callbacks can be made immediately.
+          aChatBox._callbacks = [aCallback];
+          let iframeWindow = aChatBox.iframe.contentWindow;
+          aChatBox.addEventListener("DOMContentLoaded", function DOMContentLoaded() {
+            aChatBox.removeEventListener("DOMContentLoaded", DOMContentLoaded);
+            aChatBox.isActive = !aChatBox.minimized;
+            for (let callback of aChatBox._callbacks) {
+              if (callback)
+                callback(iframeWindow);
+            }
+            aChatBox._callbacks = null;
+            function chatActivity() {
+              aChatBox.setAttribute("activity", true);
+              aChatBox.parentNode.updateTitlebar(aChatBox);
+            };
+
+            iframeWindow.addEventListener("socialChatActivity", chatActivity);
+            iframeWindow.addEventListener("unload", function unload() {
+              iframeWindow.removeEventListener("unload", unload);
+              iframeWindow.removeEventListener("socialChatActivity", chatActivity);
+            });
+          });
+
+          aChatBox.setAttribute("origin", aProvider.origin);
+          aChatBox.setAttribute("src", aURL);
+        ]]></body>
+      </method>
       <method name="openChat">
         <parameter name="aProvider"/>
         <parameter name="aURL"/>
         <parameter name="aCallback"/>
         <parameter name="aMode"/>
         <body><![CDATA[
           let cb = this.chatboxForURL.get(aURL);
           if (cb) {
             cb = cb.get();
             if (cb.parentNode) {
-              this.showChat(cb);
-              if (aCallback)
-                aCallback(cb.iframe.contentWindow);
+              this.showChat(cb, aMode);
+              if (aCallback) {
+                if (cb._callbacks == null) {
+                  // DOMContentLoaded has already fired, so callback now.
+                  aCallback(cb.iframe.contentWindow);
+                } else {
+                  // DOMContentLoaded for this chat is yet to fire...
+                  cb._callbacks.push(aCallback);
+                }
+              }
               return;
             }
             this.chatboxForURL.delete(aURL);
           }
           cb = document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul", "chatbox");
           if (aMode == "minimized")
             cb.setAttribute("minimized", "true");
           this.selectedChat = cb;
           this.insertBefore(cb, this.firstChild);
-          cb._callback = aCallback;
-          cb.setAttribute("origin", aProvider.origin);
-          cb.setAttribute("src", aURL);
+          this.initChatBox(cb, aProvider, aURL, aCallback);
           this.chatboxForURL.set(aURL, Cu.getWeakReference(cb));
           this.resize();
         ]]></body>
       </method>
 
       <method name="resize">
         <body><![CDATA[
         // Checks the current size against the collapsed state of children
--- a/browser/base/content/test/browser_social_chatwindow.js
+++ b/browser/base/content/test/browser_social_chatwindow.js
@@ -55,16 +55,95 @@ var tests = {
           ok(true, "got chatbox message");
           ok(e.data.result == "ok", "got chatbox windowRef result: "+e.data.result);
           chats.selectedChat.toggle();
           break;
       }
     }
     port.postMessage({topic: "test-init", data: { id: 1 }});
   },
+  testOpenMinimized: function(next) {
+    // In this case the sidebar opens a chat (without specifying minimized).
+    // We then minimize it and have the sidebar reopen the chat (again without
+    // minimized).  On that second call the chat should open and no longer
+    // be minimized.
+    let chats = document.getElementById("pinnedchats");
+    let port = Social.provider.getWorkerPort();
+    let seen_opened = false;
+    port.onmessage = function (e) {
+      let topic = e.data.topic;
+      switch (topic) {
+        case "test-init-done":
+          port.postMessage({topic: "test-chatbox-open"});
+          break;
+        case "chatbox-opened":
+          is(e.data.result, "ok", "the sidebar says it got a chatbox");
+          if (!seen_opened) {
+            // first time we got the opened message, so minimize the chat then
+            // re-request the same chat to be opened - we should get the
+            // message again and the chat should be restored.
+            ok(!chats.selectedChat.minimized, "chat not initially minimized")
+            chats.selectedChat.minimized = true
+            seen_opened = true;
+            port.postMessage({topic: "test-chatbox-open"});
+          } else {
+            // This is the second time we've seen this message - there should
+            // be exactly 1 chat open and it should no longer be minimized.
+            let chats = document.getElementById("pinnedchats");
+            ok(!chats.selectedChat.minimized, "chat no longer minimized")
+            chats.selectedChat.close();
+            is(chats.selectedChat, null, "should only have been one chat open");
+            port.close();
+            next();
+          }
+      }
+    }
+    port.postMessage({topic: "test-init", data: { id: 1 }});
+  },
+  // In this case the *worker* opens a chat (so minimized is specified).
+  // The worker then makes the same call again - as that second call also
+  // specifies "minimized" the chat should *not* be restored.
+  testWorkerChatWindowMinimized: function(next) {
+    const chatUrl = "https://example.com/browser/browser/base/content/test/social_chat.html";
+    let port = Social.provider.getWorkerPort();
+    let seen_opened = false;
+    ok(port, "provider has a port");
+    port.postMessage({topic: "test-init"});
+    port.onmessage = function (e) {
+      let topic = e.data.topic;
+      switch (topic) {
+        case "got-chatbox-message":
+          ok(true, "got a chat window opened");
+          let chats = document.getElementById("pinnedchats");
+          if (!seen_opened) {
+            // first time we got the opened message, so minimize the chat then
+            // re-request the same chat to be opened - we should get the
+            // message again and the chat should be restored.
+            ok(chats.selectedChat.minimized, "chatbox from worker opened as minimized");
+            seen_opened = true;
+            port.postMessage({topic: "test-worker-chat", data: chatUrl});
+            // Sadly there is no notification we can use to know the chat was
+            // re-opened :(  So we ask the chat window to "ping" us - by then
+            // the second request should have made it.
+            chats.selectedChat.iframe.contentWindow.wrappedJSObject.pingWorker();
+          } else {
+            // This is the second time we've seen this message - there should
+            // be exactly 1 chat open and it should still be minimized.
+            let chats = document.getElementById("pinnedchats");
+            ok(chats.selectedChat.minimized, "chat still minimized")
+            chats.selectedChat.close();
+            is(chats.selectedChat, null, "should only have been one chat open");
+            port.close();
+            next();
+          }
+          break;
+      }
+    }
+    port.postMessage({topic: "test-worker-chat", data: chatUrl});
+  },
   testManyChats: function(next) {
     // open enough chats to overflow the window, then check
     // if the menupopup is visible
     let port = Social.provider.getWorkerPort();
     ok(port, "provider has a port");
     port.postMessage({topic: "test-init"});
     let width = document.documentElement.boxObject.width;
     let numToOpen = (width / 200) + 1;
@@ -280,22 +359,64 @@ var tests = {
       ok(chatbar.nub.hasAttribute("activity"), "nub should also have activity");
       // first is collapsed, so use openChat to get it.
       chatbar.openChat(Social.provider, first.getAttribute("src"));
       ok(!first.hasAttribute("activity"), "first chat should no longer have activity");
       // The nub should lose the activity flag here too
       todo(!chatbar.nub.hasAttribute("activity"), "Bug 806266 - nub should no longer have activity");
       // TODO: tests for bug 806266 should arrange to have 2 chats collapsed
       // then open them checking the nub is updated correctly.
-      closeAllChats();
-      port.close();
-      next();
+      // Now we will go and change the embedded iframe in the second chat and
+      // ensure the activity magic still works (ie, check that the unload for
+      // the iframe didn't cause our event handlers to be removed.)
+      ok(!second.hasAttribute("activity"), "second chat should have no activity");
+      let subiframe = iframe2.contentDocument.getElementById("iframe");
+      subiframe.contentWindow.addEventListener("unload", function subunload() {
+        subiframe.contentWindow.removeEventListener("unload", subunload);
+        // ensure all other unload listeners have fired.
+        executeSoon(function() {
+          let evt = iframe2.contentDocument.createEvent("CustomEvent");
+          evt.initCustomEvent("socialChatActivity", true, true, {});
+          iframe2.contentDocument.documentElement.dispatchEvent(evt);
+          ok(second.hasAttribute("activity"), "second chat still has activity after unloading sub-iframe");
+          closeAllChats();
+          port.close();
+          next();
+        })
+      })
+      subiframe.setAttribute("src", "data:text/plain:new location for iframe");
     });
   },
 
+  testOnlyOneCallback: function(next) {
+    let chats = document.getElementById("pinnedchats");
+    let port = Social.provider.getWorkerPort();
+    let numOpened = 0;
+    port.onmessage = function (e) {
+      let topic = e.data.topic;
+      switch (topic) {
+        case "test-init-done":
+          port.postMessage({topic: "test-chatbox-open"});
+          break;
+        case "chatbox-opened":
+          numOpened += 1;
+          port.postMessage({topic: "ping"});
+          break;
+        case "pong":
+          executeSoon(function() {
+            is(numOpened, 1, "only got one open message");
+            chats.removeAll();
+            port.close();
+            next();
+          });
+      }
+    }
+    port.postMessage({topic: "test-init", data: { id: 1 }});
+  },
+
   // XXX - note this must be the last test until we restore the login state
   // between tests...
   testCloseOnLogout: function(next) {
     const chatUrl = "https://example.com/browser/browser/base/content/test/social_chat.html";
     let port = Social.provider.getWorkerPort();
     ok(port, "provider has a port");
     port.postMessage({topic: "test-init"});
     port.onmessage = function (e) {
--- a/browser/base/content/test/browser_social_shareButton.js
+++ b/browser/base/content/test/browser_social_shareButton.js
@@ -49,17 +49,17 @@ function testInitial(finishcb) {
   ok(unsharePopup, "share popup exists");
 
   let okButton = document.getElementById("unsharePopupContinueSharingButton");
   let undoButton = document.getElementById("unsharePopupStopSharingButton");
   let shareStatusLabel = document.getElementById("share-button-status");
 
   // ensure the worker initialization and handshakes are all done and we
   // have a profile and the worker has responsed to the recommend-prompt msg.
-  waitForCondition(function() Social.provider.profile && SocialShareButton.promptImages != null, function() {
+  waitForCondition(function() Social.provider.profile && Social.provider.recommendInfo != null, function() {
     is(shareButton.hasAttribute("shared"), false, "Share button should not have 'shared' attribute before share button is clicked");
     // check dom values
     let profile = Social.provider.profile;
     let portrait = document.getElementById("socialUserPortrait").getAttribute("src");
     is(profile.portrait, portrait, "portrait is set");
     let displayName = document.getElementById("socialUserDisplayName");
     is(displayName.label, profile.displayName, "display name is set");
     ok(!document.getElementById("unsharePopupHeader").hidden, "user profile is visible");
--- a/browser/base/content/test/social_chat.html
+++ b/browser/base/content/test/social_chat.html
@@ -16,10 +16,12 @@
       }, false);
       window.addEventListener("socialTest-CloseSelf", function(e) {
         window.close();
       }, false);
     </script>
   </head>
   <body onload="pingWorker();">
     <p>This is a test social chat window.</p>
+    <!-- an iframe here so this one page generates multiple load events -->
+    <iframe id="iframe" src="data:text/plain:this is an iframe"></iframe>
   </body>
 </html>
--- a/browser/base/content/test/social_worker.js
+++ b/browser/base/content/test/social_worker.js
@@ -8,16 +8,19 @@ onconnect = function(e) {
   let port = e.ports[0];
   port.onmessage = function onMessage(event) {
     let topic = event.data.topic;
     switch (topic) {
       case "test-init":
         testPort = port;
         port.postMessage({topic: "test-init-done"});
         break;
+      case "ping":
+        port.postMessage({topic: "pong"});
+        break;
       case "test-logout":
         apiPort.postMessage({topic: "social.user-profile", data: {}});
         break;
       case "sidebar-message":
         sidebarPort = port;
         if (testPort && event.data.result == "ok")
           testPort.postMessage({topic:"got-sidebar-message"});
         break;
--- a/browser/components/privatebrowsing/test/browser/perwindow/Makefile.in
+++ b/browser/components/privatebrowsing/test/browser/perwindow/Makefile.in
@@ -24,15 +24,17 @@ MOCHITEST_BROWSER_FILES =  \
 		browser_privatebrowsing_geoprompt.js \
 		browser_privatebrowsing_geoprompt_page.html \
 		browser_privatebrowsing_lastpbcontextexited.js \
 		browser_privatebrowsing_localStorage.js \
 		browser_privatebrowsing_localStorage_page1.html \
 		browser_privatebrowsing_localStorage_page2.html \
 		browser_privatebrowsing_opendir.js \
 		browser_privatebrowsing_openlocation.js \
+		browser_privatebrowsing_openLocationLastURL.js \
 		browser_privatebrowsing_popupblocker.js \
 		browser_privatebrowsing_theming.js \
+		browser_privatebrowsing_urlbarfocus.js \
 		browser_privatebrowsing_zoomrestore.js \
 		popup.html \
 		$(NULL)
 
 include $(topsrcdir)/config/rules.mk
copy from browser/components/privatebrowsing/test/browser/global/browser_privatebrowsing_openLocationLastURL.js
copy to browser/components/privatebrowsing/test/browser/perwindow/browser_privatebrowsing_openLocationLastURL.js
--- a/browser/components/privatebrowsing/test/browser/global/browser_privatebrowsing_openLocationLastURL.js
+++ b/browser/components/privatebrowsing/test/browser/perwindow/browser_privatebrowsing_openLocationLastURL.js
@@ -1,63 +1,63 @@
 /* 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/. */
 
 function test() {
+  waitForExplicitFinish();
   const URL_1 = "mozilla.org";
   const URL_2 = "mozilla.com";
 
-  let openLocationLastURL = getLocationModule();
-  let privateBrowsingService =
-    Cc["@mozilla.org/privatebrowsing;1"].
-      getService(Ci.nsIPrivateBrowsingService);
-
-  function clearHistory() {
-    Services.obs.notifyObservers(null, "browser:purge-session-history", "");
-  }
-  function testURL(aTestNumber, aValue) {
-    is(openLocationLastURL.value, aValue,
+  function testURL(aOpenLocation, aTestNumber, aValue) {
+    is(aOpenLocation.value, aValue,
        "Test: " + aTestNumber + ": Validate last url value.");
   }
 
-  // Clean to start testing.
-  is(typeof openLocationLastURL, "object", "Validate type of last url.");
-  openLocationLastURL.reset();
-  testURL(1, "");
+  whenNewWindowLoaded({private: false}, function(normalWindow) {
+    whenNewWindowLoaded({private: true}, function(privateWindow) {
+      let openLocationLastURL = getLocationModule(normalWindow);
+      let openLocationLastURLPB = getLocationModule(privateWindow);
 
-  // Test without private browsing.
-  openLocationLastURL.value = URL_1;
-  testURL(2, URL_1);
-  openLocationLastURL.value = "";
-  testURL(3, "");
-  openLocationLastURL.value = URL_2;
-  testURL(4, URL_2);
-  clearHistory();
-  testURL(5, "");
+      // Clean to start testing.
+      is(typeof openLocationLastURL, "object", "Validate Normal type of last url.");
+      is(typeof openLocationLastURLPB, "object", "Validate PB type of last url.");
+      openLocationLastURL.reset();
+      openLocationLastURLPB.reset();
+      testURL(openLocationLastURL, 1, "");
 
-  // Test changing private browsing.
-  openLocationLastURL.value = URL_2;
-  privateBrowsingService.privateBrowsingEnabled = true;
-  testURL(6, "");
-  privateBrowsingService.privateBrowsingEnabled = false;
-  testURL(7, URL_2);
-  privateBrowsingService.privateBrowsingEnabled = true;
-  openLocationLastURL.value = URL_1;
-  testURL(8, URL_1);
-  privateBrowsingService.privateBrowsingEnabled = false;
-  testURL(9, URL_2);
-  privateBrowsingService.privateBrowsingEnabled = true;
-  openLocationLastURL.value = URL_1;
-  testURL(10, URL_1);
+      // Test without private browsing.
+      openLocationLastURL.value = URL_1;
+      testURL(openLocationLastURL, 2, URL_1);
+      openLocationLastURL.value = "";
+      testURL(openLocationLastURL, 3, "");
+      openLocationLastURL.value = URL_2;
+      testURL(openLocationLastURL, 4, URL_2);
+      clearHistory();
+      testURL(openLocationLastURL, 5, "");
 
-  // Test cleaning history.
-  clearHistory();
-  testURL(11, "");
-  privateBrowsingService.privateBrowsingEnabled = false;
-  testURL(12, "");
+      // Test changing private browsing.
+      openLocationLastURL.value = URL_2;
+      testURL(openLocationLastURLPB, 6, "");
+      testURL(openLocationLastURL, 7, URL_2);
+      openLocationLastURLPB.value = URL_1;
+      testURL(openLocationLastURLPB, 8, URL_1);
+      testURL(openLocationLastURL, 9, URL_2);
+      openLocationLastURLPB.value = URL_1;
+      testURL(openLocationLastURLPB, 10, URL_1);
+
+      // Test cleaning history.
+      clearHistory();
+      testURL(openLocationLastURLPB, 11, "");
+      testURL(openLocationLastURL, 12, "");
+
+      privateWindow.close();
+      normalWindow.close();
+      finish();
+    });
+  });
 }
 
-function getLocationModule() {
+function getLocationModule(aWindow) {
   let openLocationModule = {};
   Cu.import("resource:///modules/openLocationLastURL.jsm", openLocationModule);
-  return new openLocationModule.OpenLocationLastURL(window);
-}
+  return new openLocationModule.OpenLocationLastURL(aWindow);
+}
\ No newline at end of file
copy from browser/components/privatebrowsing/test/browser/global/browser_privatebrowsing_urlbarfocus.js
copy to browser/components/privatebrowsing/test/browser/perwindow/browser_privatebrowsing_urlbarfocus.js
--- a/browser/components/privatebrowsing/test/browser/global/browser_privatebrowsing_urlbarfocus.js
+++ b/browser/components/privatebrowsing/test/browser/perwindow/browser_privatebrowsing_urlbarfocus.js
@@ -1,62 +1,67 @@
 /* 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 test makes sure that the URL bar is focused when entering the private browsing mode.
+// This test makes sure that the URL bar is focused when entering the private window.
 
 function test() {
-  // initialization
-  let pb = Cc["@mozilla.org/privatebrowsing;1"].
-           getService(Ci.nsIPrivateBrowsingService);
+  waitForExplicitFinish();
 
   const TEST_URL = "data:text/plain,test";
-  gBrowser.selectedTab = gBrowser.addTab();
-  let browser = gBrowser.selectedBrowser;
-  browser.addEventListener("load", function() {
-    browser.removeEventListener("load", arguments.callee, true);
-
-    // ensure that the URL bar is not focused initially
-    browser.focus();
-    isnot(document.commandDispatcher.focusedElement, gURLBar.inputField,
-      "URL Bar should not be focused before entering the private browsing mode");
-    // ensure that the URL bar is not empty initially
-    isnot(gURLBar.value, "", "URL Bar should no longer be empty after leaving the private browsing mode");
 
-    // enter private browsing mode
-    pb.privateBrowsingEnabled = true;
-    browser = gBrowser.selectedBrowser;
-    browser.addEventListener("load", function() {
-      browser.removeEventListener("load", arguments.callee, true);
+  function checkUrlbarFocus(aWin, aIsPrivate, aCallback) {
+    let urlbar = aWin.gURLBar;
+    if (aIsPrivate) {
+      is(aWin.document.commandDispatcher.focusedElement, urlbar.inputField,
+         "URL Bar should be focused inside the private window");
+      is(urlbar.value, "",
+         "URL Bar should be empty inside the private window");
+    } else {
+      isnot(aWin.document.commandDispatcher.focusedElement, urlbar.inputField,
+            "URL Bar should not be focused after opening window");
+      isnot(urlbar.value, "",
+            "URL Bar should not be empty after opening window");
+    }
+    aCallback();
+  }
 
-      // setTimeout is needed here because the onload handler of about:privatebrowsing sets the focus
-      setTimeout(function() {
-        // ensure that the URL bar is focused inside the private browsing mode
-        is(document.commandDispatcher.focusedElement, gURLBar.inputField,
-          "URL Bar should be focused inside the private browsing mode");
+  let windowsToClose = [];
+  function testOnWindow(aPrivate, aCallback) {
+    whenNewWindowLoaded({private: aPrivate}, function(win) {
+      windowsToClose.push(win);
+      executeSoon(function() aCallback(win));
+    });
+  }
 
-        // ensure that the URL bar is emptied inside the private browsing mode
-        is(gURLBar.value, "", "URL Bar should be empty inside the private browsing mode");
-
-        // leave private browsing mode
-        pb.privateBrowsingEnabled = false;
-        browser = gBrowser.selectedBrowser;
-        browser.addEventListener("load", function() {
-          browser.removeEventListener("load", arguments.callee, true);
-
-          // ensure that the URL bar is no longer focused after leaving the private browsing mode
-          isnot(document.commandDispatcher.focusedElement, gURLBar.inputField,
-            "URL Bar should no longer be focused after leaving the private browsing mode");
+  registerCleanupFunction(function() {
+    windowsToClose.forEach(function(win) {
+      win.close();
+    });
+  });
 
-          // ensure that the URL bar is no longer empty after leaving the private browsing mode
-          isnot(gURLBar.value, "", "URL Bar should no longer be empty after leaving the private browsing mode");
+  function whenLoadTab(aPrivate, aCallback) {
+    testOnWindow(aPrivate, function(win) {
+      let browser = win.gBrowser.selectedBrowser;
+      browser.addEventListener("load", function() {
+        browser.removeEventListener("load", arguments.callee, true);
+        aCallback(win);
+      }, true);
+      if (!aPrivate) {
+        browser.focus();
+        browser.loadURI(TEST_URL);
+      }
+    });
+  }
 
-          gBrowser.removeCurrentTab();
-          finish();
-        }, true);
-      }, 0);
-    }, true);
-  }, true);
-  content.location = TEST_URL;
-
-  waitForExplicitFinish();
+  whenLoadTab(false, function(win) {
+    checkUrlbarFocus(win, false, function() {
+      whenLoadTab(true, function(win) {
+        checkUrlbarFocus(win, true, function() {
+          whenLoadTab(false, function(win) {
+            checkUrlbarFocus(win, false, finish);
+          });
+        });
+      });
+    });
+  });
 }
--- a/content/base/public/nsContentUtils.h
+++ b/content/base/public/nsContentUtils.h
@@ -14,17 +14,16 @@
 #include <float.h>
 #endif
 
 #if defined(SOLARIS)
 #include <ieeefp.h>
 #endif
 
 #include "nsAString.h"
-#include "nsIStatefulFrame.h"
 #include "nsNodeInfoManager.h"
 #include "nsIXPCScriptable.h"
 #include "nsDataHashtable.h"
 #include "nsIDOMEvent.h"
 #include "nsTArray.h"
 #include "nsReadableUtils.h"
 #include "nsINode.h"
 #include "nsIDOMNode.h"
@@ -477,17 +476,16 @@ public:
   }
 
   // Returns the subject principal. Guaranteed to return non-null. May only
   // be called when nsContentUtils is initialized.
   static nsIPrincipal* GetSubjectPrincipal();
 
   static nsresult GenerateStateKey(nsIContent* aContent,
                                    const nsIDocument* aDocument,
-                                   nsIStatefulFrame::SpecialStateID aID,
                                    nsACString& aKey);
 
   /**
    * Create a new nsIURI from aSpec, using aBaseURI as the base.  The
    * origin charset of the new nsIURI will be the document charset of
    * aDocument.
    */
   static nsresult NewURIWithDocumentCharset(nsIURI** aResult,
--- a/content/base/src/FileIOObject.cpp
+++ b/content/base/src/FileIOObject.cpp
@@ -28,26 +28,26 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_
   NS_INTERFACE_MAP_ENTRY(nsIStreamListener)
   NS_INTERFACE_MAP_ENTRY(nsIRequestObserver)
 NS_INTERFACE_MAP_END_INHERITING(nsDOMEventTargetHelper)
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(FileIOObject)
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(FileIOObject,
                                                   nsDOMEventTargetHelper)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mProgressNotifier)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mError)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mProgressNotifier)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mError)
   // Can't traverse mChannel because it's a multithreaded object.
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(FileIOObject,
                                                 nsDOMEventTargetHelper)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mProgressNotifier)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mError)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mChannel)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mProgressNotifier)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mError)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mChannel)
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
  
 NS_IMPL_EVENT_HANDLER(FileIOObject, abort);
 NS_IMPL_EVENT_HANDLER(FileIOObject, error);
 NS_IMPL_EVENT_HANDLER(FileIOObject, progress);
 
 FileIOObject::FileIOObject()
   : mProgressEventWasDelayed(false),
--- a/content/base/src/WebSocket.cpp
+++ b/content/base/src/WebSocket.cpp
@@ -603,27 +603,27 @@ NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_B
 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END
 
 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(WebSocket,
                                                nsDOMEventTargetHelper)
 NS_IMPL_CYCLE_COLLECTION_TRACE_END
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(WebSocket,
                                                   nsDOMEventTargetHelper)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mPrincipal)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mURI)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mChannel)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPrincipal)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mURI)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChannel)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(WebSocket,
                                                 nsDOMEventTargetHelper)
   tmp->Disconnect();
-  NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mPrincipal)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mURI)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mChannel)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mPrincipal)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mURI)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mChannel)
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(WebSocket)
   NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor)
   NS_INTERFACE_MAP_ENTRY(nsIWebSocketListener)
   NS_INTERFACE_MAP_ENTRY(nsIObserver)
   NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
   NS_INTERFACE_MAP_ENTRY(nsIRequest)
--- a/content/base/src/nsContentIterator.cpp
+++ b/content/base/src/nsContentIterator.cpp
@@ -1143,20 +1143,20 @@ protected:
 NS_IMPL_ADDREF_INHERITED(nsContentSubtreeIterator, nsContentIterator)
 NS_IMPL_RELEASE_INHERITED(nsContentSubtreeIterator, nsContentIterator)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(nsContentSubtreeIterator)
 NS_INTERFACE_MAP_END_INHERITING(nsContentIterator)
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(nsContentSubtreeIterator)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsContentSubtreeIterator, nsContentIterator)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR_AMBIGUOUS(mRange, nsIDOMRange)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRange)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(nsContentSubtreeIterator, nsContentIterator)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mRange)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mRange)
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 
 
 
 /******************************************************
  * repository cruft
  ******************************************************/
--- a/content/base/src/nsContentList.cpp
+++ b/content/base/src/nsContentList.cpp
@@ -39,26 +39,26 @@
 using namespace mozilla::dom;
 
 nsBaseContentList::~nsBaseContentList()
 {
 }
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(nsBaseContentList)
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsBaseContentList)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK_NSTARRAY(mElements)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mElements)
   NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsBaseContentList)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
   if (nsCCUncollectableMarker::sGeneration && tmp->IsBlack() &&
       MOZ_LIKELY(!cb.WantAllTraces())) {
     return NS_SUCCESS_INTERRUPTED_TRAVERSE;
   }
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSTARRAY_OF_NSCOMPTR(mElements)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mElements)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsBaseContentList)
   NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
 NS_IMPL_CYCLE_COLLECTION_TRACE_END
 
 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(nsBaseContentList)
   if (nsCCUncollectableMarker::sGeneration && tmp->IsBlack()) {
     for (uint32_t i = 0; i < tmp->mElements.Length(); ++i) {
@@ -141,21 +141,21 @@ int32_t
 nsBaseContentList::IndexOf(nsIContent* aContent)
 {
   return IndexOf(aContent, true);
 }
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(nsSimpleContentList)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsSimpleContentList,
                                                   nsBaseContentList)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mRoot)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRoot)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(nsSimpleContentList,
                                                 nsBaseContentList)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mRoot)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mRoot)
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(nsSimpleContentList)
 NS_INTERFACE_MAP_END_INHERITING(nsBaseContentList)
 
 
 NS_IMPL_ADDREF_INHERITED(nsSimpleContentList, nsBaseContentList)
 NS_IMPL_RELEASE_INHERITED(nsSimpleContentList, nsBaseContentList)
--- a/content/base/src/nsContentSink.cpp
+++ b/content/base/src/nsContentSink.cpp
@@ -67,27 +67,26 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(
   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDocumentObserver)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(nsContentSink)
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsContentSink)
   if (tmp->mDocument) {
     tmp->mDocument->RemoveObserver(tmp);
   }
-  NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mDocument)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mParser)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mNodeInfoManager)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mScriptLoader)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocument)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mParser)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mNodeInfoManager)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mScriptLoader)
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsContentSink)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mDocument)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mParser)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NATIVE_MEMBER(mNodeInfoManager,
-                                                  nsNodeInfoManager)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mScriptLoader)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocument)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParser)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNodeInfoManager)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mScriptLoader)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 
 nsContentSink::nsContentSink()
 {
   // We have a zeroing operator new
   NS_ASSERTION(!mLayoutStarted, "What?");
   NS_ASSERTION(!mDynamicLowerValue, "What?");
--- a/content/base/src/nsContentUtils.cpp
+++ b/content/base/src/nsContentUtils.cpp
@@ -2167,49 +2167,37 @@ static inline bool IsAutocompleteOff(con
 {
   return aElement->AttrValueIs(kNameSpaceID_None, nsGkAtoms::autocomplete,
                                NS_LITERAL_STRING("off"), eIgnoreCase);
 }
 
 /*static*/ nsresult
 nsContentUtils::GenerateStateKey(nsIContent* aContent,
                                  const nsIDocument* aDocument,
-                                 nsIStatefulFrame::SpecialStateID aID,
                                  nsACString& aKey)
 {
   aKey.Truncate();
 
   uint32_t partID = aDocument ? aDocument->GetPartID() : 0;
 
-  // SpecialStateID case - e.g. scrollbars around the content window
-  // The key in this case is a special state id
-  if (nsIStatefulFrame::eNoID != aID) {
-    KeyAppendInt(partID, aKey);  // first append a partID
-    KeyAppendInt(aID, aKey);
-    return NS_OK;
-  }
-
   // We must have content if we're not using a special state id
   NS_ENSURE_TRUE(aContent, NS_ERROR_FAILURE);
 
   // Don't capture state for anonymous content
   if (aContent->IsInAnonymousSubtree()) {
     return NS_OK;
   }
 
   if (IsAutocompleteOff(aContent)) {
     return NS_OK;
   }
 
   nsCOMPtr<nsIHTMLDocument> htmlDocument(do_QueryInterface(aContent->GetCurrentDoc()));
 
   KeyAppendInt(partID, aKey);  // first append a partID
-  // Make sure we can't possibly collide with an nsIStatefulFrame
-  // special id of some sort
-  KeyAppendInt(nsIStatefulFrame::eNoID, aKey);
   bool generatedUniqueKey = false;
 
   if (htmlDocument) {
     // Flush our content model so it'll be up to date
     // If this becomes unnecessary and the following line is removed,
     // please also remove the corresponding flush operation from
     // nsHtml5TreeBuilderCppSupplement.h. (Look for "See bug 497861." there.)
     aContent->GetCurrentDoc()->FlushPendingNotifications(Flush_Content);
@@ -3954,19 +3942,18 @@ nsContentUtils::TraverseListenerManager(
     return;
   }
 
   EventListenerManagerMapEntry *entry =
     static_cast<EventListenerManagerMapEntry *>
                (PL_DHashTableOperate(&sEventListenerManagersHash, aNode,
                                         PL_DHASH_LOOKUP));
   if (PL_DHASH_ENTRY_IS_BUSY(entry)) {
-    NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NATIVE_PTR(entry->mListenerManager,
-                                                 nsEventListenerManager,
-                                  "[via hash] mListenerManager")
+    CycleCollectionNoteChild(cb, entry->mListenerManager.get(),
+                             "[via hash] mListenerManager");
   }
 }
 
 nsEventListenerManager*
 nsContentUtils::GetListenerManager(nsINode *aNode,
                                    bool aCreateIfNotFound)
 {
   if (!aCreateIfNotFound && !aNode->HasFlag(NODE_HAS_LISTENERMANAGER)) {
--- a/content/base/src/nsDOMFile.cpp
+++ b/content/base/src/nsDOMFile.cpp
@@ -683,17 +683,17 @@ nsDOMMemoryFile::GetInternalStream(nsIIn
 nsDOMMemoryFile::DataOwner::sDataOwners;
 
 /* static */ bool
 nsDOMMemoryFile::DataOwner::sMemoryReporterRegistered;
 
 NS_MEMORY_REPORTER_MALLOC_SIZEOF_FUN(DOMMemoryFileDataOwnerSizeOf,
                                      "memory-file-data");
 
-class nsDOMMemoryFileDataOwnerMemoryReporter
+class nsDOMMemoryFileDataOwnerMemoryReporter MOZ_FINAL
   : public nsIMemoryMultiReporter
 {
   NS_DECL_ISUPPORTS
 
   NS_IMETHOD GetName(nsACString& aName)
   {
     aName.AssignASCII("dom-memory-file-data-owner");
     return NS_OK;
--- a/content/base/src/nsDOMFileReader.cpp
+++ b/content/base/src/nsDOMFileReader.cpp
@@ -56,25 +56,25 @@ using namespace mozilla;
 
 using mozilla::dom::EncodingUtils;
 using mozilla::dom::FileIOObject;
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(nsDOMFileReader)
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsDOMFileReader,
                                                   FileIOObject)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mFile)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mPrincipal)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFile)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPrincipal)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(nsDOMFileReader,
                                                 FileIOObject)
   tmp->mResultArrayBuffer = nullptr;
-  NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mFile)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mPrincipal)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mFile)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mPrincipal)
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 
 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(nsDOMFileReader,
                                                nsDOMEventTargetHelper)
   NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mResultArrayBuffer)
 NS_IMPL_CYCLE_COLLECTION_TRACE_END
 
--- a/content/base/src/nsDOMMutationObserver.cpp
+++ b/content/base/src/nsDOMMutationObserver.cpp
@@ -38,27 +38,27 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(
   NS_INTERFACE_MAP_ENTRY(nsIDOMMutationRecord)
   NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(MutationRecord)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsDOMMutationRecord)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsDOMMutationRecord)
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsDOMMutationRecord)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mTarget)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mPreviousSibling)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mNextSibling)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mTarget)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mPreviousSibling)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mNextSibling)
   tmp->mAddedNodes = nullptr;
   tmp->mRemovedNodes = nullptr;
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsDOMMutationRecord)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mTarget)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mPreviousSibling)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mNextSibling)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTarget)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPreviousSibling)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNextSibling)
   NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mAddedNodes");
   cb.NoteXPCOMChild(static_cast<nsIDOMNodeList*>(tmp->mAddedNodes));
   NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mRemovedNodes");
   cb.NoteXPCOMChild(static_cast<nsIDOMNodeList*>(tmp->mRemovedNodes));
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMETHODIMP
 nsDOMMutationRecord::GetType(nsAString& aType)
@@ -401,34 +401,34 @@ NS_IMPL_CYCLE_COLLECTING_ADDREF(nsDOMMut
 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsDOMMutationObserver)
 
 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsDOMMutationObserver)
   NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
 NS_IMPL_CYCLE_COLLECTION_TRACE_END
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsDOMMutationObserver)
   NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
-  NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mScriptContext)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mOwner)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mScriptContext)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mOwner)
   for (int32_t i = 0; i < tmp->mReceivers.Count(); ++i) {
     tmp->mReceivers[i]->Disconnect(false);
   }
   tmp->mReceivers.Clear();
-  NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMARRAY(mPendingMutations)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mCallback)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mPendingMutations)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mCallback)
   // No need to handle mTransientReceivers
   NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsDOMMutationObserver)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mScriptContext)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mOwner)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMARRAY(mReceivers)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMARRAY(mPendingMutations)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mCallback)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mScriptContext)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOwner)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mReceivers)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPendingMutations)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCallback)
   // No need to handle mTransientReceivers
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 nsMutationReceiver*
 nsDOMMutationObserver::GetReceiverFor(nsINode* aNode, bool aMayCreate)
 {
   if (!aMayCreate && !aNode->MayHaveDOMMutationObserver()) {
     return nullptr;
--- a/content/base/src/nsDocument.cpp
+++ b/content/base/src/nsDocument.cpp
@@ -1634,52 +1634,50 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_
 
   // Traverse the mChildren nsAttrAndChildArray.
   for (int32_t indx = int32_t(tmp->mChildren.ChildCount()); indx > 0; --indx) {
     NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mChildren[i]");
     cb.NoteXPCOMChild(tmp->mChildren.ChildAt(indx - 1));
   }
 
   // Traverse all nsIDocument pointer members.
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mSecurityInfo)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mDisplayDocument)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSecurityInfo)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDisplayDocument)
 
   // Traverse all nsDocument nsCOMPtrs.
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mParser)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mScriptGlobalObject)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NATIVE_MEMBER(mListenerManager,
-                                                  nsEventListenerManager)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mDOMStyleSheets)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mScriptLoader)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParser)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mScriptGlobalObject)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mListenerManager)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDOMStyleSheets)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mScriptLoader)
 
   tmp->mRadioGroups.EnumerateRead(RadioGroupsTraverser, &cb);
 
   // The boxobject for an element will only exist as long as it's in the
   // document, so we'll traverse the table here instead of from the element.
   if (tmp->mBoxObjectTable) {
     tmp->mBoxObjectTable->EnumerateRead(BoxObjectTraverser, &cb);
   }
 
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mChannel)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR_AMBIGUOUS(mStyleAttrStyleSheet, nsIStyleSheet)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mXPathEvaluatorTearoff)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mLayoutHistoryState)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mOnloadBlocker)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mFirstBaseNodeWithHref)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mDOMImplementation)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR_AMBIGUOUS(mImageMaps,
-                                                       nsIDOMNodeList)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mOriginalDocument)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mCachedEncoder)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mStateObjectCached)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChannel)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStyleAttrStyleSheet)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mXPathEvaluatorTearoff)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLayoutHistoryState)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOnloadBlocker)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFirstBaseNodeWithHref)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDOMImplementation)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mImageMaps)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOriginalDocument)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCachedEncoder)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStateObjectCached)
 
   // Traverse all our nsCOMArrays.
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMARRAY(mStyleSheets)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMARRAY(mCatalogSheets)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMARRAY(mPreloadingImages)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStyleSheets)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCatalogSheets)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPreloadingImages)
 
   for (uint32_t i = 0; i < tmp->mFrameRequestCallbacks.Length(); ++i) {
     NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mFrameRequestCallbacks[i]");
     cb.NoteXPCOMChild(tmp->mFrameRequestCallbacks[i]);
   }
 
   // Traverse animation components
   if (tmp->mAnimationController) {
@@ -1714,28 +1712,28 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(ns
   // Unlink the mChildren nsAttrAndChildArray.
   for (int32_t indx = int32_t(tmp->mChildren.ChildCount()) - 1;
        indx >= 0; --indx) {
     tmp->mChildren.ChildAt(indx)->UnbindFromTree();
     tmp->mChildren.RemoveChildAt(indx);
   }
   tmp->mFirstChild = nullptr;
 
-  NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mXPathEvaluatorTearoff)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mXPathEvaluatorTearoff)
   tmp->mCachedRootElement = nullptr; // Avoid a dangling pointer
-  NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mDisplayDocument)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mFirstBaseNodeWithHref)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mDOMImplementation)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mImageMaps)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mOriginalDocument)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mCachedEncoder)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mDisplayDocument)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mFirstBaseNodeWithHref)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mDOMImplementation)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mImageMaps)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mOriginalDocument)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mCachedEncoder)
 
   tmp->mParentDocument = nullptr;
 
-  NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMARRAY(mPreloadingImages)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mPreloadingImages)
 
 
   if (tmp->mBoxObjectTable) {
    tmp->mBoxObjectTable->EnumerateRead(ClearAllBoxObjects, nullptr);
    delete tmp->mBoxObjectTable;
    tmp->mBoxObjectTable = nullptr;
  }
 
--- a/content/base/src/nsDocumentEncoder.cpp
+++ b/content/base/src/nsDocumentEncoder.cpp
@@ -170,29 +170,29 @@ NS_IMPL_CYCLE_COLLECTING_ADDREF(nsDocume
 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsDocumentEncoder)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsDocumentEncoder)
    NS_INTERFACE_MAP_ENTRY(nsIDocumentEncoder)
    NS_INTERFACE_MAP_ENTRY(nsISupports)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsDocumentEncoder)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mDocument)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mSelection)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mRange)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mNode)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mCommonParent)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocument)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mSelection)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mRange)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mNode)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mCommonParent)
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsDocumentEncoder)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mDocument)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mSelection)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR_AMBIGUOUS(mRange, nsIDOMRange)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mNode)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mCommonParent)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocument)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSelection)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRange)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNode)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCommonParent)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 nsDocumentEncoder::nsDocumentEncoder() : mCachedBuffer(nullptr)
 {
   Initialize();
   mMimeType.AssignLiteral("text/plain");
 
 }
--- a/content/base/src/nsEventSource.cpp
+++ b/content/base/src/nsEventSource.cpp
@@ -99,23 +99,23 @@ NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_B
 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END
 
 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(nsEventSource,
                                                nsDOMEventTargetHelper)
 NS_IMPL_CYCLE_COLLECTION_TRACE_END
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsEventSource,
                                                   nsDOMEventTargetHelper)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mSrc)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mNotificationCallbacks)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mLoadGroup)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mChannelEventSink)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mHttpChannel)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mTimer)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mUnicodeDecoder)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSrc)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNotificationCallbacks)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLoadGroup)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChannelEventSink)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mHttpChannel)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTimer)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mUnicodeDecoder)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(nsEventSource, nsDOMEventTargetHelper)
   tmp->Close();
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 DOMCI_DATA(EventSource, nsEventSource)
 
@@ -640,21 +640,21 @@ public:
 
 private:
   nsRefPtr<nsEventSource> mEventSource;
 };
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(AsyncVerifyRedirectCallbackFwr)
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(AsyncVerifyRedirectCallbackFwr)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR_AMBIGUOUS(mEventSource, nsIEventSource)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEventSource)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(AsyncVerifyRedirectCallbackFwr)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mEventSource)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mEventSource)
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(AsyncVerifyRedirectCallbackFwr)
   NS_INTERFACE_MAP_ENTRY(nsISupports)
   NS_INTERFACE_MAP_ENTRY(nsIAsyncVerifyRedirectCallback)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(AsyncVerifyRedirectCallbackFwr)
--- a/content/base/src/nsFrameLoader.cpp
+++ b/content/base/src/nsFrameLoader.cpp
@@ -254,26 +254,26 @@ nsContentView::GetId(nsContentViewId* aI
 // small (but not 1) branching factor.  With large branching factors the number
 // of shells can rapidly become huge and run us out of memory.  To solve that,
 // we'd need to re-institute a fixed version of bug 98158.
 #define MAX_DEPTH_CONTENT_FRAMES 10
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(nsFrameLoader)
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsFrameLoader)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mDocShell)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mMessageManager)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mChildMessageManager)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocShell)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mMessageManager)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mChildMessageManager)
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsFrameLoader)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mDocShell)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocShell)
   NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "nsFrameLoader::mMessageManager");
   cb.NoteXPCOMChild(static_cast<nsIContentFrameMessageManager*>(tmp->mMessageManager.get()));
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mChildMessageManager)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChildMessageManager)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsFrameLoader)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsFrameLoader)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsFrameLoader)
   NS_INTERFACE_MAP_ENTRY(nsIFrameLoader)
   NS_INTERFACE_MAP_ENTRY(nsIContentViewManager)
--- a/content/base/src/nsFrameMessageManager.cpp
+++ b/content/base/src/nsFrameMessageManager.cpp
@@ -59,26 +59,26 @@ IsChromeProcess()
 NS_IMPL_CYCLE_COLLECTION_CLASS(nsFrameMessageManager)
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsFrameMessageManager)
   uint32_t count = tmp->mListeners.Length();
   for (uint32_t i = 0; i < count; i++) {
     NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mListeners[i] mListener");
     cb.NoteXPCOMChild(tmp->mListeners[i].mListener.get());
   }
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMARRAY(mChildManagers)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChildManagers)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsFrameMessageManager)
   tmp->mListeners.Clear();
   for (int32_t i = tmp->mChildManagers.Count(); i > 0; --i) {
     static_cast<nsFrameMessageManager*>(tmp->mChildManagers[i - 1])->
       Disconnect(false);
   }
-  NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMARRAY(mChildManagers)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mChildManagers)
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsFrameMessageManager)
   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIContentFrameMessageManager)
 
   /* nsFrameMessageManager implements nsIMessageSender and nsIMessageBroadcaster,
    * both of which descend from nsIMessageListenerManager. QI'ing to
@@ -1006,17 +1006,17 @@ nsFrameScriptExecutor::InitTabChildGloba
   return true;
 }
 
 // static
 void
 nsFrameScriptExecutor::Traverse(nsFrameScriptExecutor *tmp,
                                 nsCycleCollectionTraversalCallback &cb)
 {
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mGlobal)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGlobal)
   nsIXPConnect* xpc = nsContentUtils::XPConnect();
   if (xpc) {
     NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mCx");
     xpc->NoteJSContext(tmp->mCx, cb);
   }
 }
 
 NS_IMPL_ISUPPORTS1(nsScriptCacheCleaner, nsIObserver)
--- a/content/base/src/nsINode.cpp
+++ b/content/base/src/nsINode.cpp
@@ -1167,17 +1167,17 @@ nsINode::Traverse(nsINode *tmp, nsCycleC
         if (parent && !parent->UnoptimizableCCNode() && parent->IsBlack()) {
           NS_ABORT_IF_FALSE(parent->IndexOf(tmp) >= 0, "Parent doesn't own us?");
           return false;
         }
       }
     }
   }
 
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mNodeInfo)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNodeInfo)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_RAWPTR(GetParent())
 
   nsSlots *slots = tmp->GetExistingSlots();
   if (slots) {
     slots->Traverse(cb);
   }
 
   if (tmp->HasProperties()) {
--- a/content/base/src/nsInProcessTabChildGlobal.cpp
+++ b/content/base/src/nsInProcessTabChildGlobal.cpp
@@ -146,23 +146,23 @@ nsInProcessTabChildGlobal::Init()
 
   return NS_OK;
 }
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(nsInProcessTabChildGlobal)
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(nsInProcessTabChildGlobal,
                                                 nsDOMEventTargetHelper)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mMessageManager)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mGlobal)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mMessageManager)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mGlobal)
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsInProcessTabChildGlobal,
                                                   nsDOMEventTargetHelper)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mMessageManager)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMessageManager)
   nsFrameScriptExecutor::Traverse(tmp, cb);
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(nsInProcessTabChildGlobal)
   NS_INTERFACE_MAP_ENTRY(nsIMessageListenerManager)
   NS_INTERFACE_MAP_ENTRY(nsIMessageSender)
   NS_INTERFACE_MAP_ENTRY(nsISyncMessageSender)
   NS_INTERFACE_MAP_ENTRY(nsIContentFrameMessageManager)
--- a/content/base/src/nsNodeInfo.cpp
+++ b/content/base/src/nsNodeInfo.cpp
@@ -178,18 +178,17 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_
     }
 
     cb.DescribeRefCountedNode(tmp->mRefCnt.get(), name);
   }
   else {
     NS_IMPL_CYCLE_COLLECTION_DESCRIBE(nsNodeInfo, tmp->mRefCnt.get())
   }
 
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NATIVE_MEMBER(mOwnerManager,
-                                                  nsNodeInfoManager)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_RAWPTR(mOwnerManager)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsNodeInfo)
 NS_IMPL_CYCLE_COLLECTING_RELEASE_WITH_DESTROY(nsNodeInfo, LastRelease())
 NS_INTERFACE_TABLE_HEAD(nsNodeInfo)
   NS_INTERFACE_TABLE1(nsNodeInfo, nsINodeInfo)
   NS_INTERFACE_TABLE_TO_MAP_SEGUE_CYCLE_COLLECTION(nsNodeInfo)
 NS_INTERFACE_MAP_END
--- a/content/base/src/nsNodeIterator.cpp
+++ b/content/base/src/nsNodeIterator.cpp
@@ -155,22 +155,22 @@ nsNodeIterator::~nsNodeIterator()
 /*
  * nsISupports and cycle collection stuff
  */
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(nsNodeIterator)
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsNodeIterator)
     if (!tmp->mDetached && tmp->mRoot)
         tmp->mRoot->RemoveMutationObserver(tmp);
-  NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mRoot)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mFilter)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mRoot)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mFilter)
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsNodeIterator)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mRoot)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mFilter)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRoot)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFilter)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 DOMCI_DATA(NodeIterator, nsNodeIterator)
 
 // QueryInterface implementation for nsNodeIterator
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsNodeIterator)
     NS_INTERFACE_MAP_ENTRY(nsIDOMNodeIterator)
     NS_INTERFACE_MAP_ENTRY(nsIMutationObserver)
--- a/content/base/src/nsRange.cpp
+++ b/content/base/src/nsRange.cpp
@@ -268,19 +268,19 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(
   NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(Range)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsRange)
   tmp->Reset();
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsRange)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mStartParent)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mEndParent)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mRoot)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStartParent)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEndParent)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRoot)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 static void
 RangeHashTableDtor(void* aObject, nsIAtom* aPropertyName, void* aPropertyValue,
                    void* aData)
 {
   nsRange::RangeHashTable* ranges =
     static_cast<nsRange::RangeHashTable*>(aPropertyValue);
--- a/content/base/src/nsStyleLinkElement.cpp
+++ b/content/base/src/nsStyleLinkElement.cpp
@@ -43,17 +43,17 @@ nsStyleLinkElement::Unlink()
 {
   mStyleSheet = nullptr;
 }
 
 void
 nsStyleLinkElement::Traverse(nsCycleCollectionTraversalCallback &cb)
 {
   nsStyleLinkElement* tmp = this;
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mStyleSheet);
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStyleSheet);
 }
 
 NS_IMETHODIMP 
 nsStyleLinkElement::SetStyleSheet(nsIStyleSheet* aStyleSheet)
 {
   nsRefPtr<nsCSSStyleSheet> cssSheet = do_QueryObject(mStyleSheet);
   if (cssSheet) {
     cssSheet->SetOwningNode(nullptr);
--- a/content/base/src/nsXMLHttpRequest.cpp
+++ b/content/base/src/nsXMLHttpRequest.cpp
@@ -591,47 +591,46 @@ NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_
 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END
 
 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(nsXMLHttpRequest)
   return tmp->IsBlack();
 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsXMLHttpRequest,
                                                   nsXHREventTarget)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mContext)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mChannel)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mReadRequest)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mResponseXML)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mCORSPreflightChannel)
-
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mXMLParserStreamListener)
-
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mChannelEventSink)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mProgressEventSink)
-
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR_AMBIGUOUS(mUpload,
-                                                       nsIXMLHttpRequestUpload)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mContext)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChannel)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mReadRequest)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mResponseXML)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCORSPreflightChannel)
+
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mXMLParserStreamListener)
+
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChannelEventSink)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mProgressEventSink)
+
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mUpload)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(nsXMLHttpRequest,
                                                 nsXHREventTarget)
   tmp->mResultArrayBuffer = nullptr;
   tmp->mResultJSON = JSVAL_VOID;
-  NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mContext)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mChannel)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mReadRequest)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mResponseXML)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mCORSPreflightChannel)
-
-  NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mXMLParserStreamListener)
-
-  NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mChannelEventSink)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mProgressEventSink)
-
-  NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mUpload)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mContext)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mChannel)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mReadRequest)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mResponseXML)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mCORSPreflightChannel)
+
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mXMLParserStreamListener)
+
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mChannelEventSink)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mProgressEventSink)
+
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mUpload)
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(nsXMLHttpRequest,
                                                nsXHREventTarget)
   NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mResultArrayBuffer)
   NS_IMPL_CYCLE_COLLECTION_TRACE_JSVAL_MEMBER_CALLBACK(mResultJSON)
 NS_IMPL_CYCLE_COLLECTION_TRACE_END
 
@@ -3560,21 +3559,21 @@ public:
 
 private:
   nsRefPtr<nsXMLHttpRequest> mXHR;
 };
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(AsyncVerifyRedirectCallbackForwarder)
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(AsyncVerifyRedirectCallbackForwarder)
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR_AMBIGUOUS(mXHR, nsIXMLHttpRequest)
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mXHR)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(AsyncVerifyRedirectCallbackForwarder)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mXHR)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mXHR)
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(AsyncVerifyRedirectCallbackForwarder)
   NS_INTERFACE_MAP_ENTRY(nsISupports)
   NS_INTERFACE_MAP_ENTRY(nsIAsyncVerifyRedirectCallback)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(AsyncVerifyRedirectCallbackForwarder)
@@ -4080,23 +4079,23 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(
   NS_INTERFACE_MAP_ENTRY(nsIDOMLSProgressEvent)
   NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(XMLHttpProgressEvent)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsXMLHttpProgressEvent)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsXMLHttpProgressEvent)
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsXMLHttpProgressEvent)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mInner);
-  NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mWindow);
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mInner);
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mWindow);
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsXMLHttpProgressEvent)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mInner)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mWindow);
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mInner)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWindow);
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMETHODIMP nsXMLHttpProgressEvent::GetInput(nsIDOMLSInput * *aInput)
 {
   *aInput = nullptr;
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
@@ -4147,21 +4146,21 @@ NS_IMPL_CYCLE_COLLECTING_RELEASE(nsXMLHt
 // Can't NS_IMPL_CYCLE_COLLECTION_1 because mXHR has ambiguous
 // inheritance from nsISupports.
 NS_IMPL_CYCLE_COLLECTION_CLASS(nsXMLHttpRequestXPCOMifier)
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsXMLHttpRequestXPCOMifier)
 if (tmp->mXHR) {
   tmp->mXHR->mXPCOMifier = nullptr;
 }
-NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mXHR)
+NS_IMPL_CYCLE_COLLECTION_UNLINK(mXHR)
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsXMLHttpRequestXPCOMifier)
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR_AMBIGUOUS(mXHR, nsIXMLHttpRequest)
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mXHR)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMETHODIMP
 nsXMLHttpRequestXPCOMifier::GetInterface(const nsIID & aIID, void **aResult)
 {
   // Return ourselves for the things we implement (except
   // nsIInterfaceRequestor) and the XHR for the rest.
   if (!aIID.Equals(NS_GET_IID(nsIInterfaceRequestor))) {
--- a/content/canvas/src/CanvasRenderingContext2D.cpp
+++ b/content/canvas/src/CanvasRenderingContext2D.cpp
@@ -1,3937 +1,3926 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
- * 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 "base/basictypes.h"
-#include "CanvasRenderingContext2D.h"
-
-#include "nsIDOMXULElement.h"
-
-#include "prenv.h"
-
-#include "nsIServiceManager.h"
-#include "nsMathUtils.h"
-
-#include "nsContentUtils.h"
-
-#include "nsIDocument.h"
-#include "nsHTMLCanvasElement.h"
-#include "nsSVGEffects.h"
-#include "nsPresContext.h"
-#include "nsIPresShell.h"
-#include "nsIVariant.h"
-
-#include "nsIInterfaceRequestorUtils.h"
-#include "nsIFrame.h"
-#include "nsError.h"
-#include "nsIScriptError.h"
-
-#include "nsCSSParser.h"
-#include "mozilla/css/StyleRule.h"
-#include "mozilla/css/Declaration.h"
-#include "nsComputedDOMStyle.h"
-#include "nsStyleSet.h"
-
-#include "nsPrintfCString.h"
-
-#include "nsReadableUtils.h"
-
-#include "nsColor.h"
-#include "nsGfxCIID.h"
-#include "nsIScriptSecurityManager.h"
-#include "nsIDocShell.h"
-#include "nsIDOMWindow.h"
-#include "nsPIDOMWindow.h"
-#include "nsIDocShellTreeItem.h"
-#include "nsIDocShellTreeNode.h"
-#include "nsIXPConnect.h"
-#include "nsDisplayList.h"
-
-#include "nsTArray.h"
-
-#include "imgIEncoder.h"
-
-#include "gfxContext.h"
-#include "gfxASurface.h"
-#include "gfxImageSurface.h"
-#include "gfxPlatform.h"
-#include "gfxFont.h"
-#include "gfxBlur.h"
-#include "gfxUtils.h"
-#include "gfxFontMissingGlyphs.h"
-
-#include "nsFrameManager.h"
-#include "nsFrameLoader.h"
-#include "nsBidi.h"
-#include "nsBidiPresUtils.h"
-#include "Layers.h"
-#include "CanvasUtils.h"
-#include "nsIMemoryReporter.h"
-#include "nsStyleUtil.h"
-#include "CanvasImageCache.h"
-
-#include <algorithm>
-
-#include "jsapi.h"
-#include "jsfriendapi.h"
-
-#include "mozilla/Assertions.h"
-#include "mozilla/CheckedInt.h"
-#include "mozilla/dom/ContentParent.h"
-#include "mozilla/dom/ImageData.h"
-#include "mozilla/dom/PBrowserParent.h"
-#include "mozilla/dom/TypedArray.h"
-#include "mozilla/gfx/2D.h"
-#include "mozilla/gfx/PathHelpers.h"
-#include "mozilla/ipc/DocumentRendererParent.h"
-#include "mozilla/ipc/PDocumentRendererParent.h"
-#include "mozilla/Preferences.h"
-#include "mozilla/Telemetry.h"
-#include "mozilla/unused.h"
-#include "nsCCUncollectableMarker.h"
-#include "nsWrapperCacheInlines.h"
-#include "nsJSUtils.h"
-#include "XPCQuickStubs.h"
-#include "mozilla/dom/BindingUtils.h"
-#include "nsHTMLImageElement.h"
-#include "nsHTMLVideoElement.h"
-#include "mozilla/dom/CanvasRenderingContext2DBinding.h"
-
-#ifdef XP_WIN
-#include "gfxWindowsPlatform.h"
-#endif
-
-// windows.h (included by chromium code) defines this, in its infinite wisdom
-#undef DrawText
-
-using namespace mozilla;
-using namespace mozilla::CanvasUtils;
-using namespace mozilla::css;
-using namespace mozilla::gfx;
-using namespace mozilla::ipc;
-using namespace mozilla::layers;
-
-namespace mgfx = mozilla::gfx;
-
-#define NS_TEXTMETRICSAZURE_PRIVATE_IID \
-  {0x9793f9e7, 0x9dc1, 0x4e9c, {0x81, 0xc8, 0xfc, 0xa7, 0x14, 0xf4, 0x30, 0x79}}
-
-namespace mozilla {
-namespace dom {
-
-static float kDefaultFontSize = 10.0;
-static NS_NAMED_LITERAL_STRING(kDefaultFontName, "sans-serif");
-static NS_NAMED_LITERAL_STRING(kDefaultFontStyle, "10px sans-serif");
-
-// Cap sigma to avoid overly large temp surfaces.
-const Float SIGMA_MAX = 100;
-
-/* Memory reporter stuff */
-static nsIMemoryReporter *gCanvasAzureMemoryReporter = nullptr;
-static int64_t gCanvasAzureMemoryUsed = 0;
-
-static int64_t GetCanvasAzureMemoryUsed() {
-  return gCanvasAzureMemoryUsed;
-}
-
-// This is KIND_OTHER because it's not always clear where in memory the pixels
-// of a canvas are stored.  Furthermore, this memory will be tracked by the
-// underlying surface implementations.  See bug 655638 for details.
-NS_MEMORY_REPORTER_IMPLEMENT(CanvasAzureMemory,
-  "canvas-2d-pixel-bytes",
-  KIND_OTHER,
-  UNITS_BYTES,
-  GetCanvasAzureMemoryUsed,
-  "Memory used by 2D canvases. Each canvas requires (width * height * 4) "
-  "bytes.")
-
-class CanvasRadialGradient : public CanvasGradient
-{
-public:
-  CanvasRadialGradient(const Point &aBeginOrigin, Float aBeginRadius,
-                       const Point &aEndOrigin, Float aEndRadius)
-    : CanvasGradient(RADIAL)
-    , mCenter1(aBeginOrigin)
-    , mCenter2(aEndOrigin)
-    , mRadius1(aBeginRadius)
-    , mRadius2(aEndRadius)
-  {
-  }
-
-  Point mCenter1;
-  Point mCenter2;
-  Float mRadius1;
-  Float mRadius2;
-};
-
-class CanvasLinearGradient : public CanvasGradient
-{
-public:
-  CanvasLinearGradient(const Point &aBegin, const Point &aEnd)
-    : CanvasGradient(LINEAR)
-    , mBegin(aBegin)
-    , mEnd(aEnd)
-  {
-  }
-
-protected:
-  friend class CanvasGeneralPattern;
-
-  // Beginning of linear gradient.
-  Point mBegin;
-  // End of linear gradient.
-  Point mEnd;
-};
-
-// This class is named 'GeneralCanvasPattern' instead of just
-// 'GeneralPattern' to keep Windows PGO builds from confusing the
-// GeneralPattern class in gfxContext.cpp with this one.
-
-class CanvasGeneralPattern
-{
-public:
-  typedef CanvasRenderingContext2D::Style Style;
-  typedef CanvasRenderingContext2D::ContextState ContextState;
-
-  CanvasGeneralPattern() : mPattern(nullptr) {}
-  ~CanvasGeneralPattern()
-  {
-    if (mPattern) {
-      mPattern->~Pattern();
-    }
-  }
-
-  Pattern& ForStyle(CanvasRenderingContext2D *aCtx,
-                    Style aStyle,
-                    DrawTarget *aRT)
-  {
-    // This should only be called once or the mPattern destructor will
-    // not be executed.
-    NS_ASSERTION(!mPattern, "ForStyle() should only be called once on CanvasGeneralPattern!");
-
-    const ContextState &state = aCtx->CurrentState();
-
-    if (state.StyleIsColor(aStyle)) {
-      mPattern = new (mColorPattern.addr()) ColorPattern(Color::FromABGR(state.colorStyles[aStyle]));
-    } else if (state.gradientStyles[aStyle] &&
-               state.gradientStyles[aStyle]->GetType() == CanvasGradient::LINEAR) {
-      CanvasLinearGradient *gradient =
-        static_cast<CanvasLinearGradient*>(state.gradientStyles[aStyle].get());
-
-      mPattern = new (mLinearGradientPattern.addr())
-        LinearGradientPattern(gradient->mBegin, gradient->mEnd,
-                              gradient->GetGradientStopsForTarget(aRT));
-    } else if (state.gradientStyles[aStyle] &&
-               state.gradientStyles[aStyle]->GetType() == CanvasGradient::RADIAL) {
-      CanvasRadialGradient *gradient =
-        static_cast<CanvasRadialGradient*>(state.gradientStyles[aStyle].get());
-
-      mPattern = new (mRadialGradientPattern.addr())
-        RadialGradientPattern(gradient->mCenter1, gradient->mCenter2, gradient->mRadius1,
-                              gradient->mRadius2, gradient->GetGradientStopsForTarget(aRT));
-    } else if (state.patternStyles[aStyle]) {
-      if (aCtx->mCanvasElement) {
-        CanvasUtils::DoDrawImageSecurityCheck(aCtx->mCanvasElement,
-                                              state.patternStyles[aStyle]->mPrincipal,
-                                              state.patternStyles[aStyle]->mForceWriteOnly,
-                                              state.patternStyles[aStyle]->mCORSUsed);
-      }
-
-      ExtendMode mode;
-      if (state.patternStyles[aStyle]->mRepeat == CanvasPattern::NOREPEAT) {
-        mode = EXTEND_CLAMP;
-      } else {
-        mode = EXTEND_REPEAT;
-      }
-      mPattern = new (mSurfacePattern.addr())
-        SurfacePattern(state.patternStyles[aStyle]->mSurface, mode);
-    }
-
-    return *mPattern;
-  }
-
-  union {
-    AlignedStorage2<ColorPattern> mColorPattern;
-    AlignedStorage2<LinearGradientPattern> mLinearGradientPattern;
-    AlignedStorage2<RadialGradientPattern> mRadialGradientPattern;
-    AlignedStorage2<SurfacePattern> mSurfacePattern;
-  };
-  Pattern *mPattern;
-};
-
-/* This is an RAII based class that can be used as a drawtarget for
- * operations that need a shadow drawn. It will automatically provide a
- * temporary target when needed, and if so blend it back with a shadow.
- *
- * aBounds specifies the bounds of the drawing operation that will be
- * drawn to the target, it is given in device space! This function will
- * change aBounds to incorporate shadow bounds. If this is NULL the drawing
- * operation will be assumed to cover an infinite rect.
- */
-class AdjustedTarget
-{
-public:
-  typedef CanvasRenderingContext2D::ContextState ContextState;
-
-  AdjustedTarget(CanvasRenderingContext2D *ctx,
-                 mgfx::Rect *aBounds = nullptr)
-    : mCtx(nullptr)
-  {
-    if (!ctx->NeedToDrawShadow()) {
-      mTarget = ctx->mTarget;
-      return;
-    }
-    mCtx = ctx;
-
-    const ContextState &state = mCtx->CurrentState();
-
-    mSigma = state.shadowBlur / 2.0f;
-
-    if (mSigma > SIGMA_MAX) {
-      mSigma = SIGMA_MAX;
-    }
-
-    Matrix transform = mCtx->mTarget->GetTransform();
-
-    mTempRect = mgfx::Rect(0, 0, ctx->mWidth, ctx->mHeight);
-
-    static const gfxFloat GAUSSIAN_SCALE_FACTOR = (3 * sqrt(2 * M_PI) / 4) * 1.5;
-    int32_t blurRadius = (int32_t) floor(mSigma * GAUSSIAN_SCALE_FACTOR + 0.5);
-
-    // We need to enlarge and possibly offset our temporary surface
-    // so that things outside of the canvas may cast shadows.
-    mTempRect.Inflate(Margin(blurRadius + NS_MAX<Float>(state.shadowOffset.x, 0),
-                             blurRadius + NS_MAX<Float>(state.shadowOffset.y, 0),
-                             blurRadius + NS_MAX<Float>(-state.shadowOffset.x, 0),
-                             blurRadius + NS_MAX<Float>(-state.shadowOffset.y, 0)));
-
-    if (aBounds) {
-      // We actually include the bounds of the shadow blur, this makes it
-      // easier to execute the actual blur on hardware, and shouldn't affect
-      // the amount of pixels that need to be touched.
-      aBounds->Inflate(Margin(blurRadius, blurRadius,
-                              blurRadius, blurRadius));
-      mTempRect = mTempRect.Intersect(*aBounds);
-    }
-
-    mTempRect.ScaleRoundOut(1.0f);
-
-    transform._31 -= mTempRect.x;
-    transform._32 -= mTempRect.y;
-
-    mTarget =
-      mCtx->mTarget->CreateShadowDrawTarget(IntSize(int32_t(mTempRect.width), int32_t(mTempRect.height)),
-                                            FORMAT_B8G8R8A8, mSigma);
-
-    if (!mTarget) {
-      // XXX - Deal with the situation where our temp size is too big to
-      // fit in a texture.
-      mTarget = ctx->mTarget;
-      mCtx = nullptr;
-    } else {
-      mTarget->SetTransform(transform);
-    }
-  }
-
-  ~AdjustedTarget()
-  {
-    if (!mCtx) {
-      return;
-    }
-
-    RefPtr<SourceSurface> snapshot = mTarget->Snapshot();
-
-    mCtx->mTarget->DrawSurfaceWithShadow(snapshot, mTempRect.TopLeft(),
-                                         Color::FromABGR(mCtx->CurrentState().shadowColor),
-                                         mCtx->CurrentState().shadowOffset, mSigma,
-                                         mCtx->CurrentState().op);
-  }
-
-  DrawTarget* operator->()
-  {
-    return mTarget;
-  }
-
-private:
-  RefPtr<DrawTarget> mTarget;
-  CanvasRenderingContext2D *mCtx;
-  Float mSigma;
-  mgfx::Rect mTempRect;
-};
-
-NS_IMETHODIMP
-CanvasGradient::AddColorStop(float offset, const nsAString& colorstr)
-{
-  if (!FloatValidate(offset) || offset < 0.0 || offset > 1.0) {
-    return NS_ERROR_DOM_INDEX_SIZE_ERR;
-  }
-
-  nsCSSValue value;
-  nsCSSParser parser;
-  if (!parser.ParseColorString(colorstr, nullptr, 0, value)) {
-    return NS_ERROR_DOM_SYNTAX_ERR;
-  }
-
-  nscolor color;
-  if (!nsRuleNode::ComputeColor(value, nullptr, nullptr, color)) {
-    return NS_ERROR_DOM_SYNTAX_ERR;
-  }
-
-  mStops = nullptr;
-
-  GradientStop newStop;
-
-  newStop.offset = offset;
-  newStop.color = Color::FromABGR(color);
-
-  mRawStops.AppendElement(newStop);
-
-  return NS_OK;
-}
-
-NS_DEFINE_STATIC_IID_ACCESSOR(CanvasGradient, NS_CANVASGRADIENTAZURE_PRIVATE_IID)
-
-NS_IMPL_ADDREF(CanvasGradient)
-NS_IMPL_RELEASE(CanvasGradient)
-
-NS_INTERFACE_MAP_BEGIN(CanvasGradient)
-  NS_INTERFACE_MAP_ENTRY(mozilla::dom::CanvasGradient)
-  NS_INTERFACE_MAP_ENTRY(nsIDOMCanvasGradient)
-  NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(CanvasGradient)
-  NS_INTERFACE_MAP_ENTRY(nsISupports)
-NS_INTERFACE_MAP_END
-
-NS_DEFINE_STATIC_IID_ACCESSOR(CanvasPattern, NS_CANVASPATTERNAZURE_PRIVATE_IID)
-
-NS_IMPL_ADDREF(CanvasPattern)
-NS_IMPL_RELEASE(CanvasPattern)
-
-NS_INTERFACE_MAP_BEGIN(CanvasPattern)
-  NS_INTERFACE_MAP_ENTRY(mozilla::dom::CanvasPattern)
-  NS_INTERFACE_MAP_ENTRY(nsIDOMCanvasPattern)
-  NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(CanvasPattern)
-  NS_INTERFACE_MAP_ENTRY(nsISupports)
-NS_INTERFACE_MAP_END
-
-/**
- ** TextMetrics
- **/
-class TextMetrics : public nsIDOMTextMetrics
-{
-public:
-  TextMetrics(float w) : width(w) { }
-
-  virtual ~TextMetrics() { }
-
-  NS_DECLARE_STATIC_IID_ACCESSOR(NS_TEXTMETRICSAZURE_PRIVATE_IID)
-
-  NS_IMETHOD GetWidth(float* w)
-  {
-    *w = width;
-    return NS_OK;
-  }
-
-  NS_DECL_ISUPPORTS
-
-private:
-  float width;
-};
-
-NS_DEFINE_STATIC_IID_ACCESSOR(TextMetrics, NS_TEXTMETRICSAZURE_PRIVATE_IID)
-
-NS_IMPL_ADDREF(TextMetrics)
-NS_IMPL_RELEASE(TextMetrics)
-
-NS_INTERFACE_MAP_BEGIN(TextMetrics)
-  NS_INTERFACE_MAP_ENTRY(mozilla::dom::TextMetrics)
-  NS_INTERFACE_MAP_ENTRY(nsIDOMTextMetrics)
-  NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(TextMetrics)
-  NS_INTERFACE_MAP_ENTRY(nsISupports)
-NS_INTERFACE_MAP_END
-
-class CanvasRenderingContext2DUserData : public LayerUserData {
-public:
-    CanvasRenderingContext2DUserData(CanvasRenderingContext2D *aContext)
-    : mContext(aContext)
-  {
-    aContext->mUserDatas.AppendElement(this);
-  }
-  ~CanvasRenderingContext2DUserData()
-  {
-    if (mContext) {
-      mContext->mUserDatas.RemoveElement(this);
-    }
-  }
-  static void DidTransactionCallback(void* aData)
-  {
-    CanvasRenderingContext2DUserData* self =
-      static_cast<CanvasRenderingContext2DUserData*>(aData);
-    if (self->mContext) {
-      self->mContext->MarkContextClean();
-    }
-  }
-  bool IsForContext(CanvasRenderingContext2D *aContext)
-  {
-    return mContext == aContext;
-  }
-  void Forget()
-  {
-    mContext = nullptr;
-  }
-
-private:
-  CanvasRenderingContext2D *mContext;
-};
-
-NS_IMPL_CYCLE_COLLECTING_ADDREF(CanvasRenderingContext2D)
-NS_IMPL_CYCLE_COLLECTING_RELEASE(CanvasRenderingContext2D)
-
-NS_IMPL_CYCLE_COLLECTION_CLASS(CanvasRenderingContext2D)
-NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(CanvasRenderingContext2D)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mCanvasElement)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
-NS_IMPL_CYCLE_COLLECTION_UNLINK_END
-NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(CanvasRenderingContext2D)
-  NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
-NS_IMPL_CYCLE_COLLECTION_TRACE_END
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(CanvasRenderingContext2D)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR_AMBIGUOUS(mCanvasElement, nsINode)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
-
-NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(CanvasRenderingContext2D)
- if (nsCCUncollectableMarker::sGeneration && tmp->IsBlack()) {
-   dom::Element* canvasElement = tmp->mCanvasElement;
-    if (canvasElement) {
-      if (canvasElement->IsPurple()) {
-        canvasElement->RemovePurple();
-      }
-      dom::Element::MarkNodeChildren(canvasElement);
-    }
-    return true;
-  }
-NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END
-
-NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(CanvasRenderingContext2D)
-  return nsCCUncollectableMarker::sGeneration && tmp->IsBlack();
-NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END
-
-NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(CanvasRenderingContext2D)
-  return nsCCUncollectableMarker::sGeneration && tmp->IsBlack();
-NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END
-
-NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(CanvasRenderingContext2D)
-  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
-  NS_INTERFACE_MAP_ENTRY(nsICanvasRenderingContextInternal)
-  NS_INTERFACE_MAP_ENTRY(nsISupports)
-NS_INTERFACE_MAP_END
-
-/**
- ** CanvasRenderingContext2D impl
- **/
-
-
-// Initialize our static variables.
-uint32_t CanvasRenderingContext2D::sNumLivingContexts = 0;
-uint8_t (*CanvasRenderingContext2D::sUnpremultiplyTable)[256] = nullptr;
-uint8_t (*CanvasRenderingContext2D::sPremultiplyTable)[256] = nullptr;
-DrawTarget* CanvasRenderingContext2D::sErrorTarget = nullptr;
-
-
-
-CanvasRenderingContext2D::CanvasRenderingContext2D()
-  : mZero(false), mOpaque(false), mResetLayer(true)
-  , mIPC(false)
-  , mIsEntireFrameInvalid(false)
-  , mPredictManyRedrawCalls(false), mPathTransformWillUpdate(false)
-  , mInvalidateCount(0)
-{
-  sNumLivingContexts++;
-  SetIsDOMBinding();
-}
-
-CanvasRenderingContext2D::~CanvasRenderingContext2D()
-{
-  Reset();
-  // Drop references from all CanvasRenderingContext2DUserData to this context
-  for (uint32_t i = 0; i < mUserDatas.Length(); ++i) {
-    mUserDatas[i]->Forget();
-  }
-  sNumLivingContexts--;
-  if (!sNumLivingContexts) {
-    delete[] sUnpremultiplyTable;
-    delete[] sPremultiplyTable;
-    sUnpremultiplyTable = nullptr;
-    sPremultiplyTable = nullptr;
-    NS_IF_RELEASE(sErrorTarget);
-  }
-}
-
-JSObject*
-CanvasRenderingContext2D::WrapObject(JSContext *cx, JSObject *scope,
-                                     bool *triedToWrap)
-{
-  return CanvasRenderingContext2DBinding::Wrap(cx, scope, this, triedToWrap);
-}
-
-bool
-CanvasRenderingContext2D::ParseColor(const nsAString& aString,
-                                     nscolor* aColor)
-{
-  nsIDocument* document = mCanvasElement
-                          ? mCanvasElement->OwnerDoc()
-                          : nullptr;
-
-  // Pass the CSS Loader object to the parser, to allow parser error
-  // reports to include the outer window ID.
-  nsCSSParser parser(document ? document->CSSLoader() : nullptr);
-  nsCSSValue value;
-  if (!parser.ParseColorString(aString, nullptr, 0, value)) {
-    return false;
-  }
-
-  if (value.GetUnit() == nsCSSUnit::eCSSUnit_Color) {
-    // if we already have a color we can just use it directly
-    *aColor = value.GetColorValue();
-  } else {
-    // otherwise resolve it
-    nsIPresShell* presShell = GetPresShell();
-    nsRefPtr<nsStyleContext> parentContext;
-    if (mCanvasElement && mCanvasElement->IsInDoc()) {
-      // Inherit from the canvas element.
-      parentContext = nsComputedDOMStyle::GetStyleContextForElement(
-        mCanvasElement, nullptr, presShell);
-    }
-
-    unused << nsRuleNode::ComputeColor(
-      value, presShell ? presShell->GetPresContext() : nullptr, parentContext,
-      *aColor);
-  }
-  return true;
-}
-
-nsresult
-CanvasRenderingContext2D::Reset()
-{
-  if (mCanvasElement) {
-    mCanvasElement->InvalidateCanvas();
-  }
-
-  // only do this for non-docshell created contexts,
-  // since those are the ones that we created a surface for
-  if (mTarget && IsTargetValid() && !mDocShell) {
-    gCanvasAzureMemoryUsed -= mWidth * mHeight * 4;
-  }
-
-  mTarget = nullptr;
-
-  // Since the target changes the backing texture will change, and this will
-  // no longer be valid.
-  mThebesSurface = nullptr;
-  mIsEntireFrameInvalid = false;
-  mPredictManyRedrawCalls = false;
-
-  return NS_OK;
-}
-
-static void
-WarnAboutUnexpectedStyle(nsHTMLCanvasElement* canvasElement)
-{
-  nsContentUtils::ReportToConsole(
-    nsIScriptError::warningFlag,
-    "Canvas",
-    canvasElement ? canvasElement->OwnerDoc() : nullptr,
-    nsContentUtils::eDOM_PROPERTIES,
-    "UnexpectedCanvasVariantStyle");
-}
-
-void
-CanvasRenderingContext2D::SetStyleFromString(const nsAString& str,
-                                             Style whichStyle)
-{
-  MOZ_ASSERT(!str.IsVoid());
-
-  nscolor color;
-  if (!ParseColor(str, &color)) {
-    return;
-  }
-
-  CurrentState().SetColorStyle(whichStyle, color);
-}
-
-nsISupports*
-CanvasRenderingContext2D::GetStyleAsStringOrInterface(nsAString& aStr,
-                                                      CanvasMultiGetterType& aType,
-                                                      Style aWhichStyle)
-{
-  const ContextState &state = CurrentState();
-  nsISupports* supports;
-  if (state.patternStyles[aWhichStyle]) {
-    aStr.SetIsVoid(true);
-    supports = state.patternStyles[aWhichStyle];
-    aType = CMG_STYLE_PATTERN;
-  } else if (state.gradientStyles[aWhichStyle]) {
-    aStr.SetIsVoid(true);
-    supports = state.gradientStyles[aWhichStyle];
-    aType = CMG_STYLE_GRADIENT;
-  } else {
-    StyleColorToString(state.colorStyles[aWhichStyle], aStr);
-    supports = nullptr;
-    aType = CMG_STYLE_STRING;
-  }
-  return supports;
-}
-
-// static
-void
-CanvasRenderingContext2D::StyleColorToString(const nscolor& aColor, nsAString& aStr)
-{
-  // We can't reuse the normal CSS color stringification code,
-  // because the spec calls for a different algorithm for canvas.
-  if (NS_GET_A(aColor) == 255) {
-    CopyUTF8toUTF16(nsPrintfCString("#%02x%02x%02x",
-                                    NS_GET_R(aColor),
-                                    NS_GET_G(aColor),
-                                    NS_GET_B(aColor)),
-                    aStr);
-  } else {
-    CopyUTF8toUTF16(nsPrintfCString("rgba(%d, %d, %d, ",
-                                    NS_GET_R(aColor),
-                                    NS_GET_G(aColor),
-                                    NS_GET_B(aColor)),
-                    aStr);
-    aStr.AppendFloat(nsStyleUtil::ColorComponentToFloat(NS_GET_A(aColor)));
-    aStr.Append(')');
-  }
-}
-
-nsresult
-CanvasRenderingContext2D::Redraw()
-{
-  if (mIsEntireFrameInvalid) {
-    return NS_OK;
-  }
-
-  mIsEntireFrameInvalid = true;
-
-  if (!mCanvasElement) {
-    NS_ASSERTION(mDocShell, "Redraw with no canvas element or docshell!");
-    return NS_OK;
-  }
-
-  if (!mThebesSurface)
-    mThebesSurface =
-      gfxPlatform::GetPlatform()->GetThebesSurfaceForDrawTarget(mTarget);
-  mThebesSurface->MarkDirty();
-
-  nsSVGEffects::InvalidateDirectRenderingObservers(mCanvasElement);
-
-  mCanvasElement->InvalidateCanvasContent(nullptr);
-
-  return NS_OK;
-}
-
-void
-CanvasRenderingContext2D::Redraw(const mgfx::Rect &r)
-{
-  ++mInvalidateCount;
-
-  if (mIsEntireFrameInvalid) {
-    return;
-  }
-
-  if (mPredictManyRedrawCalls ||
-    mInvalidateCount > kCanvasMaxInvalidateCount) {
-    Redraw();
-    return;
-  }
-
-  if (!mCanvasElement) {
-    NS_ASSERTION(mDocShell, "Redraw with no canvas element or docshell!");
-    return;
-  }
-
-  if (!mThebesSurface)
-    mThebesSurface =
-      gfxPlatform::GetPlatform()->GetThebesSurfaceForDrawTarget(mTarget);
-  mThebesSurface->MarkDirty();
-
-  nsSVGEffects::InvalidateDirectRenderingObservers(mCanvasElement);
-
-  mCanvasElement->InvalidateCanvasContent(&r);
-}
-
-void
-CanvasRenderingContext2D::RedrawUser(const gfxRect& r)
-{
-  if (mIsEntireFrameInvalid) {
-    ++mInvalidateCount;
-    return;
-  }
-
-  mgfx::Rect newr =
-    mTarget->GetTransform().TransformBounds(ToRect(r));
-  Redraw(newr);
-}
-
-void
-CanvasRenderingContext2D::EnsureTarget()
-{
-  if (mTarget) {
-    return;
-  }
-
-   // Check that the dimensions are sane
-  IntSize size(mWidth, mHeight);
-  if (size.width <= 0xFFFF && size.height <= 0xFFFF &&
-      size.width >= 0 && size.height >= 0) {
-    SurfaceFormat format = GetSurfaceFormat();
-    nsIDocument* ownerDoc = nullptr;
-    if (mCanvasElement) {
-      ownerDoc = mCanvasElement->OwnerDoc();
-    }
-
-    nsRefPtr<LayerManager> layerManager = nullptr;
-
-    if (ownerDoc) {
-      layerManager =
-        nsContentUtils::PersistentLayerManagerForDocument(ownerDoc);
-    }
-
-     if (layerManager) {
-       mTarget = layerManager->CreateDrawTarget(size, format);
-     } else {
-       mTarget = gfxPlatform::GetPlatform()->CreateOffscreenDrawTarget(size, format);
-     }
-  }
-
-  if (mTarget) {
-    if (gCanvasAzureMemoryReporter == nullptr) {
-        gCanvasAzureMemoryReporter = new NS_MEMORY_REPORTER_NAME(CanvasAzureMemory);
-      NS_RegisterMemoryReporter(gCanvasAzureMemoryReporter);
-    }
-
-    gCanvasAzureMemoryUsed += mWidth * mHeight * 4;
-    JSContext* context = nsContentUtils::GetCurrentJSContext();
-    if (context) {
-      JS_updateMallocCounter(context, mWidth * mHeight * 4);
-    }
-
-    mTarget->ClearRect(mgfx::Rect(Point(0, 0), Size(mWidth, mHeight)));
-    // Force a full layer transaction since we didn't have a layer before
-    // and now we might need one.
-    if (mCanvasElement) {
-      mCanvasElement->InvalidateCanvas();
-    }
-    // Calling Redraw() tells our invalidation machinery that the entire
-    // canvas is already invalid, which can speed up future drawing.
-    Redraw();
-  } else {
-    EnsureErrorTarget();
-    mTarget = sErrorTarget;
-  }
-}
-
-NS_IMETHODIMP
-CanvasRenderingContext2D::SetDimensions(int32_t width, int32_t height)
-{
-  ClearTarget();
-
-  // Zero sized surfaces cause issues, so just go with 1x1.
-  if (height == 0 || width == 0) {
-    mZero = true;
-    mWidth = 1;
-    mHeight = 1;
-  } else {
-    mZero = false;
-    mWidth = width;
-    mHeight = height;
-  }
-
-  return NS_OK;
-}
-
-void
-CanvasRenderingContext2D::ClearTarget()
-{
-  Reset();
-
-  mResetLayer = true;
-
-  // set up the initial canvas defaults
-  mStyleStack.Clear();
-  mPathBuilder = nullptr;
-  mPath = nullptr;
-  mDSPathBuilder = nullptr;
-
-  ContextState *state = mStyleStack.AppendElement();
-  state->globalAlpha = 1.0;
-
-  state->colorStyles[STYLE_FILL] = NS_RGB(0,0,0);
-  state->colorStyles[STYLE_STROKE] = NS_RGB(0,0,0);
-  state->shadowColor = NS_RGBA(0,0,0,0);
-}
-
-NS_IMETHODIMP
-CanvasRenderingContext2D::InitializeWithSurface(nsIDocShell *shell,
-                                                gfxASurface *surface,
-                                                int32_t width,
-                                                int32_t height)
-{
-  mDocShell = shell;
-  mThebesSurface = surface;
-
-  SetDimensions(width, height);
-  mTarget = gfxPlatform::GetPlatform()->
-    CreateDrawTargetForSurface(surface, IntSize(width, height));
-  if (!mTarget) {
-    EnsureErrorTarget();
-    mTarget = sErrorTarget;
-  }
-
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-CanvasRenderingContext2D::SetIsOpaque(bool isOpaque)
-{
-  if (isOpaque != mOpaque) {
-    mOpaque = isOpaque;
-    ClearTarget();
-  }
-
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-CanvasRenderingContext2D::SetIsIPC(bool isIPC)
-{
-  if (isIPC != mIPC) {
-    mIPC = isIPC;
-    ClearTarget();
-  }
-
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-CanvasRenderingContext2D::Render(gfxContext *ctx, gfxPattern::GraphicsFilter aFilter, uint32_t aFlags)
-{
-  nsresult rv = NS_OK;
-
-  EnsureTarget();
-  if (!IsTargetValid()) {
-    return NS_ERROR_FAILURE;
-  }
-
-  nsRefPtr<gfxASurface> surface;
-
-  if (NS_FAILED(GetThebesSurface(getter_AddRefs(surface)))) {
-    return NS_ERROR_FAILURE;
-  }
-
-  nsRefPtr<gfxPattern> pat = new gfxPattern(surface);
-
-  pat->SetFilter(aFilter);
-  pat->SetExtend(gfxPattern::EXTEND_PAD);
-
-  gfxContext::GraphicsOperator op = ctx->CurrentOperator();
-  if (mOpaque)
-      ctx->SetOperator(gfxContext::OPERATOR_SOURCE);
-
-  // XXX I don't want to use PixelSnapped here, but layout doesn't guarantee
-  // pixel alignment for this stuff!
-  ctx->NewPath();
-  ctx->PixelSnappedRectangleAndSetPattern(gfxRect(0, 0, mWidth, mHeight), pat);
-  ctx->Fill();
-
-  if (mOpaque)
-      ctx->SetOperator(op);
-
-  if (!(aFlags & RenderFlagPremultAlpha)) {
-      nsRefPtr<gfxASurface> curSurface = ctx->CurrentSurface();
-      nsRefPtr<gfxImageSurface> gis = curSurface->GetAsImageSurface();
-      NS_ABORT_IF_FALSE(gis, "If non-premult alpha, must be able to get image surface!");
-
-      gfxUtils::UnpremultiplyImageSurface(gis);
-  }
-
-  return rv;
-}
-
-NS_IMETHODIMP
-CanvasRenderingContext2D::GetInputStream(const char *aMimeType,
-                                         const PRUnichar *aEncoderOptions,
-                                         nsIInputStream **aStream)
-{
-  EnsureTarget();
-  if (!IsTargetValid()) {
-    return NS_ERROR_FAILURE;
-  }
-
-  nsRefPtr<gfxASurface> surface;
-
-  if (NS_FAILED(GetThebesSurface(getter_AddRefs(surface)))) {
-    return NS_ERROR_FAILURE;
-  }
-
-  nsresult rv;
-  const char encoderPrefix[] = "@mozilla.org/image/encoder;2?type=";
-  nsAutoArrayPtr<char> conid(new (std::nothrow) char[strlen(encoderPrefix) + strlen(aMimeType) + 1]);
-
-  if (!conid) {
-    return NS_ERROR_OUT_OF_MEMORY;
-  }
-
-  strcpy(conid, encoderPrefix);
-  strcat(conid, aMimeType);
-
-  nsCOMPtr<imgIEncoder> encoder = do_CreateInstance(conid);
-  if (!encoder) {
-    return NS_ERROR_FAILURE;
-  }
-
-  nsAutoArrayPtr<uint8_t> imageBuffer(new (std::nothrow) uint8_t[mWidth * mHeight * 4]);
-  if (!imageBuffer) {
-    return NS_ERROR_OUT_OF_MEMORY;
-  }
-
-  nsRefPtr<gfxImageSurface> imgsurf =
-    new gfxImageSurface(imageBuffer.get(),
-                        gfxIntSize(mWidth, mHeight),
-                        mWidth * 4,
-                        gfxASurface::ImageFormatARGB32);
-
-  if (!imgsurf || imgsurf->CairoStatus()) {
-    return NS_ERROR_FAILURE;
-  }
-
-  nsRefPtr<gfxContext> ctx = new gfxContext(imgsurf);
-
-  if (!ctx || ctx->HasError()) {
-    return NS_ERROR_FAILURE;
-  }
-
-  ctx->SetOperator(gfxContext::OPERATOR_SOURCE);
-  ctx->SetSource(surface, gfxPoint(0, 0));
-  ctx->Paint();
-
-  rv = encoder->InitFromData(imageBuffer.get(),
-                              mWidth * mHeight * 4, mWidth, mHeight, mWidth * 4,
-                              imgIEncoder::INPUT_FORMAT_HOSTARGB,
-                              nsDependentString(aEncoderOptions));
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  return CallQueryInterface(encoder, aStream);
-}
-
-SurfaceFormat
-CanvasRenderingContext2D::GetSurfaceFormat() const
-{
-  return mOpaque ? FORMAT_B8G8R8X8 : FORMAT_B8G8R8A8;
-}
-
-//
-// state
-//
-
-void
-CanvasRenderingContext2D::Save()
-{
-  EnsureTarget();
-  mStyleStack[mStyleStack.Length() - 1].transform = mTarget->GetTransform();
-  mStyleStack.SetCapacity(mStyleStack.Length() + 1);
-  mStyleStack.AppendElement(CurrentState());
-}
-
-void
-CanvasRenderingContext2D::Restore()
-{
-  if (mStyleStack.Length() - 1 == 0)
-    return;
-
-  TransformWillUpdate();
-
-  for (uint32_t i = 0; i < CurrentState().clipsPushed.size(); i++) {
-    mTarget->PopClip();
-  }
-
-  mStyleStack.RemoveElementAt(mStyleStack.Length() - 1);
-
-  mTarget->SetTransform(CurrentState().transform);
-}
-
-//
-// transformations
-//
-
-void
-CanvasRenderingContext2D::Scale(double x, double y, ErrorResult& error)
-{
-  if (!FloatValidate(x,y)) {
-    return;
-  }
-
-  TransformWillUpdate();
-  if (!IsTargetValid()) {
-    error.Throw(NS_ERROR_FAILURE);
-    return;
-  }
-
-  Matrix newMatrix = mTarget->GetTransform();
-  mTarget->SetTransform(newMatrix.Scale(x, y));
-}
-
-void
-CanvasRenderingContext2D::Rotate(double angle, ErrorResult& error)
-{
-  if (!FloatValidate(angle)) {
-    return;
-  }
-
-  TransformWillUpdate();
-  if (!IsTargetValid()) {
-    error.Throw(NS_ERROR_FAILURE);
-    return;
-  }
-
-
-  Matrix rotation = Matrix::Rotation(angle);
-  mTarget->SetTransform(rotation * mTarget->GetTransform());
-}
-
-void
-CanvasRenderingContext2D::Translate(double x, double y, ErrorResult& error)
-{
-  if (!FloatValidate(x,y)) {
-    return;
-  }
-
-  TransformWillUpdate();
-  if (!IsTargetValid()) {
-    error.Throw(NS_ERROR_FAILURE);
-    return;
-  }
-
-  Matrix newMatrix = mTarget->GetTransform();
-  mTarget->SetTransform(newMatrix.Translate(x, y));
-}
-
-void
-CanvasRenderingContext2D::Transform(double m11, double m12, double m21,
-                                    double m22, double dx, double dy,
-                                    ErrorResult& error)
-{
-  if (!FloatValidate(m11,m12,m21,m22,dx,dy)) {
-    return;
-  }
-
-  TransformWillUpdate();
-  if (!IsTargetValid()) {
-    error.Throw(NS_ERROR_FAILURE);
-    return;
-  }
-
-  Matrix matrix(m11, m12, m21, m22, dx, dy);
-  mTarget->SetTransform(matrix * mTarget->GetTransform());
-}
-
-void
-CanvasRenderingContext2D::SetTransform(double m11, double m12,
-                                       double m21, double m22,
-                                       double dx, double dy,
-                                       ErrorResult& error)
-{
-  if (!FloatValidate(m11,m12,m21,m22,dx,dy)) {
-    return;
-  }
-
-  TransformWillUpdate();
-  if (!IsTargetValid()) {
-    error.Throw(NS_ERROR_FAILURE);
-    return;
-  }
-
-  Matrix matrix(m11, m12, m21, m22, dx, dy);
-  mTarget->SetTransform(matrix);
-}
-
-JSObject*
-MatrixToJSObject(JSContext* cx, const Matrix& matrix, ErrorResult& error)
-{
-  jsval elts[] = {
-    DOUBLE_TO_JSVAL(matrix._11), DOUBLE_TO_JSVAL(matrix._12),
-    DOUBLE_TO_JSVAL(matrix._21), DOUBLE_TO_JSVAL(matrix._22),
-    DOUBLE_TO_JSVAL(matrix._31), DOUBLE_TO_JSVAL(matrix._32)
-  };
-
-  // XXX Should we enter GetWrapper()'s compartment?
-  JSObject* obj = JS_NewArrayObject(cx, 6, elts);
-  if  (!obj) {
-    error.Throw(NS_ERROR_OUT_OF_MEMORY);
-  }
-  return obj;
-}
-
-bool
-ObjectToMatrix(JSContext* cx, JSObject& obj, Matrix& matrix, ErrorResult& error)
-{
-  uint32_t length;
-  if (!JS_GetArrayLength(cx, &obj, &length) || length != 6) {
-    // Not an array-like thing or wrong size
-    error.Throw(NS_ERROR_INVALID_ARG);
-    return false;
-  }
-
-  Float* elts[] = { &matrix._11, &matrix._12, &matrix._21, &matrix._22,
-                    &matrix._31, &matrix._32 };
-  for (uint32_t i = 0; i < 6; ++i) {
-    jsval elt;
-    double d;
-    if (!JS_GetElement(cx, &obj, i, &elt)) {
-      error.Throw(NS_ERROR_FAILURE);
-      return false;
-    }
-    if (!CoerceDouble(elt, &d)) {
-      error.Throw(NS_ERROR_INVALID_ARG);
-      return false;
-    }
-    if (!FloatValidate(d)) {
-      // This is weird, but it's the behavior of SetTransform()
-      return false;
-    }
-    *elts[i] = Float(d);
-  }
-  return true;
-}
-
-void
-CanvasRenderingContext2D::SetMozCurrentTransform(JSContext* cx,
-                                                 JSObject& currentTransform,
-                                                 ErrorResult& error)
-{
-  EnsureTarget();
-  if (!IsTargetValid()) {
-    error.Throw(NS_ERROR_FAILURE);
-    return;
-  }
-
-  Matrix newCTM;
-  if (ObjectToMatrix(cx, currentTransform, newCTM, error)) {
-    mTarget->SetTransform(newCTM);
-  }
-}
-
-JSObject*
-CanvasRenderingContext2D::GetMozCurrentTransform(JSContext* cx,
-                                                 ErrorResult& error) const
-{
-  return MatrixToJSObject(cx, mTarget ? mTarget->GetTransform() : Matrix(), error);
-}
-
-void
-CanvasRenderingContext2D::SetMozCurrentTransformInverse(JSContext* cx,
-                                                        JSObject& currentTransform,
-                                                        ErrorResult& error)
-{
-  EnsureTarget();
-  if (!IsTargetValid()) {
-    error.Throw(NS_ERROR_FAILURE);
-    return;
-  }
-
-  Matrix newCTMInverse;
-  if (ObjectToMatrix(cx, currentTransform, newCTMInverse, error)) {
-    // XXX ERRMSG we need to report an error to developers here! (bug 329026)
-    if (newCTMInverse.Invert()) {
-      mTarget->SetTransform(newCTMInverse);
-    }
-  }
-}
-
-JSObject*
-CanvasRenderingContext2D::GetMozCurrentTransformInverse(JSContext* cx,
-                                                        ErrorResult& error) const
-{
-  if (!mTarget) {
-    return MatrixToJSObject(cx, Matrix(), error);
-  }
-
-  Matrix ctm = mTarget->GetTransform();
-
-  if (!ctm.Invert()) {
-    double NaN = JSVAL_TO_DOUBLE(JS_GetNaNValue(cx));
-    ctm = Matrix(NaN, NaN, NaN, NaN, NaN, NaN);
-  }
-
-  return MatrixToJSObject(cx, ctm, error);
-}
-
-//
-// colors
-//
-
-void
-CanvasRenderingContext2D::SetStyleFromJSValue(JSContext* cx,
-                                              JS::Value& value,
-                                              Style whichStyle)
-{
-  if (value.isString()) {
-    nsDependentJSString strokeStyle;
-    if (strokeStyle.init(cx, value.toString())) {
-      SetStyleFromString(strokeStyle, whichStyle);
-    }
-    return;
-  }
-
-  if (value.isObject()) {
-    nsCOMPtr<nsISupports> holder;
-
-    CanvasGradient* gradient;
-    nsresult rv = xpc_qsUnwrapArg<CanvasGradient>(cx, value, &gradient,
-                                                  static_cast<nsISupports**>(getter_AddRefs(holder)),
-                                                  &value);
-    if (NS_SUCCEEDED(rv)) {
-      SetStyleFromGradient(gradient, whichStyle);
-      return;
-    }
-
-    CanvasPattern* pattern;
-    rv = xpc_qsUnwrapArg<CanvasPattern>(cx, value, &pattern,
-                                        static_cast<nsISupports**>(getter_AddRefs(holder)),
-                                        &value);
-    if (NS_SUCCEEDED(rv)) {
-      SetStyleFromPattern(pattern, whichStyle);
-      return;
-    }
-  }
-
-  WarnAboutUnexpectedStyle(mCanvasElement);
-}
-
-static JS::Value
-WrapStyle(JSContext* cx, JSObject* obj,
-          CanvasRenderingContext2D::CanvasMultiGetterType type,
-          nsAString& str, nsISupports* supports, ErrorResult& error)
-{
-  JS::Value v;
-  bool ok;
-  switch (type) {
-    case CanvasRenderingContext2D::CMG_STYLE_STRING:
-    {
-      ok = xpc::StringToJsval(cx, str, &v);
-      break;
-    }
-    case CanvasRenderingContext2D::CMG_STYLE_PATTERN:
-    case CanvasRenderingContext2D::CMG_STYLE_GRADIENT:
-    {
-      ok = dom::WrapObject(cx, obj, supports, &v);
-      break;
-    }
-    default:
-      MOZ_NOT_REACHED("unexpected CanvasMultiGetterType");
-  }
-  if (!ok) {
-    error.Throw(NS_ERROR_FAILURE);
-  }
-  return v;
-}
-
-
-JS::Value
-CanvasRenderingContext2D::GetStrokeStyle(JSContext* cx,
-                                         ErrorResult& error)
-{
-  nsString str;
-  CanvasMultiGetterType t;
-  nsISupports* supports = GetStyleAsStringOrInterface(str, t, STYLE_STROKE);
-  return WrapStyle(cx, GetWrapper(), t, str, supports, error);
-}
-
-JS::Value
-CanvasRenderingContext2D::GetFillStyle(JSContext* cx,
-                                       ErrorResult& error)
-{
-  nsString str;
-  CanvasMultiGetterType t;
-  nsISupports* supports = GetStyleAsStringOrInterface(str, t, STYLE_FILL);
-  return WrapStyle(cx, GetWrapper(), t, str, supports, error);
-}
-
-void
-CanvasRenderingContext2D::SetFillRule(const nsAString& aString)
-{
-  FillRule rule;
-
-  if (aString.EqualsLiteral("evenodd"))
-    rule = FILL_EVEN_ODD;
-  else if (aString.EqualsLiteral("nonzero"))
-    rule = FILL_WINDING;
-  else
-    return;
-
-  CurrentState().fillRule = rule;
-}
-
-void
-CanvasRenderingContext2D::GetFillRule(nsAString& aString)
-{
-  switch (CurrentState().fillRule) {
-  case FILL_WINDING:
-    aString.AssignLiteral("nonzero"); break;
-  case FILL_EVEN_ODD:
-    aString.AssignLiteral("evenodd"); break;
-  }
-}
-//
-// gradients and patterns
-//
-already_AddRefed<nsIDOMCanvasGradient>
-CanvasRenderingContext2D::CreateLinearGradient(double x0, double y0, double x1, double y1,
-                                               ErrorResult& aError)
-{
-  if (!FloatValidate(x0,y0,x1,y1)) {
-    aError.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
-    return nullptr;
-  }
-
-  nsRefPtr<nsIDOMCanvasGradient> grad =
-    new CanvasLinearGradient(Point(x0, y0), Point(x1, y1));
-
-  return grad.forget();
-}
-
-already_AddRefed<nsIDOMCanvasGradient>
-CanvasRenderingContext2D::CreateRadialGradient(double x0, double y0, double r0,
-                                               double x1, double y1, double r1,
-                                               ErrorResult& aError)
-{
-  if (!FloatValidate(x0,y0,r0,x1,y1,r1)) {
-    aError.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
-    return nullptr;
-  }
-
-  if (r0 < 0.0 || r1 < 0.0) {
-    aError.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
-    return nullptr;
-  }
-
-  nsRefPtr<nsIDOMCanvasGradient> grad =
-    new CanvasRadialGradient(Point(x0, y0), r0, Point(x1, y1), r1);
-
-  return grad.forget();
-}
-
-already_AddRefed<nsIDOMCanvasPattern>
-CanvasRenderingContext2D::CreatePattern(const HTMLImageOrCanvasOrVideoElement& element,
-                                        const nsAString& repeat,
-                                        ErrorResult& error)
-{
-  CanvasPattern::RepeatMode repeatMode =
-    CanvasPattern::NOREPEAT;
-
-  if (repeat.IsEmpty() || repeat.EqualsLiteral("repeat")) {
-    repeatMode = CanvasPattern::REPEAT;
-  } else if (repeat.EqualsLiteral("repeat-x")) {
-    repeatMode = CanvasPattern::REPEATX;
-  } else if (repeat.EqualsLiteral("repeat-y")) {
-    repeatMode = CanvasPattern::REPEATY;
-  } else if (repeat.EqualsLiteral("no-repeat")) {
-    repeatMode = CanvasPattern::NOREPEAT;
-  } else {
-    error.Throw(NS_ERROR_DOM_SYNTAX_ERR);
-    return NULL;
-  }
-
-  Element* htmlElement;
-  if (element.IsHTMLCanvasElement()) {
-    nsHTMLCanvasElement* canvas = element.GetAsHTMLCanvasElement();
-    htmlElement = canvas;
-
-    nsIntSize size = canvas->GetSize();
-    if (size.width == 0 || size.height == 0) {
-      error.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
-      return NULL;
-    }
-
-    // Special case for Canvas, which could be an Azure canvas!
-    nsICanvasRenderingContextInternal *srcCanvas = canvas->GetContextAtIndex(0);
-    if (srcCanvas) {
-      // This might not be an Azure canvas!
-      RefPtr<SourceSurface> srcSurf = srcCanvas->GetSurfaceSnapshot();
-
-      nsRefPtr<CanvasPattern> pat =
-        new CanvasPattern(srcSurf, repeatMode, htmlElement->NodePrincipal(), canvas->IsWriteOnly(), false);
-
-      return pat.forget();
-    }
-  } else if (element.IsHTMLImageElement()) {
-    htmlElement = element.GetAsHTMLImageElement();
-  } else {
-    htmlElement = element.GetAsHTMLVideoElement();
-  }
-
-  // The canvas spec says that createPattern should use the first frame
-  // of animated images
-  nsLayoutUtils::SurfaceFromElementResult res =
-    nsLayoutUtils::SurfaceFromElement(htmlElement,
-      nsLayoutUtils::SFE_WANT_FIRST_FRAME | nsLayoutUtils::SFE_WANT_NEW_SURFACE);
-
-  if (!res.mSurface) {
-    error.Throw(NS_ERROR_NOT_AVAILABLE);
-    return NULL;
-  }
-
-  // Ignore nullptr cairo surfaces! See bug 666312.
-  if (!res.mSurface->CairoSurface() || res.mSurface->CairoStatus()) {
-    return NULL;
-  }
-
-  EnsureTarget();
-  RefPtr<SourceSurface> srcSurf =
-    gfxPlatform::GetPlatform()->GetSourceSurfaceForSurface(mTarget, res.mSurface);
-
-  nsRefPtr<CanvasPattern> pat =
-    new CanvasPattern(srcSurf, repeatMode, res.mPrincipal,
-                             res.mIsWriteOnly, res.mCORSUsed);
-
-  return pat.forget();
-}
-
-//
-// shadows
-//
-void
-CanvasRenderingContext2D::SetShadowColor(const nsAString& shadowColor)
-{
-  nscolor color;
-  if (!ParseColor(shadowColor, &color)) {
-    return;
-  }
-
-  CurrentState().shadowColor = color;
-}
-
-//
-// rects
-//
-
-void
-CanvasRenderingContext2D::ClearRect(double x, double y, double w,
-                                    double h)
-{
-  if (!FloatValidate(x,y,w,h) || !mTarget) {
-    return;
-  }
-
-  mTarget->ClearRect(mgfx::Rect(x, y, w, h));
-
-  RedrawUser(gfxRect(x, y, w, h));
-}
-
-void
-CanvasRenderingContext2D::FillRect(double x, double y, double w,
-                                   double h)
-{
-  if (!FloatValidate(x,y,w,h)) {
-    return;
-  }
-
-  const ContextState &state = CurrentState();
-
-  if (state.patternStyles[STYLE_FILL]) {
-    CanvasPattern::RepeatMode repeat =
-      state.patternStyles[STYLE_FILL]->mRepeat;
-    // In the FillRect case repeat modes are easy to deal with.
-    bool limitx = repeat == CanvasPattern::NOREPEAT || repeat == CanvasPattern::REPEATY;
-    bool limity = repeat == CanvasPattern::NOREPEAT || repeat == CanvasPattern::REPEATX;
-
-    IntSize patternSize =
-      state.patternStyles[STYLE_FILL]->mSurface->GetSize();
-
-    // We always need to execute painting for non-over operators, even if
-    // we end up with w/h = 0.
-    if (limitx) {
-      if (x < 0) {
-        w += x;
-        if (w < 0) {
-          w = 0;
-        }
-
-        x = 0;
-      }
-      if (x + w > patternSize.width) {
-        w = patternSize.width - x;
-        if (w < 0) {
-          w = 0;
-        }
-      }
-    }
-    if (limity) {
-      if (y < 0) {
-        h += y;
-        if (h < 0) {
-          h = 0;
-        }
-
-        y = 0;
-      }
-      if (y + h > patternSize.height) {
-        h = patternSize.height - y;
-        if (h < 0) {
-          h = 0;
-        }
-      }
-    }
-  }
-
-  mgfx::Rect bounds;
-
-  EnsureTarget();
-  if (NeedToDrawShadow()) {
-    bounds = mgfx::Rect(x, y, w, h);
-    bounds = mTarget->GetTransform().TransformBounds(bounds);
-  }
-
-  AdjustedTarget(this, bounds.IsEmpty() ? nullptr : &bounds)->
-    FillRect(mgfx::Rect(x, y, w, h),
-             CanvasGeneralPattern().ForStyle(this, STYLE_FILL, mTarget),
-             DrawOptions(state.globalAlpha, UsedOperation()));
-
-  RedrawUser(gfxRect(x, y, w, h));
-}
-
-void
-CanvasRenderingContext2D::StrokeRect(double x, double y, double w,
-                                     double h)
-{
-  if (!FloatValidate(x,y,w,h)) {
-    return;
-  }
-
-  const ContextState &state = CurrentState();
-
-  mgfx::Rect bounds;
-
-  if (!w && !h) {
-    return;
-  }
-
-  EnsureTarget();
-  if (!IsTargetValid()) {
-    return;
-  }
-
-  if (NeedToDrawShadow()) {
-    bounds = mgfx::Rect(x - state.lineWidth / 2.0f, y - state.lineWidth / 2.0f,
-                        w + state.lineWidth, h + state.lineWidth);
-    bounds = mTarget->GetTransform().TransformBounds(bounds);
-  }
-
-  if (!h) {
-    CapStyle cap = CAP_BUTT;
-    if (state.lineJoin == JOIN_ROUND) {
-      cap = CAP_ROUND;
-    }
-    AdjustedTarget(this, bounds.IsEmpty() ? nullptr : &bounds)->
-      StrokeLine(Point(x, y), Point(x + w, y),
-                  CanvasGeneralPattern().ForStyle(this, STYLE_STROKE, mTarget),
-                  StrokeOptions(state.lineWidth, state.lineJoin,
-                                cap, state.miterLimit,
-                                state.dash.Length(),
-                                state.dash.Elements(),
-                                state.dashOffset),
-                  DrawOptions(state.globalAlpha, UsedOperation()));
-    return;
-  }
-
-  if (!w) {
-    CapStyle cap = CAP_BUTT;
-    if (state.lineJoin == JOIN_ROUND) {
-      cap = CAP_ROUND;
-    }
-    AdjustedTarget(this, bounds.IsEmpty() ? nullptr : &bounds)->
-      StrokeLine(Point(x, y), Point(x, y + h),
-                  CanvasGeneralPattern().ForStyle(this, STYLE_STROKE, mTarget),
-                  StrokeOptions(state.lineWidth, state.lineJoin,
-                                cap, state.miterLimit,
-                                state.dash.Length(),
-                                state.dash.Elements(),
-                                state.dashOffset),
-                  DrawOptions(state.globalAlpha, UsedOperation()));
-    return;
-  }
-
-  AdjustedTarget(this, bounds.IsEmpty() ? nullptr : &bounds)->
-    StrokeRect(mgfx::Rect(x, y, w, h),
-                CanvasGeneralPattern().ForStyle(this, STYLE_STROKE, mTarget),
-                StrokeOptions(state.lineWidth, state.lineJoin,
-                              state.lineCap, state.miterLimit,
-                              state.dash.Length(),
-                              state.dash.Elements(),
-                              state.dashOffset),
-                DrawOptions(state.globalAlpha, UsedOperation()));
-
-  Redraw();
-}
-
-//
-// path bits
-//
-
-void
-CanvasRenderingContext2D::BeginPath()
-{
-  mPath = nullptr;
-  mPathBuilder = nullptr;
-  mDSPathBuilder = nullptr;
-  mPathTransformWillUpdate = false;
-}
-
-void
-CanvasRenderingContext2D::Fill()
-{
-  EnsureUserSpacePath();
-
-  if (!mPath) {
-    return;
-  }
-
-  mgfx::Rect bounds;
-
-  if (NeedToDrawShadow()) {
-    bounds = mPath->GetBounds(mTarget->GetTransform());
-  }
-
-  AdjustedTarget(this, bounds.IsEmpty() ? nullptr : &bounds)->
-    Fill(mPath, CanvasGeneralPattern().ForStyle(this, STYLE_FILL, mTarget),
-         DrawOptions(CurrentState().globalAlpha, UsedOperation()));
-
-  Redraw();
-}
-
-void
-CanvasRenderingContext2D::Stroke()
-{
-  EnsureUserSpacePath();
-
-  if (!mPath) {
-    return;
-  }
-
-  const ContextState &state = CurrentState();
-
-  StrokeOptions strokeOptions(state.lineWidth, state.lineJoin,
-                              state.lineCap, state.miterLimit,
-                              state.dash.Length(), state.dash.Elements(),
-                              state.dashOffset);
-
-  mgfx::Rect bounds;
-  if (NeedToDrawShadow()) {
-    bounds =
-      mPath->GetStrokedBounds(strokeOptions, mTarget->GetTransform());
-  }
-
-  AdjustedTarget(this, bounds.IsEmpty() ? nullptr : &bounds)->
-    Stroke(mPath, CanvasGeneralPattern().ForStyle(this, STYLE_STROKE, mTarget),
-           strokeOptions, DrawOptions(state.globalAlpha, UsedOperation()));
-
-  Redraw();
-}
-
-void
-CanvasRenderingContext2D::Clip()
-{
-  EnsureUserSpacePath();
-
-  if (!mPath) {
-    return;
-  }
-
-  mTarget->PushClip(mPath);
-  CurrentState().clipsPushed.push_back(mPath);
-}
-
-void
-CanvasRenderingContext2D::ArcTo(double x1, double y1, double x2,
-                                double y2, double radius,
-                                ErrorResult& error)
-{
-  if (!FloatValidate(x1, y1, x2, y2, radius)) {
-    return;
-  }
-
-  if (radius < 0) {
-    error.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
-    return;
-  }
-
-  EnsureWritablePath();
-
-  // Current point in user space!
-  Point p0;
-  if (mPathBuilder) {
-    p0 = mPathBuilder->CurrentPoint();
-  } else {
-    Matrix invTransform = mTarget->GetTransform();
-    if (!invTransform.Invert()) {
-      return;
-    }
-
-    p0 = invTransform * mDSPathBuilder->CurrentPoint();
-  }
-
-  Point p1(x1, y1);
-  Point p2(x2, y2);
-
-  // Execute these calculations in double precision to avoid cumulative
-  // rounding errors.
-  double dir, a2, b2, c2, cosx, sinx, d, anx, any,
-         bnx, bny, x3, y3, x4, y4, cx, cy, angle0, angle1;
-  bool anticlockwise;
-
-  if (p0 == p1 || p1 == p2 || radius == 0) {
-    LineTo(p1.x, p1.y);
-    return;
-  }
-
-  // Check for colinearity
-  dir = (p2.x - p1.x) * (p0.y - p1.y) + (p2.y - p1.y) * (p1.x - p0.x);
-  if (dir == 0) {
-    LineTo(p1.x, p1.y);
-    return;
-  }
-
-
-  // XXX - Math for this code was already available from the non-azure code
-  // and would be well tested. Perhaps converting to bezier directly might
-  // be more efficient longer run.
-  a2 = (p0.x-x1)*(p0.x-x1) + (p0.y-y1)*(p0.y-y1);
-  b2 = (x1-x2)*(x1-x2) + (y1-y2)*(y1-y2);
-  c2 = (p0.x-x2)*(p0.x-x2) + (p0.y-y2)*(p0.y-y2);
-  cosx = (a2+b2-c2)/(2*sqrt(a2*b2));
-
-  sinx = sqrt(1 - cosx*cosx);
-  d = radius / ((1 - cosx) / sinx);
-
-  anx = (x1-p0.x) / sqrt(a2);
-  any = (y1-p0.y) / sqrt(a2);
-  bnx = (x1-x2) / sqrt(b2);
-  bny = (y1-y2) / sqrt(b2);
-  x3 = x1 - anx*d;
-  y3 = y1 - any*d;
-  x4 = x1 - bnx*d;
-  y4 = y1 - bny*d;
-  anticlockwise = (dir < 0);
-  cx = x3 + any*radius*(anticlockwise ? 1 : -1);
-  cy = y3 - anx*radius*(anticlockwise ? 1 : -1);
-  angle0 = atan2((y3-cy), (x3-cx));
-  angle1 = atan2((y4-cy), (x4-cx));
-
-
-  LineTo(x3, y3);
-
-  Arc(cx, cy, radius, angle0, angle1, anticlockwise, error);
-}
-
-void
-CanvasRenderingContext2D::Arc(double x, double y, double r,
-                              double startAngle, double endAngle,
-                              bool anticlockwise, ErrorResult& error)
-{
-  if (!FloatValidate(x, y, r, startAngle, endAngle)) {
-    return;
-  }
-
-  if (r < 0.0) {
-    error.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
-    return;
-  }
-
-  EnsureWritablePath();
-
-  ArcToBezier(this, Point(x, y), r, startAngle, endAngle, anticlockwise);
-}
-
-void
-CanvasRenderingContext2D::Rect(double x, double y, double w, double h)
-{
-  if (!FloatValidate(x, y, w, h)) {
-    return;
-  }
-
-  EnsureWritablePath();
-
-  if (mPathBuilder) {
-    mPathBuilder->MoveTo(Point(x, y));
-    mPathBuilder->LineTo(Point(x + w, y));
-    mPathBuilder->LineTo(Point(x + w, y + h));
-    mPathBuilder->LineTo(Point(x, y + h));
-    mPathBuilder->Close();
-  } else {
-    mDSPathBuilder->MoveTo(mTarget->GetTransform() * Point(x, y));
-    mDSPathBuilder->LineTo(mTarget->GetTransform() * Point(x + w, y));
-    mDSPathBuilder->LineTo(mTarget->GetTransform() * Point(x + w, y + h));
-    mDSPathBuilder->LineTo(mTarget->GetTransform() * Point(x, y + h));
-    mDSPathBuilder->Close();
-  }
-}
-
-void
-CanvasRenderingContext2D::EnsureWritablePath()
-{
-  if (mDSPathBuilder) {
-    return;
-  }
-
-  FillRule fillRule = CurrentState().fillRule;
-
-  if (mPathBuilder) {
-    if (mPathTransformWillUpdate) {
-      mPath = mPathBuilder->Finish();
-      mDSPathBuilder =
-        mPath->TransformedCopyToBuilder(mPathToDS, fillRule);
-      mPath = nullptr;
-      mPathBuilder = nullptr;
-      mPathTransformWillUpdate = false;
-    }
-    return;
-  }
-
-  EnsureTarget();
-  if (!mPath) {
-    NS_ASSERTION(!mPathTransformWillUpdate, "mPathTransformWillUpdate should be false, if all paths are null");
-    mPathBuilder = mTarget->CreatePathBuilder(fillRule);
-  } else if (!mPathTransformWillUpdate) {
-    mPathBuilder = mPath->CopyToBuilder(fillRule);
-  } else {
-    mDSPathBuilder =
-      mPath->TransformedCopyToBuilder(mPathToDS, fillRule);
-    mPathTransformWillUpdate = false;
-  }
-}
-
-void
-CanvasRenderingContext2D::EnsureUserSpacePath(bool aCommitTransform /* = true */)
-{
-  FillRule fillRule = CurrentState().fillRule;
-
-  if (!mPath && !mPathBuilder && !mDSPathBuilder) {
-    EnsureTarget();
-    mPathBuilder = mTarget->CreatePathBuilder(fillRule);
-  }
-
-  if (mPathBuilder) {
-    mPath = mPathBuilder->Finish();
-    mPathBuilder = nullptr;
-  }
-
-  if (aCommitTransform &&
-      mPath &&
-      mPathTransformWillUpdate) {
-    mDSPathBuilder =
-      mPath->TransformedCopyToBuilder(mPathToDS, fillRule);
-    mPath = nullptr;
-    mPathTransformWillUpdate = false;
-  }
-
-  if (mDSPathBuilder) {
-    RefPtr<Path> dsPath;
-    dsPath = mDSPathBuilder->Finish();
-    mDSPathBuilder = nullptr;
-
-    Matrix inverse = mTarget->GetTransform();
-    if (!inverse.Invert()) {
-      NS_WARNING("Could not invert transform");
-      return;
-    }
-
-    mPathBuilder =
-      dsPath->TransformedCopyToBuilder(inverse, fillRule);
-    mPath = mPathBuilder->Finish();
-    mPathBuilder = nullptr;
-  }
-
-  if (mPath && mPath->GetFillRule() != fillRule) {
-    mPathBuilder = mPath->CopyToBuilder(fillRule);
-    mPath = mPathBuilder->Finish();
-  }
-
-  NS_ASSERTION(mPath, "mPath should exist");
-}
-
-void
-CanvasRenderingContext2D::TransformWillUpdate()
-{
-  EnsureTarget();
-
-  // Store the matrix that would transform the current path to device
-  // space.
-  if (mPath || mPathBuilder) {
-    if (!mPathTransformWillUpdate) {
-      // If the transform has already been updated, but a device space builder
-      // has not been created yet mPathToDS contains the right transform to
-      // transform the current mPath into device space.
-      // We should leave it alone.
-      mPathToDS = mTarget->GetTransform();
-    }
-    mPathTransformWillUpdate = true;
-  }
-}
-
-//
-// text
-//
-
-/**
- * Helper function for SetFont that creates a style rule for the given font.
- * @param aFont The CSS font string
- * @param aNode The canvas element
- * @param aResult Pointer in which to place the new style rule.
- * @remark Assumes all pointer arguments are non-null.
- */
-static nsresult
-CreateFontStyleRule(const nsAString& aFont,
-                    nsINode* aNode,
-                    StyleRule** aResult)
-{
-  nsRefPtr<StyleRule> rule;
-  bool changed;
-
-  nsIPrincipal* principal = aNode->NodePrincipal();
-  nsIDocument* document = aNode->OwnerDoc();
-
-  nsIURI* docURL = document->GetDocumentURI();
-  nsIURI* baseURL = document->GetDocBaseURI();
-
-  // Pass the CSS Loader object to the parser, to allow parser error reports
-  // to include the outer window ID.
-  nsCSSParser parser(document->CSSLoader());
-
-  nsresult rv = parser.ParseStyleAttribute(EmptyString(), docURL, baseURL,
-                                           principal, getter_AddRefs(rule));
-  if (NS_FAILED(rv)) {
-    return rv;
-  }
-
-  rv = parser.ParseProperty(eCSSProperty_font, aFont, docURL, baseURL,
-                            principal, rule->GetDeclaration(), &changed,
-                            false);
-  if (NS_FAILED(rv))
-    return rv;
-
-  rv = parser.ParseProperty(eCSSProperty_line_height,
-                            NS_LITERAL_STRING("normal"), docURL, baseURL,
-                            principal, rule->GetDeclaration(), &changed,
-                            false);
-  if (NS_FAILED(rv)) {
-    return rv;
-  }
-
-  rule->RuleMatched();
-
-  rule.forget(aResult);
-  return NS_OK;
-}
-
-void
-CanvasRenderingContext2D::SetFont(const nsAString& font,
-                                  ErrorResult& error)
-{
-  /*
-    * If font is defined with relative units (e.g. ems) and the parent
-    * style context changes in between calls, setting the font to the
-    * same value as previous could result in a different computed value,
-    * so we cannot have the optimization where we check if the new font
-    * string is equal to the old one.
-    */
-
-  if (!mCanvasElement && !mDocShell) {
-    NS_WARNING("Canvas element must be non-null or a docshell must be provided");
-    error.Throw(NS_ERROR_FAILURE);
-    return;
-  }
-
-  nsIPresShell* presShell = GetPresShell();
-  if (!presShell) {
-    error.Throw(NS_ERROR_FAILURE);
-    return;
-  }
-  nsIDocument* document = presShell->GetDocument();
-
-  nsRefPtr<css::StyleRule> rule;
-  error = CreateFontStyleRule(font, document, getter_AddRefs(rule));
-
-  if (error.Failed()) {
-    return;
-  }
-
-  css::Declaration *declaration = rule->GetDeclaration();
-  // The easiest way to see whether we got a syntax error or whether
-  // we got 'inherit' or 'initial' is to look at font-size-adjust,
-  // which the shorthand resets to either 'none' or
-  // '-moz-system-font'.
-  // We know the declaration is not !important, so we can use
-  // GetNormalBlock().
-  const nsCSSValue *fsaVal =
-    declaration->GetNormalBlock()->ValueFor(eCSSProperty_font_size_adjust);
-  if (!fsaVal || (fsaVal->GetUnit() != eCSSUnit_None &&
-                  fsaVal->GetUnit() != eCSSUnit_System_Font)) {
-      // We got an all-property value or a syntax error.  The spec says
-      // this value must be ignored.
-    return;
-  }
-
-  nsTArray< nsCOMPtr<nsIStyleRule> > rules;
-  rules.AppendElement(rule);
-
-  nsStyleSet* styleSet = presShell->StyleSet();
-
-  // have to get a parent style context for inherit-like relative
-  // values (2em, bolder, etc.)
-  nsRefPtr<nsStyleContext> parentContext;
-
-  if (mCanvasElement && mCanvasElement->IsInDoc()) {
-      // inherit from the canvas element
-      parentContext = nsComputedDOMStyle::GetStyleContextForElement(
-              mCanvasElement,
-              nullptr,
-              presShell);
-  } else {
-    // otherwise inherit from default (10px sans-serif)
-    nsRefPtr<css::StyleRule> parentRule;
-    error = CreateFontStyleRule(NS_LITERAL_STRING("10px sans-serif"),
-                                document,
-                                getter_AddRefs(parentRule));
-
-    if (error.Failed()) {
-      return;
-    }
-
-    nsTArray< nsCOMPtr<nsIStyleRule> > parentRules;
-    parentRules.AppendElement(parentRule);
-    parentContext = styleSet->ResolveStyleForRules(nullptr, parentRules);
-  }
-
-  if (!parentContext) {
-    error.Throw(NS_ERROR_FAILURE);
-    return;
-  }
-
-  nsRefPtr<nsStyleContext> sc =
-      styleSet->ResolveStyleForRules(parentContext, rules);
-  if (!sc) {
-    error.Throw(NS_ERROR_FAILURE);
-    return;
-  }
-
-  const nsStyleFont* fontStyle = sc->GetStyleFont();
-
-  NS_ASSERTION(fontStyle, "Could not obtain font style");
-
-  nsIAtom* language = sc->GetStyleFont()->mLanguage;
-  if (!language) {
-    language = presShell->GetPresContext()->GetLanguageFromCharset();
-  }
-
-  // use CSS pixels instead of dev pixels to avoid being affected by page zoom
-  const uint32_t aupcp = nsPresContext::AppUnitsPerCSSPixel();
-  // un-zoom the font size to avoid being affected by text-only zoom
-  //
-  // Purposely ignore the font size that respects the user's minimum
-  // font preference (fontStyle->mFont.size) in favor of the computed
-  // size (fontStyle->mSize).  See
-  // https://bugzilla.mozilla.org/show_bug.cgi?id=698652.
-  const nscoord fontSize = nsStyleFont::UnZoomText(parentContext->PresContext(), fontStyle->mSize);
-
-  bool printerFont = (presShell->GetPresContext()->Type() == nsPresContext::eContext_PrintPreview ||
-                      presShell->GetPresContext()->Type() == nsPresContext::eContext_Print);
-
-  gfxFontStyle style(fontStyle->mFont.style,
-                     fontStyle->mFont.weight,
-                     fontStyle->mFont.stretch,
-                     NSAppUnitsToFloatPixels(fontSize, float(aupcp)),
-                     language,
-                     fontStyle->mFont.sizeAdjust,
-                     fontStyle->mFont.systemFont,
-                     printerFont,
-                     fontStyle->mFont.languageOverride);
-
-  fontStyle->mFont.AddFontFeaturesToStyle(&style);
-
-  CurrentState().fontGroup =
-      gfxPlatform::GetPlatform()->CreateFontGroup(fontStyle->mFont.name,
-                                                  &style,
-                                                  presShell->GetPresContext()->GetUserFontSet());
-  NS_ASSERTION(CurrentState().fontGroup, "Could not get font group");
-
-  // The font getter is required to be reserialized based on what we
-  // parsed (including having line-height removed).  (Older drafts of
-  // the spec required font sizes be converted to pixels, but that no
-  // longer seems to be required.)
-  declaration->GetValue(eCSSProperty_font, CurrentState().font);
-}
-
-void
-CanvasRenderingContext2D::SetTextAlign(const nsAString& ta)
-{
-  if (ta.EqualsLiteral("start"))
-    CurrentState().textAlign = TEXT_ALIGN_START;
-  else if (ta.EqualsLiteral("end"))
-    CurrentState().textAlign = TEXT_ALIGN_END;
-  else if (ta.EqualsLiteral("left"))
-    CurrentState().textAlign = TEXT_ALIGN_LEFT;
-  else if (ta.EqualsLiteral("right"))
-    CurrentState().textAlign = TEXT_ALIGN_RIGHT;
-  else if (ta.EqualsLiteral("center"))
-    CurrentState().textAlign = TEXT_ALIGN_CENTER;
-}
-
-void
-CanvasRenderingContext2D::GetTextAlign(nsAString& ta)
-{
-  switch (CurrentState().textAlign)
-  {
-  case TEXT_ALIGN_START:
-    ta.AssignLiteral("start");
-    break;
-  case TEXT_ALIGN_END:
-    ta.AssignLiteral("end");
-    break;
-  case TEXT_ALIGN_LEFT:
-    ta.AssignLiteral("left");
-    break;
-  case TEXT_ALIGN_RIGHT:
-    ta.AssignLiteral("right");
-    break;
-  case TEXT_ALIGN_CENTER:
-    ta.AssignLiteral("center");
-    break;
-  }
-}
-
-void
-CanvasRenderingContext2D::SetTextBaseline(const nsAString& tb)
-{
-  if (tb.EqualsLiteral("top"))
-    CurrentState().textBaseline = TEXT_BASELINE_TOP;
-  else if (tb.EqualsLiteral("hanging"))
-    CurrentState().textBaseline = TEXT_BASELINE_HANGING;
-  else if (tb.EqualsLiteral("middle"))
-    CurrentState().textBaseline = TEXT_BASELINE_MIDDLE;
-  else if (tb.EqualsLiteral("alphabetic"))
-    CurrentState().textBaseline = TEXT_BASELINE_ALPHABETIC;
-  else if (tb.EqualsLiteral("ideographic"))
-    CurrentState().textBaseline = TEXT_BASELINE_IDEOGRAPHIC;
-  else if (tb.EqualsLiteral("bottom"))
-    CurrentState().textBaseline = TEXT_BASELINE_BOTTOM;
-}
-
-void
-CanvasRenderingContext2D::GetTextBaseline(nsAString& tb)
-{
-  switch (CurrentState().textBaseline)
-  {
-  case TEXT_BASELINE_TOP:
-    tb.AssignLiteral("top");
-    break;
-  case TEXT_BASELINE_HANGING:
-    tb.AssignLiteral("hanging");
-    break;
-  case TEXT_BASELINE_MIDDLE:
-    tb.AssignLiteral("middle");
-    break;
-  case TEXT_BASELINE_ALPHABETIC:
-    tb.AssignLiteral("alphabetic");
-    break;
-  case TEXT_BASELINE_IDEOGRAPHIC:
-    tb.AssignLiteral("ideographic");
-    break;
-  case TEXT_BASELINE_BOTTOM:
-    tb.AssignLiteral("bottom");
-    break;
-  }
-}
-
-/*
- * Helper function that replaces the whitespace characters in a string
- * with U+0020 SPACE. The whitespace characters are defined as U+0020 SPACE,
- * U+0009 CHARACTER TABULATION (tab), U+000A LINE FEED (LF), U+000B LINE
- * TABULATION, U+000C FORM FEED (FF), and U+000D CARRIAGE RETURN (CR).
- * @param str The string whose whitespace characters to replace.
- */
-static inline void
-TextReplaceWhitespaceCharacters(nsAutoString& str)
-{
-  str.ReplaceChar("\x09\x0A\x0B\x0C\x0D", PRUnichar(' '));
-}
-
-void
-CanvasRenderingContext2D::FillText(const nsAString& text, double x,
-                                   double y,
-                                   const Optional<double>& maxWidth,
-                                   ErrorResult& error)
-{
-  error = DrawOrMeasureText(text, x, y, maxWidth, TEXT_DRAW_OPERATION_FILL, nullptr);
-}
-
-void
-CanvasRenderingContext2D::StrokeText(const nsAString& text, double x,
-                                     double y,
-                                     const Optional<double>& maxWidth,
-                                     ErrorResult& error)
-{
-  error = DrawOrMeasureText(text, x, y, maxWidth, TEXT_DRAW_OPERATION_STROKE, nullptr);
-}
-
-already_AddRefed<nsIDOMTextMetrics>
-CanvasRenderingContext2D::MeasureText(const nsAString& rawText,
-                                      ErrorResult& error)
-{
-  float width;
-  Optional<double> maxWidth;
-  error = DrawOrMeasureText(rawText, 0, 0, maxWidth, TEXT_DRAW_OPERATION_MEASURE, &width);
-  if (error.Failed()) {
-    return NULL;
-  }
-
-  nsRefPtr<nsIDOMTextMetrics> textMetrics = new TextMetrics(width);
-
-  return textMetrics.forget();
-}
-
-/**
- * Used for nsBidiPresUtils::ProcessText
- */
-struct NS_STACK_CLASS CanvasBidiProcessor : public nsBidiPresUtils::BidiProcessor
-{
-  typedef CanvasRenderingContext2D::ContextState ContextState;
-
-  virtual void SetText(const PRUnichar* text, int32_t length, nsBidiDirection direction)
-  {
-    mFontgrp->UpdateFontList(); // ensure user font generation is current
-    mTextRun = mFontgrp->MakeTextRun(text,
-                                     length,
-                                     mThebes,
-                                     mAppUnitsPerDevPixel,
-                                     direction==NSBIDI_RTL ? gfxTextRunFactory::TEXT_IS_RTL : 0);
-  }
-
-  virtual nscoord GetWidth()
-  {
-    gfxTextRun::Metrics textRunMetrics = mTextRun->MeasureText(0,
-                                                               mTextRun->GetLength(),
-                                                               mDoMeasureBoundingBox ?
-                                                                 gfxFont::TIGHT_INK_EXTENTS :
-                                                                 gfxFont::LOOSE_INK_EXTENTS,
-                                                               mThebes,
-                                                               nullptr);
-
-    // this only measures the height; the total width is gotten from the
-    // the return value of ProcessText.
-    if (mDoMeasureBoundingBox) {
-      textRunMetrics.mBoundingBox.Scale(1.0 / mAppUnitsPerDevPixel);
-      mBoundingBox = mBoundingBox.Union(textRunMetrics.mBoundingBox);
-    }
-
-    return NSToCoordRound(textRunMetrics.mAdvanceWidth);
-  }
-
-  virtual void DrawText(nscoord xOffset, nscoord width)
-  {
-    gfxPoint point = mPt;
-    point.x += xOffset;
-
-    // offset is given in terms of left side of string
-    if (mTextRun->IsRightToLeft()) {
-      // Bug 581092 - don't use rounded pixel width to advance to
-      // right-hand end of run, because this will cause different
-      // glyph positioning for LTR vs RTL drawing of the same
-      // glyph string on OS X and DWrite where textrun widths may
-      // involve fractional pixels.
-      gfxTextRun::Metrics textRunMetrics =
-        mTextRun->MeasureText(0,
-                              mTextRun->GetLength(),
-                              mDoMeasureBoundingBox ?
-                                  gfxFont::TIGHT_INK_EXTENTS :
-                                  gfxFont::LOOSE_INK_EXTENTS,
-                              mThebes,
-                              nullptr);
-      point.x += textRunMetrics.mAdvanceWidth;
-      // old code was:
-      //   point.x += width * mAppUnitsPerDevPixel;
-      // TODO: restore this if/when we move to fractional coords
-      // throughout the text layout process
-    }
-
-    uint32_t numRuns;
-    const gfxTextRun::GlyphRun *runs = mTextRun->GetGlyphRuns(&numRuns);
-    const uint32_t appUnitsPerDevUnit = mAppUnitsPerDevPixel;
-    const double devUnitsPerAppUnit = 1.0/double(appUnitsPerDevUnit);
-    Point baselineOrigin =
-      Point(point.x * devUnitsPerAppUnit, point.y * devUnitsPerAppUnit);
-
-    float advanceSum = 0;
-
-    mCtx->EnsureTarget();
-    for (uint32_t c = 0; c < numRuns; c++) {
-      gfxFont *font = runs[c].mFont;
-      uint32_t endRun = 0;
-      if (c + 1 < numRuns) {
-        endRun = runs[c + 1].mCharacterOffset;
-      } else {
-        endRun = mTextRun->GetLength();
-      }
-
-      const gfxTextRun::CompressedGlyph *glyphs = mTextRun->GetCharacterGlyphs();
-
-      RefPtr<ScaledFont> scaledFont =
-        gfxPlatform::GetPlatform()->GetScaledFontForFont(mCtx->mTarget, font);
-
-      if (!scaledFont) {
-        // This can occur when something switched DirectWrite off.
-        return;
-      }
-
-      GlyphBuffer buffer;
-
-      std::vector<Glyph> glyphBuf;
-
-      for (uint32_t i = runs[c].mCharacterOffset; i < endRun; i++) {
-        Glyph newGlyph;
-        if (glyphs[i].IsSimpleGlyph()) {
-          newGlyph.mIndex = glyphs[i].GetSimpleGlyph();
-          if (mTextRun->IsRightToLeft()) {
-            newGlyph.mPosition.x = baselineOrigin.x - advanceSum -
-              glyphs[i].GetSimpleAdvance() * devUnitsPerAppUnit;
-          } else {
-            newGlyph.mPosition.x = baselineOrigin.x + advanceSum;
-          }
-          newGlyph.mPosition.y = baselineOrigin.y;
-          advanceSum += glyphs[i].GetSimpleAdvance() * devUnitsPerAppUnit;
-          glyphBuf.push_back(newGlyph);
-          continue;
-        }
-
-        if (!glyphs[i].GetGlyphCount()) {
-          continue;
-        }
-
-        gfxTextRun::DetailedGlyph *detailedGlyphs =
-          mTextRun->GetDetailedGlyphs(i);
-
-        if (glyphs[i].IsMissing()) {
-          float xpos;
-          float advance = detailedGlyphs[0].mAdvance * devUnitsPerAppUnit;
-          if (mTextRun->IsRightToLeft()) {
-            xpos = baselineOrigin.x - advanceSum - advance;
-          } else {
-            xpos = baselineOrigin.x + advanceSum;
-          }
-          advanceSum += advance;
-
-          // default-ignorable characters will have zero advance width.
-          // we don't draw a hexbox for them, just leave them invisible
-          if (advance > 0) {
-            // for now, we use gfxFontMissingGlyphs to draw the hexbox;
-            // some day we should replace this with a direct Azure version
-
-            // get the DrawTarget's transform, so we can apply it to the
-            // thebes context for gfxFontMissingGlyphs
-            Matrix matrix = mCtx->mTarget->GetTransform();
-            nsRefPtr<gfxContext> thebes;
-            if (gfxPlatform::GetPlatform()->SupportsAzureContent()) {
-              // XXX See bug 808288 comment 5 - Bas says:
-              // This is a little tricky, potentially this could go wrong if
-              // we fell back to a Cairo context because of for example
-              // extremely large Canvas size. Cairo content is technically
-              // -not- supported, but SupportsAzureContent would return true
-              // as the browser uses D2D content.
-              // I'm thinking Cairo content will be good enough to do
-              // DrawMissingGlyph though.
-              thebes = new gfxContext(mCtx->mTarget);
-            } else {
-              nsRefPtr<gfxASurface> drawSurf;
-              mCtx->GetThebesSurface(getter_AddRefs(drawSurf));
-              thebes = new gfxContext(drawSurf);
-            }
-            thebes->SetMatrix(gfxMatrix(matrix._11, matrix._12, matrix._21,
-                                        matrix._22, matrix._31, matrix._32));
-
-            gfxFloat height = font->GetMetrics().maxAscent;
-            gfxRect glyphRect(xpos, baselineOrigin.y - height,
-                              advance, height);
-            gfxFontMissingGlyphs::DrawMissingGlyph(thebes, glyphRect,
-                                                   detailedGlyphs[0].mGlyphID);
-
-            mCtx->mTarget->SetTransform(matrix);
-          }
-          continue;
-        }
-
-        for (uint32_t c = 0; c < glyphs[i].GetGlyphCount(); c++) {
-          newGlyph.mIndex = detailedGlyphs[c].mGlyphID;
-          if (mTextRun->IsRightToLeft()) {
-            newGlyph.mPosition.x = baselineOrigin.x + detailedGlyphs[c].mXOffset * devUnitsPerAppUnit -
-              advanceSum - detailedGlyphs[c].mAdvance * devUnitsPerAppUnit;
-          } else {
-            newGlyph.mPosition.x = baselineOrigin.x + detailedGlyphs[c].mXOffset * devUnitsPerAppUnit + advanceSum;
-          }
-          newGlyph.mPosition.y = baselineOrigin.y + detailedGlyphs[c].mYOffset * devUnitsPerAppUnit;
-          glyphBuf.push_back(newGlyph);
-          advanceSum += detailedGlyphs[c].mAdvance * devUnitsPerAppUnit;
-        }
-      }
-
-      if (!glyphBuf.size()) {
-        // This may happen for glyph runs for a 0 size font.
-        continue;
-      }
-
-      buffer.mGlyphs = &glyphBuf.front();
-      buffer.mNumGlyphs = glyphBuf.size();
-
-      Rect bounds = mCtx->mTarget->GetTransform().
-        TransformBounds(Rect(mBoundingBox.x, mBoundingBox.y,
-                             mBoundingBox.width, mBoundingBox.height));
-      if (mOp == CanvasRenderingContext2D::TEXT_DRAW_OPERATION_FILL) {
-        AdjustedTarget(mCtx, &bounds)->
-          FillGlyphs(scaledFont, buffer,
-                     CanvasGeneralPattern().
-                       ForStyle(mCtx, CanvasRenderingContext2D::STYLE_FILL, mCtx->mTarget),
-                     DrawOptions(mState->globalAlpha, mCtx->UsedOperation()));
-      } else if (mOp == CanvasRenderingContext2D::TEXT_DRAW_OPERATION_STROKE) {
-        RefPtr<Path> path = scaledFont->GetPathForGlyphs(buffer, mCtx->mTarget);
-
-        const ContextState& state = *mState;
-        AdjustedTarget(mCtx, &bounds)->
-          Stroke(path, CanvasGeneralPattern().
-                   ForStyle(mCtx, CanvasRenderingContext2D::STYLE_STROKE, mCtx->mTarget),
-                 StrokeOptions(state.lineWidth, state.lineJoin,
-                               state.lineCap, state.miterLimit,
-                               state.dash.Length(),
-                               state.dash.Elements(),
-                               state.dashOffset),
-                 DrawOptions(state.globalAlpha, mCtx->UsedOperation()));
-
-      }
-    }
-  }
-
-  // current text run
-  nsAutoPtr<gfxTextRun> mTextRun;
-
-  // pointer to a screen reference context used to measure text and such
-  nsRefPtr<gfxContext> mThebes;
-
-  // Pointer to the draw target we should fill our text to
-  CanvasRenderingContext2D *mCtx;
-
-  // position of the left side of the string, alphabetic baseline
-  gfxPoint mPt;
-
-  // current font
-  gfxFontGroup* mFontgrp;
-
-  // dev pixel conversion factor
-  uint32_t mAppUnitsPerDevPixel;
-
-  // operation (fill or stroke)
-  CanvasRenderingContext2D::TextDrawOperation mOp;
-
-  // context state
-  ContextState *mState;
-
-  // union of bounding boxes of all runs, needed for shadows
-  gfxRect mBoundingBox;
-
-  // true iff the bounding box should be measured
-  bool mDoMeasureBoundingBox;
-};
-
-nsresult
-CanvasRenderingContext2D::DrawOrMeasureText(const nsAString& aRawText,
-                                            float aX,
-                                            float aY,
-                                            const Optional<double>& aMaxWidth,
-                                            TextDrawOperation aOp,
-                                            float* aWidth)
-{
-  nsresult rv;
-
-  if (!FloatValidate(aX, aY) ||
-      (aMaxWidth.WasPassed() && !FloatValidate(aMaxWidth.Value())))
-      return NS_ERROR_DOM_SYNTAX_ERR;
-
-  // spec isn't clear on what should happen if aMaxWidth <= 0, so
-  // treat it as an invalid argument
-  // technically, 0 should be an invalid value as well, but 0 is the default
-  // arg, and there is no way to tell if the default was used
-  if (aMaxWidth.WasPassed() && aMaxWidth.Value() < 0)
-    return NS_ERROR_INVALID_ARG;
-
-  if (!mCanvasElement && !mDocShell) {
-    NS_WARNING("Canvas element must be non-null or a docshell must be provided");
-    return NS_ERROR_FAILURE;
-  }
-
-  nsCOMPtr<nsIPresShell> presShell = GetPresShell();
-  if (!presShell)
-    return NS_ERROR_FAILURE;
-
-  nsIDocument* document = presShell->GetDocument();
-
-  // replace all the whitespace characters with U+0020 SPACE
-  nsAutoString textToDraw(aRawText);
-  TextReplaceWhitespaceCharacters(textToDraw);
-
-  // for now, default to ltr if not in doc
-  bool isRTL = false;
-
-  if (mCanvasElement && mCanvasElement->IsInDoc()) {
-    // try to find the closest context
-    nsRefPtr<nsStyleContext> canvasStyle =
-      nsComputedDOMStyle::GetStyleContextForElement(mCanvasElement,
-                                                    nullptr,
-                                                    presShell);
-    if (!canvasStyle) {
-      return NS_ERROR_FAILURE;
-    }
-
-    isRTL = canvasStyle->GetStyleVisibility()->mDirection ==
-      NS_STYLE_DIRECTION_RTL;
-  } else {
-    isRTL = GET_BIDI_OPTION_DIRECTION(document->GetBidiOptions()) == IBMBIDI_TEXTDIRECTION_RTL;
-  }
-
-  gfxFontGroup* currentFontStyle = GetCurrentFontStyle();
-  NS_ASSERTION(currentFontStyle, "font group is null");
-
-  if (currentFontStyle->GetStyle()->size == 0.0F) {
-    if (aWidth) {
-      *aWidth = 0;
-    }
-    return NS_OK;
-  }
-
-  const ContextState &state = CurrentState();
-
-  // This is only needed to know if we can know the drawing bounding box easily.
-  bool doDrawShadow = aOp == TEXT_DRAW_OPERATION_FILL && NeedToDrawShadow();
-
-  CanvasBidiProcessor processor;
-
-  GetAppUnitsValues(&processor.mAppUnitsPerDevPixel, nullptr);
-  processor.mPt = gfxPoint(aX, aY);
-  processor.mThebes =
-    new gfxContext(gfxPlatform::GetPlatform()->ScreenReferenceSurface());
-
-  // If we don't have a target then we don't have a transform. A target won't
-  // be needed in the case where we're measuring the text size. This allows
-  // to avoid creating a target if it's only being used to measure text sizes.
-  if (mTarget) {
-    Matrix matrix = mTarget->GetTransform();
-    processor.mThebes->SetMatrix(gfxMatrix(matrix._11, matrix._12, matrix._21, matrix._22, matrix._31, matrix._32));
-  }
-  processor.mCtx = this;
-  processor.mOp = aOp;
-  processor.mBoundingBox = gfxRect(0, 0, 0, 0);
-  processor.mDoMeasureBoundingBox = doDrawShadow || !mIsEntireFrameInvalid;
-  processor.mState = &CurrentState();
-  processor.mFontgrp = currentFontStyle;
-
-  nscoord totalWidthCoord;
-
-  // calls bidi algo twice since it needs the full text width and the
-  // bounding boxes before rendering anything
-  nsBidi bidiEngine;
-  rv = nsBidiPresUtils::ProcessText(textToDraw.get(),
-                                    textToDraw.Length(),
-                                    isRTL ? NSBIDI_RTL : NSBIDI_LTR,
-                                    presShell->GetPresContext(),
-                                    processor,
-                                    nsBidiPresUtils::MODE_MEASURE,
-                                    nullptr,
-                                    0,
-                                    &totalWidthCoord,
-                                    &bidiEngine);
-  if (NS_FAILED(rv)) {
-    return rv;
-  }
-
-  float totalWidth = float(totalWidthCoord) / processor.mAppUnitsPerDevPixel;
-  if (aWidth) {
-    *aWidth = totalWidth;
-  }
-
-  // if only measuring, don't need to do any more work
-  if (aOp==TEXT_DRAW_OPERATION_MEASURE) {
-    return NS_OK;
-  }
-
-  // offset pt.x based on text align
-  gfxFloat anchorX;
-
-  if (state.textAlign == TEXT_ALIGN_CENTER) {
-    anchorX = .5;
-  } else if (state.textAlign == TEXT_ALIGN_LEFT ||
-            (!isRTL && state.textAlign == TEXT_ALIGN_START) ||
-            (isRTL && state.textAlign == TEXT_ALIGN_END)) {
-    anchorX = 0;
-  } else {
-    anchorX = 1;
-  }
-
-  processor.mPt.x -= anchorX * totalWidth;
-
-  // offset pt.y based on text baseline
-  processor.mFontgrp->UpdateFontList(); // ensure user font generation is current
-  NS_ASSERTION(processor.mFontgrp->FontListLength()>0, "font group contains no fonts");
-  const gfxFont::Metrics& fontMetrics = processor.mFontgrp->GetFontAt(0)->GetMetrics();
-
-  gfxFloat anchorY;
-
-  switch (state.textBaseline)
-  {
-  case TEXT_BASELINE_HANGING:
-      // fall through; best we can do with the information available
-  case TEXT_BASELINE_TOP:
-    anchorY = fontMetrics.emAscent;
-    break;
-  case TEXT_BASELINE_MIDDLE:
-    anchorY = (fontMetrics.emAscent - fontMetrics.emDescent) * .5f;
-    break;
-  case TEXT_BASELINE_IDEOGRAPHIC:
-    // fall through; best we can do with the information available
-  case TEXT_BASELINE_ALPHABETIC:
-    anchorY = 0;
-    break;
-  case TEXT_BASELINE_BOTTOM:
-    anchorY = -fontMetrics.emDescent;
-    break;
-  default:
-      MOZ_NOT_REACHED("unexpected TextBaseline");
-  }
-
-  processor.mPt.y += anchorY;
-
-  // correct bounding box to get it to be the correct size/position
-  processor.mBoundingBox.width = totalWidth;
-  processor.mBoundingBox.MoveBy(processor.mPt);
-
-  processor.mPt.x *= processor.mAppUnitsPerDevPixel;
-  processor.mPt.y *= processor.mAppUnitsPerDevPixel;
-
-  EnsureTarget();
-  Matrix oldTransform = mTarget->GetTransform();
-  // if text is over aMaxWidth, then scale the text horizontally such that its
-  // width is precisely aMaxWidth
-  if (aMaxWidth.WasPassed() && aMaxWidth.Value() > 0 &&
-      totalWidth > aMaxWidth.Value()) {
-    Matrix newTransform = oldTransform;
-
-    // Translate so that the anchor point is at 0,0, then scale and then
-    // translate back.
-    newTransform.Translate(aX, 0);
-    newTransform.Scale(aMaxWidth.Value() / totalWidth, 1);
-    newTransform.Translate(-aX, 0);
-    /* we do this to avoid an ICE in the android compiler */
-    Matrix androidCompilerBug = newTransform;
-    mTarget->SetTransform(androidCompilerBug);
-  }
-
-  // save the previous bounding box
-  gfxRect boundingBox = processor.mBoundingBox;
-
-  // don't ever need to measure the bounding box twice
-  processor.mDoMeasureBoundingBox = false;
-
-  rv = nsBidiPresUtils::ProcessText(textToDraw.get(),
-                                    textToDraw.Length(),
-                                    isRTL ? NSBIDI_RTL : NSBIDI_LTR,
-                                    presShell->GetPresContext(),
-                                    processor,
-                                    nsBidiPresUtils::MODE_DRAW,
-                                    nullptr,
-                                    0,
-                                    nullptr,
-                                    &bidiEngine);
-
-
-  mTarget->SetTransform(oldTransform);
-
-  if (aOp == CanvasRenderingContext2D::TEXT_DRAW_OPERATION_FILL &&
-      !doDrawShadow) {
-    RedrawUser(boundingBox);
-    return NS_OK;
-  }
-
-  Redraw();
-  return NS_OK;
-}
-
-gfxFontGroup *CanvasRenderingContext2D::GetCurrentFontStyle()
-{
-  // use lazy initilization for the font group since it's rather expensive
-  if (!CurrentState().fontGroup) {
-    ErrorResult err;
-    SetFont(kDefaultFontStyle, err);
-    if (err.Failed()) {
-      gfxFontStyle style;
-      style.size = kDefaultFontSize;
-      CurrentState().fontGroup =
-        gfxPlatform::GetPlatform()->CreateFontGroup(kDefaultFontName,
-                                                    &style,
-                                                    nullptr);
-      if (CurrentState().fontGroup) {
-        CurrentState().font = kDefaultFontStyle;
-      } else {
-        NS_ERROR("Default canvas font is invalid");
-      }
-    }
-
-  }
-
-  return CurrentState().fontGroup;
-}
-
-//
-// line caps/joins
-//
-
-void
-CanvasRenderingContext2D::SetLineCap(const nsAString& capstyle)
-{
-  CapStyle cap;
-
-  if (capstyle.EqualsLiteral("butt")) {
-    cap = CAP_BUTT;
-  } else if (capstyle.EqualsLiteral("round")) {
-    cap = CAP_ROUND;
-  } else if (capstyle.EqualsLiteral("square")) {
-    cap = CAP_SQUARE;
-  } else {
-    // XXX ERRMSG we need to report an error to developers here! (bug 329026)
-    return;
-  }
-
-  CurrentState().lineCap = cap;
-}
-
-void
-CanvasRenderingContext2D::GetLineCap(nsAString& capstyle)
-{
-  switch (CurrentState().lineCap) {
-  case CAP_BUTT:
-    capstyle.AssignLiteral("butt");
-    break;
-  case CAP_ROUND:
-    capstyle.AssignLiteral("round");
-    break;
-  case CAP_SQUARE:
-    capstyle.AssignLiteral("square");
-    break;
-  }
-}
-
-void
-CanvasRenderingContext2D::SetLineJoin(const nsAString& joinstyle)
-{
-  JoinStyle j;
-
-  if (joinstyle.EqualsLiteral("round")) {
-    j = JOIN_ROUND;
-  } else if (joinstyle.EqualsLiteral("bevel")) {
-    j = JOIN_BEVEL;
-  } else if (joinstyle.EqualsLiteral("miter")) {
-    j = JOIN_MITER_OR_BEVEL;
-  } else {
-    // XXX ERRMSG we need to report an error to developers here! (bug 329026)
-    return;
-  }
-
-  CurrentState().lineJoin = j;
-}
-
-void
-CanvasRenderingContext2D::GetLineJoin(nsAString& joinstyle, ErrorResult& error)
-{
-  switch (CurrentState().lineJoin) {
-  case JOIN_ROUND:
-    joinstyle.AssignLiteral("round");
-    break;
-  case JOIN_BEVEL:
-    joinstyle.AssignLiteral("bevel");
-    break;
-  case JOIN_MITER_OR_BEVEL:
-    joinstyle.AssignLiteral("miter");
-    break;
-  default:
-    error.Throw(NS_ERROR_FAILURE);
-  }
-}
-
-void
-CanvasRenderingContext2D::SetMozDash(JSContext* cx,
-                                     const JS::Value& mozDash,
-                                     ErrorResult& error)
-{
-  FallibleTArray<Float> dash;
-  error = JSValToDashArray(cx, mozDash, dash);
-  if (!error.Failed()) {
-    ContextState& state = CurrentState();
-    state.dash = dash;
-    if (state.dash.IsEmpty()) {
-      state.dashOffset = 0;
-    }
-  }
-}
-
-JS::Value
-CanvasRenderingContext2D::GetMozDash(JSContext* cx, ErrorResult& error)
-{
-  JS::Value mozDash;
-  error = DashArrayToJSVal(CurrentState().dash, cx, &mozDash);
-  return mozDash;
-}
-
-void
-CanvasRenderingContext2D::SetMozDashOffset(double mozDashOffset)
-{
-  if (!FloatValidate(mozDashOffset)) {
-    return;
-  }
-
-  ContextState& state = CurrentState();
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * 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 "base/basictypes.h"
+#include "CanvasRenderingContext2D.h"
+
+#include "nsIDOMXULElement.h"
+
+#include "prenv.h"
+
+#include "nsIServiceManager.h"
+#include "nsMathUtils.h"
+
+#include "nsContentUtils.h"
+
+#include "nsIDocument.h"
+#include "nsHTMLCanvasElement.h"
+#include "nsSVGEffects.h"
+#include "nsPresContext.h"
+#include "nsIPresShell.h"
+#include "nsIVariant.h"
+
+#include "nsIInterfaceRequestorUtils.h"
+#include "nsIFrame.h"
+#include "nsError.h"
+#include "nsIScriptError.h"
+
+#include "nsCSSParser.h"
+#include "mozilla/css/StyleRule.h"
+#include "mozilla/css/Declaration.h"
+#include "nsComputedDOMStyle.h"
+#include "nsStyleSet.h"
+
+#include "nsPrintfCString.h"
+
+#include "nsReadableUtils.h"
+
+#include "nsColor.h"
+#include "nsGfxCIID.h"
+#include "nsIScriptSecurityManager.h"
+#include "nsIDocShell.h"
+#include "nsIDOMWindow.h"
+#include "nsPIDOMWindow.h"
+#include "nsIDocShellTreeItem.h"
+#include "nsIDocShellTreeNode.h"
+#include "nsIXPConnect.h"
+#include "nsDisplayList.h"
+
+#include "nsTArray.h"
+
+#include "imgIEncoder.h"
+
+#include "gfxContext.h"
+#include "gfxASurface.h"
+#include "gfxImageSurface.h"
+#include "gfxPlatform.h"
+#include "gfxFont.h"
+#include "gfxBlur.h"
+#include "gfxUtils.h"
+#include "gfxFontMissingGlyphs.h"
+
+#include "nsFrameManager.h"
+#include "nsFrameLoader.h"
+#include "nsBidi.h"
+#include "nsBidiPresUtils.h"
+#include "Layers.h"
+#include "CanvasUtils.h"
+#include "nsIMemoryReporter.h"
+#include "nsStyleUtil.h"
+#include "CanvasImageCache.h"
+
+#include <algorithm>
+
+#include "jsapi.h"
+#include "jsfriendapi.h"
+
+#include "mozilla/Assertions.h"
+#include "mozilla/CheckedInt.h"
+#include "mozilla/dom/ContentParent.h"
+#include "mozilla/dom/ImageData.h"
+#include "mozilla/dom/PBrowserParent.h"
+#include "mozilla/dom/TypedArray.h"
+#include "mozilla/gfx/2D.h"
+#include "mozilla/gfx/PathHelpers.h"
+#include "mozilla/ipc/DocumentRendererParent.h"
+#include "mozilla/ipc/PDocumentRendererParent.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/Telemetry.h"
+#include "mozilla/unused.h"
+#include "nsCCUncollectableMarker.h"
+#include "nsWrapperCacheInlines.h"
+#include "nsJSUtils.h"
+#include "XPCQuickStubs.h"
+#include "mozilla/dom/BindingUtils.h"
+#include "nsHTMLImageElement.h"
+#include "nsHTMLVideoElement.h"
+#include "mozilla/dom/CanvasRenderingContext2DBinding.h"
+
+#ifdef XP_WIN
+#include "gfxWindowsPlatform.h"
+#endif
+
+// windows.h (included by chromium code) defines this, in its infinite wisdom
+#undef DrawText
+
+using namespace mozilla;
+using namespace mozilla::CanvasUtils;
+using namespace mozilla::css;
+using namespace mozilla::gfx;
+using namespace mozilla::ipc;
+using namespace mozilla::layers;
+
+namespace mgfx = mozilla::gfx;
+
+#define NS_TEXTMETRICSAZURE_PRIVATE_IID \
+  {0x9793f9e7, 0x9dc1, 0x4e9c, {0x81, 0xc8, 0xfc, 0xa7, 0x14, 0xf4, 0x30, 0x79}}
+
+namespace mozilla {
+namespace dom {
+
+static float kDefaultFontSize = 10.0;
+static NS_NAMED_LITERAL_STRING(kDefaultFontName, "sans-serif");
+static NS_NAMED_LITERAL_STRING(kDefaultFontStyle, "10px sans-serif");
+
+// Cap sigma to avoid overly large temp surfaces.
+const Float SIGMA_MAX = 100;
+
+/* Memory reporter stuff */
+static nsIMemoryReporter *gCanvasAzureMemoryReporter = nullptr;
+static int64_t gCanvasAzureMemoryUsed = 0;
+
+static int64_t GetCanvasAzureMemoryUsed() {
+  return gCanvasAzureMemoryUsed;
+}
+
+// This is KIND_OTHER because it's not always clear where in memory the pixels
+// of a canvas are stored.  Furthermore, this memory will be tracked by the
+// underlying surface implementations.  See bug 655638 for details.
+NS_MEMORY_REPORTER_IMPLEMENT(CanvasAzureMemory,
+  "canvas-2d-pixel-bytes",
+  KIND_OTHER,
+  UNITS_BYTES,
+  GetCanvasAzureMemoryUsed,
+  "Memory used by 2D canvases. Each canvas requires (width * height * 4) "
+  "bytes.")
+
+class CanvasRadialGradient : public CanvasGradient
+{
+public:
+  CanvasRadialGradient(const Point &aBeginOrigin, Float aBeginRadius,
+                       const Point &aEndOrigin, Float aEndRadius)
+    : CanvasGradient(RADIAL)
+    , mCenter1(aBeginOrigin)
+    , mCenter2(aEndOrigin)
+    , mRadius1(aBeginRadius)
+    , mRadius2(aEndRadius)
+  {
+  }
+
+  Point mCenter1;
+  Point mCenter2;
+  Float mRadius1;
+  Float mRadius2;
+};
+
+class CanvasLinearGradient : public CanvasGradient
+{
+public:
+  CanvasLinearGradient(const Point &aBegin, const Point &aEnd)
+    : CanvasGradient(LINEAR)
+    , mBegin(aBegin)
+    , mEnd(aEnd)
+  {
+  }
+
+protected:
+  friend class CanvasGeneralPattern;
+
+  // Beginning of linear gradient.
+  Point mBegin;
+  // End of linear gradient.
+  Point mEnd;
+};
+
+// This class is named 'GeneralCanvasPattern' instead of just
+// 'GeneralPattern' to keep Windows PGO builds from confusing the
+// GeneralPattern class in gfxContext.cpp with this one.
+
+class CanvasGeneralPattern
+{
+public:
+  typedef CanvasRenderingContext2D::Style Style;
+  typedef CanvasRenderingContext2D::ContextState ContextState;
+
+  CanvasGeneralPattern() : mPattern(nullptr) {}
+  ~CanvasGeneralPattern()
+  {
+    if (mPattern) {
+      mPattern->~Pattern();
+    }
+  }
+
+  Pattern& ForStyle(CanvasRenderingContext2D *aCtx,
+                    Style aStyle,
+                    DrawTarget *aRT)
+  {
+    // This should only be called once or the mPattern destructor will
+    // not be executed.
+    NS_ASSERTION(!mPattern, "ForStyle() should only be called once on CanvasGeneralPattern!");
+
+    const ContextState &state = aCtx->CurrentState();
+
+    if (state.StyleIsColor(aStyle)) {
+      mPattern = new (mColorPattern.addr()) ColorPattern(Color::FromABGR(state.colorStyles[aStyle]));
+    } else if (state.gradientStyles[aStyle] &&
+               state.gradientStyles[aStyle]->GetType() == CanvasGradient::LINEAR) {
+      CanvasLinearGradient *gradient =
+        static_cast<CanvasLinearGradient*>(state.gradientStyles[aStyle].get());
+
+      mPattern = new (mLinearGradientPattern.addr())
+        LinearGradientPattern(gradient->mBegin, gradient->mEnd,
+                              gradient->GetGradientStopsForTarget(aRT));
+    } else if (state.gradientStyles[aStyle] &&
+               state.gradientStyles[aStyle]->GetType() == CanvasGradient::RADIAL) {
+      CanvasRadialGradient *gradient =
+        static_cast<CanvasRadialGradient*>(state.gradientStyles[aStyle].get());
+
+      mPattern = new (mRadialGradientPattern.addr())
+        RadialGradientPattern(gradient->mCenter1, gradient->mCenter2, gradient->mRadius1,
+                              gradient->mRadius2, gradient->GetGradientStopsForTarget(aRT));
+    } else if (state.patternStyles[aStyle]) {
+      if (aCtx->mCanvasElement) {
+        CanvasUtils::DoDrawImageSecurityCheck(aCtx->mCanvasElement,
+                                              state.patternStyles[aStyle]->mPrincipal,
+                                              state.patternStyles[aStyle]->mForceWriteOnly,
+                                              state.patternStyles[aStyle]->mCORSUsed);
+      }
+
+      ExtendMode mode;
+      if (state.patternStyles[aStyle]->mRepeat == CanvasPattern::NOREPEAT) {
+        mode = EXTEND_CLAMP;
+      } else {
+        mode = EXTEND_REPEAT;
+      }
+      mPattern = new (mSurfacePattern.addr())
+        SurfacePattern(state.patternStyles[aStyle]->mSurface, mode);
+    }
+
+    return *mPattern;
+  }
+
+  union {
+    AlignedStorage2<ColorPattern> mColorPattern;
+    AlignedStorage2<LinearGradientPattern> mLinearGradientPattern;
+    AlignedStorage2<RadialGradientPattern> mRadialGradientPattern;
+    AlignedStorage2<SurfacePattern> mSurfacePattern;
+  };
+  Pattern *mPattern;
+};
+
+/* This is an RAII based class that can be used as a drawtarget for
+ * operations that need a shadow drawn. It will automatically provide a
+ * temporary target when needed, and if so blend it back with a shadow.
+ *
+ * aBounds specifies the bounds of the drawing operation that will be
+ * drawn to the target, it is given in device space! This function will
+ * change aBounds to incorporate shadow bounds. If this is NULL the drawing
+ * operation will be assumed to cover an infinite rect.
+ */
+class AdjustedTarget
+{
+public:
+  typedef CanvasRenderingContext2D::ContextState ContextState;
+
+  AdjustedTarget(CanvasRenderingContext2D *ctx,
+                 mgfx::Rect *aBounds = nullptr)
+    : mCtx(nullptr)
+  {
+    if (!ctx->NeedToDrawShadow()) {
+      mTarget = ctx->mTarget;
+      return;
+    }
+    mCtx = ctx;
+
+    const ContextState &state = mCtx->CurrentState();
+
+    mSigma = state.shadowBlur / 2.0f;
+
+    if (mSigma > SIGMA_MAX) {
+      mSigma = SIGMA_MAX;
+    }
+
+    Matrix transform = mCtx->mTarget->GetTransform();
+
+    mTempRect = mgfx::Rect(0, 0, ctx->mWidth, ctx->mHeight);
+
+    static const gfxFloat GAUSSIAN_SCALE_FACTOR = (3 * sqrt(2 * M_PI) / 4) * 1.5;
+    int32_t blurRadius = (int32_t) floor(mSigma * GAUSSIAN_SCALE_FACTOR + 0.5);
+
+    // We need to enlarge and possibly offset our temporary surface
+    // so that things outside of the canvas may cast shadows.
+    mTempRect.Inflate(Margin(blurRadius + NS_MAX<Float>(state.shadowOffset.x, 0),
+                             blurRadius + NS_MAX<Float>(state.shadowOffset.y, 0),
+                             blurRadius + NS_MAX<Float>(-state.shadowOffset.x, 0),
+                             blurRadius + NS_MAX<Float>(-state.shadowOffset.y, 0)));
+
+    if (aBounds) {
+      // We actually include the bounds of the shadow blur, this makes it
+      // easier to execute the actual blur on hardware, and shouldn't affect
+      // the amount of pixels that need to be touched.
+      aBounds->Inflate(Margin(blurRadius, blurRadius,
+                              blurRadius, blurRadius));
+      mTempRect = mTempRect.Intersect(*aBounds);
+    }
+
+    mTempRect.ScaleRoundOut(1.0f);
+
+    transform._31 -= mTempRect.x;
+    transform._32 -= mTempRect.y;
+
+    mTarget =
+      mCtx->mTarget->CreateShadowDrawTarget(IntSize(int32_t(mTempRect.width), int32_t(mTempRect.height)),
+                                            FORMAT_B8G8R8A8, mSigma);
+
+    if (!mTarget) {
+      // XXX - Deal with the situation where our temp size is too big to
+      // fit in a texture.
+      mTarget = ctx->mTarget;
+      mCtx = nullptr;
+    } else {
+      mTarget->SetTransform(transform);
+    }
+  }
+
+  ~AdjustedTarget()
+  {
+    if (!mCtx) {
+      return;
+    }
+
+    RefPtr<SourceSurface> snapshot = mTarget->Snapshot();
+
+    mCtx->mTarget->DrawSurfaceWithShadow(snapshot, mTempRect.TopLeft(),
+                                         Color::FromABGR(mCtx->CurrentState().shadowColor),
+                                         mCtx->CurrentState().shadowOffset, mSigma,
+                                         mCtx->CurrentState().op);
+  }
+
+  DrawTarget* operator->()
+  {
+    return mTarget;
+  }
+
+private:
+  RefPtr<DrawTarget> mTarget;
+  CanvasRenderingContext2D *mCtx;
+  Float mSigma;
+  mgfx::Rect mTempRect;
+};
+
+NS_IMETHODIMP
+CanvasGradient::AddColorStop(float offset, const nsAString& colorstr)
+{
+  if (!FloatValidate(offset) || offset < 0.0 || offset > 1.0) {
+    return NS_ERROR_DOM_INDEX_SIZE_ERR;
+  }
+
+  nsCSSValue value;
+  nsCSSParser parser;
+  if (!parser.ParseColorString(colorstr, nullptr, 0, value)) {
+    return NS_ERROR_DOM_SYNTAX_ERR;
+  }
+
+  nscolor color;
+  if (!nsRuleNode::ComputeColor(value, nullptr, nullptr, color)) {
+    return NS_ERROR_DOM_SYNTAX_ERR;
+  }
+
+  mStops = nullptr;
+
+  GradientStop newStop;
+
+  newStop.offset = offset;
+  newStop.color = Color::FromABGR(color);
+
+  mRawStops.AppendElement(newStop);
+
+  return NS_OK;
+}
+
+NS_DEFINE_STATIC_IID_ACCESSOR(CanvasGradient, NS_CANVASGRADIENTAZURE_PRIVATE_IID)
+
+NS_IMPL_ADDREF(CanvasGradient)
+NS_IMPL_RELEASE(CanvasGradient)
+
+NS_INTERFACE_MAP_BEGIN(CanvasGradient)
+  NS_INTERFACE_MAP_ENTRY(mozilla::dom::CanvasGradient)
+  NS_INTERFACE_MAP_ENTRY(nsIDOMCanvasGradient)
+  NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(CanvasGradient)
+  NS_INTERFACE_MAP_ENTRY(nsISupports)
+NS_INTERFACE_MAP_END
+
+NS_DEFINE_STATIC_IID_ACCESSOR(CanvasPattern, NS_CANVASPATTERNAZURE_PRIVATE_IID)
+
+NS_IMPL_ADDREF(CanvasPattern)
+NS_IMPL_RELEASE(CanvasPattern)
+
+NS_INTERFACE_MAP_BEGIN(CanvasPattern)
+  NS_INTERFACE_MAP_ENTRY(mozilla::dom::CanvasPattern)
+  NS_INTERFACE_MAP_ENTRY(nsIDOMCanvasPattern)
+  NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(CanvasPattern)
+  NS_INTERFACE_MAP_ENTRY(nsISupports)
+NS_INTERFACE_MAP_END
+
+/**
+ ** TextMetrics
+ **/
+class TextMetrics : public nsIDOMTextMetrics
+{
+public:
+  TextMetrics(float w) : width(w) { }
+
+  virtual ~TextMetrics() { }
+
+  NS_DECLARE_STATIC_IID_ACCESSOR(NS_TEXTMETRICSAZURE_PRIVATE_IID)
+
+  NS_IMETHOD GetWidth(float* w)
+  {
+    *w = width;
+    return NS_OK;
+  }
+
+  NS_DECL_ISUPPORTS
+
+private:
+  float width;
+};
+
+NS_DEFINE_STATIC_IID_ACCESSOR(TextMetrics, NS_TEXTMETRICSAZURE_PRIVATE_IID)
+
+NS_IMPL_ADDREF(TextMetrics)
+NS_IMPL_RELEASE(TextMetrics)
+
+NS_INTERFACE_MAP_BEGIN(TextMetrics)
+  NS_INTERFACE_MAP_ENTRY(mozilla::dom::TextMetrics)
+  NS_INTERFACE_MAP_ENTRY(nsIDOMTextMetrics)
+  NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(TextMetrics)
+  NS_INTERFACE_MAP_ENTRY(nsISupports)
+NS_INTERFACE_MAP_END
+
+class CanvasRenderingContext2DUserData : public LayerUserData {
+public:
+    CanvasRenderingContext2DUserData(CanvasRenderingContext2D *aContext)
+    : mContext(aContext)
+  {
+    aContext->mUserDatas.AppendElement(this);
+  }
+  ~CanvasRenderingContext2DUserData()
+  {
+    if (mContext) {
+      mContext->mUserDatas.RemoveElement(this);
+    }
+  }
+  static void DidTransactionCallback(void* aData)
+  {
+    CanvasRenderingContext2DUserData* self =
+      static_cast<CanvasRenderingContext2DUserData*>(aData);
+    if (self->mContext) {
+      self->mContext->MarkContextClean();
+    }
+  }
+  bool IsForContext(CanvasRenderingContext2D *aContext)
+  {
+    return mContext == aContext;
+  }
+  void Forget()
+  {
+    mContext = nullptr;
+  }
+
+private:
+  CanvasRenderingContext2D *mContext;
+};
+
+NS_IMPL_CYCLE_COLLECTING_ADDREF(CanvasRenderingContext2D)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(CanvasRenderingContext2D)
+
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_1(CanvasRenderingContext2D, mCanvasElement)
+
+NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(CanvasRenderingContext2D)
+ if (nsCCUncollectableMarker::sGeneration && tmp->IsBlack()) {
+   dom::Element* canvasElement = tmp->mCanvasElement;
+    if (canvasElement) {
+      if (canvasElement->IsPurple()) {
+        canvasElement->RemovePurple();
+      }
+      dom::Element::MarkNodeChildren(canvasElement);
+    }
+    return true;
+  }
+NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END
+
+NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(CanvasRenderingContext2D)
+  return nsCCUncollectableMarker::sGeneration && tmp->IsBlack();
+NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END
+
+NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(CanvasRenderingContext2D)
+  return nsCCUncollectableMarker::sGeneration && tmp->IsBlack();
+NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(CanvasRenderingContext2D)
+  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
+  NS_INTERFACE_MAP_ENTRY(nsICanvasRenderingContextInternal)
+  NS_INTERFACE_MAP_ENTRY(nsISupports)
+NS_INTERFACE_MAP_END
+
+/**
+ ** CanvasRenderingContext2D impl
+ **/
+
+
+// Initialize our static variables.
+uint32_t CanvasRenderingContext2D::sNumLivingContexts = 0;
+uint8_t (*CanvasRenderingContext2D::sUnpremultiplyTable)[256] = nullptr;
+uint8_t (*CanvasRenderingContext2D::sPremultiplyTable)[256] = nullptr;
+DrawTarget* CanvasRenderingContext2D::sErrorTarget = nullptr;
+
+
+
+CanvasRenderingContext2D::CanvasRenderingContext2D()
+  : mZero(false), mOpaque(false), mResetLayer(true)
+  , mIPC(false)
+  , mIsEntireFrameInvalid(false)
+  , mPredictManyRedrawCalls(false), mPathTransformWillUpdate(false)
+  , mInvalidateCount(0)
+{
+  sNumLivingContexts++;
+  SetIsDOMBinding();
+}
+
+CanvasRenderingContext2D::~CanvasRenderingContext2D()
+{
+  Reset();
+  // Drop references from all CanvasRenderingContext2DUserData to this context
+  for (uint32_t i = 0; i < mUserDatas.Length(); ++i) {
+    mUserDatas[i]->Forget();
+  }
+  sNumLivingContexts--;
+  if (!sNumLivingContexts) {
+    delete[] sUnpremultiplyTable;
+    delete[] sPremultiplyTable;
+    sUnpremultiplyTable = nullptr;
+    sPremultiplyTable = nullptr;
+    NS_IF_RELEASE(sErrorTarget);
+  }
+}
+
+JSObject*
+CanvasRenderingContext2D::WrapObject(JSContext *cx, JSObject *scope,
+                                     bool *triedToWrap)
+{
+  return CanvasRenderingContext2DBinding::Wrap(cx, scope, this, triedToWrap);
+}
+
+bool
+CanvasRenderingContext2D::ParseColor(const nsAString& aString,
+                                     nscolor* aColor)
+{
+  nsIDocument* document = mCanvasElement
+                          ? mCanvasElement->OwnerDoc()
+                          : nullptr;
+
+  // Pass the CSS Loader object to the parser, to allow parser error
+  // reports to include the outer window ID.
+  nsCSSParser parser(document ? document->CSSLoader() : nullptr);
+  nsCSSValue value;
+  if (!parser.ParseColorString(aString, nullptr, 0, value)) {
+    return false;
+  }
+
+  if (value.GetUnit() == nsCSSUnit::eCSSUnit_Color) {
+    // if we already have a color we can just use it directly
+    *aColor = value.GetColorValue();
+  } else {
+    // otherwise resolve it
+    nsIPresShell* presShell = GetPresShell();
+    nsRefPtr<nsStyleContext> parentContext;
+    if (mCanvasElement && mCanvasElement->IsInDoc()) {
+      // Inherit from the canvas element.
+      parentContext = nsComputedDOMStyle::GetStyleContextForElement(
+        mCanvasElement, nullptr, presShell);
+    }
+
+    unused << nsRuleNode::ComputeColor(
+      value, presShell ? presShell->GetPresContext() : nullptr, parentContext,
+      *aColor);
+  }
+  return true;
+}
+
+nsresult
+CanvasRenderingContext2D::Reset()
+{
+  if (mCanvasElement) {
+    mCanvasElement->InvalidateCanvas();
+  }
+
+  // only do this for non-docshell created contexts,
+  // since those are the ones that we created a surface for
+  if (mTarget && IsTargetValid() && !mDocShell) {
+    gCanvasAzureMemoryUsed -= mWidth * mHeight * 4;
+  }
+
+  mTarget = nullptr;
+
+  // Since the target changes the backing texture will change, and this will
+  // no longer be valid.
+  mThebesSurface = nullptr;
+  mIsEntireFrameInvalid = false;
+  mPredictManyRedrawCalls = false;
+
+  return NS_OK;
+}
+
+static void
+WarnAboutUnexpectedStyle(nsHTMLCanvasElement* canvasElement)
+{
+  nsContentUtils::ReportToConsole(
+    nsIScriptError::warningFlag,
+    "Canvas",
+    canvasElement ? canvasElement->OwnerDoc() : nullptr,
+    nsContentUtils::eDOM_PROPERTIES,
+    "UnexpectedCanvasVariantStyle");
+}
+
+void
+CanvasRenderingContext2D::SetStyleFromString(const nsAString& str,
+                                             Style whichStyle)
+{
+  MOZ_ASSERT(!str.IsVoid());
+
+  nscolor color;
+  if (!ParseColor(str, &color)) {
+    return;
+  }
+
+  CurrentState().SetColorStyle(whichStyle, color);
+}
+
+nsISupports*
+CanvasRenderingContext2D::GetStyleAsStringOrInterface(nsAString& aStr,
+                                                      CanvasMultiGetterType& aType,
+                                                      Style aWhichStyle)
+{
+  const ContextState &state = CurrentState();
+  nsISupports* supports;
+  if (state.patternStyles[aWhichStyle]) {
+    aStr.SetIsVoid(true);
+    supports = state.patternStyles[aWhichStyle];
+    aType = CMG_STYLE_PATTERN;
+  } else if (state.gradientStyles[aWhichStyle]) {
+    aStr.SetIsVoid(true);
+    supports = state.gradientStyles[aWhichStyle];
+    aType = CMG_STYLE_GRADIENT;
+  } else {
+    StyleColorToString(state.colorStyles[aWhichStyle], aStr);
+    supports = nullptr;
+    aType = CMG_STYLE_STRING;
+  }
+  return supports;
+}
+
+// static
+void
+CanvasRenderingContext2D::StyleColorToString(const nscolor& aColor, nsAString& aStr)
+{
+  // We can't reuse the normal CSS color stringification code,
+  // because the spec calls for a different algorithm for canvas.
+  if (NS_GET_A(aColor) == 255) {
+    CopyUTF8toUTF16(nsPrintfCString("#%02x%02x%02x",
+                                    NS_GET_R(aColor),
+                                    NS_GET_G(aColor),
+                                    NS_GET_B(aColor)),
+                    aStr);
+  } else {
+    CopyUTF8toUTF16(nsPrintfCString("rgba(%d, %d, %d, ",
+                                    NS_GET_R(aColor),
+                                    NS_GET_G(aColor),
+                                    NS_GET_B(aColor)),
+                    aStr);
+    aStr.AppendFloat(nsStyleUtil::ColorComponentToFloat(NS_GET_A(aColor)));
+    aStr.Append(')');
+  }
+}
+
+nsresult
+CanvasRenderingContext2D::Redraw()
+{
+  if (mIsEntireFrameInvalid) {
+    return NS_OK;
+  }
+
+  mIsEntireFrameInvalid = true;
+
+  if (!mCanvasElement) {
+    NS_ASSERTION(mDocShell, "Redraw with no canvas element or docshell!");
+    return NS_OK;
+  }
+
+  if (!mThebesSurface)
+    mThebesSurface =
+      gfxPlatform::GetPlatform()->GetThebesSurfaceForDrawTarget(mTarget);
+  mThebesSurface->MarkDirty();
+
+  nsSVGEffects::InvalidateDirectRenderingObservers(mCanvasElement);
+
+  mCanvasElement->InvalidateCanvasContent(nullptr);
+
+  return NS_OK;
+}
+
+void
+CanvasRenderingContext2D::Redraw(const mgfx::Rect &r)
+{
+  ++mInvalidateCount;
+
+  if (mIsEntireFrameInvalid) {
+    return;
+  }
+
+  if (mPredictManyRedrawCalls ||
+    mInvalidateCount > kCanvasMaxInvalidateCount) {
+    Redraw();
+    return;
+  }
+
+  if (!mCanvasElement) {
+    NS_ASSERTION(mDocShell, "Redraw with no canvas element or docshell!");
+    return;
+  }
+
+  if (!mThebesSurface)
+    mThebesSurface =
+      gfxPlatform::GetPlatform()->GetThebesSurfaceForDrawTarget(mTarget);
+  mThebesSurface->MarkDirty();
+
+  nsSVGEffects::InvalidateDirectRenderingObservers(mCanvasElement);
+
+  mCanvasElement->InvalidateCanvasContent(&r);
+}
+
+void
+CanvasRenderingContext2D::RedrawUser(const gfxRect& r)
+{
+  if (mIsEntireFrameInvalid) {
+    ++mInvalidateCount;
+    return;
+  }
+
+  mgfx::Rect newr =
+    mTarget->GetTransform().TransformBounds(ToRect(r));
+  Redraw(newr);
+}
+
+void
+CanvasRenderingContext2D::EnsureTarget()
+{
+  if (mTarget) {
+    return;
+  }
+
+   // Check that the dimensions are sane
+  IntSize size(mWidth, mHeight);
+  if (size.width <= 0xFFFF && size.height <= 0xFFFF &&
+      size.width >= 0 && size.height >= 0) {
+    SurfaceFormat format = GetSurfaceFormat();
+    nsIDocument* ownerDoc = nullptr;
+    if (mCanvasElement) {
+      ownerDoc = mCanvasElement->OwnerDoc();
+    }
+
+    nsRefPtr<LayerManager> layerManager = nullptr;
+
+    if (ownerDoc) {
+      layerManager =
+        nsContentUtils::PersistentLayerManagerForDocument(ownerDoc);
+    }
+
+     if (layerManager) {
+       mTarget = layerManager->CreateDrawTarget(size, format);
+     } else {
+       mTarget = gfxPlatform::GetPlatform()->CreateOffscreenDrawTarget(size, format);
+     }
+  }
+
+  if (mTarget) {
+    if (gCanvasAzureMemoryReporter == nullptr) {
+        gCanvasAzureMemoryReporter = new NS_MEMORY_REPORTER_NAME(CanvasAzureMemory);
+      NS_RegisterMemoryReporter(gCanvasAzureMemoryReporter);
+    }
+
+    gCanvasAzureMemoryUsed += mWidth * mHeight * 4;
+    JSContext* context = nsContentUtils::GetCurrentJSContext();
+    if (context) {
+      JS_updateMallocCounter(context, mWidth * mHeight * 4);
+    }
+
+    mTarget->ClearRect(mgfx::Rect(Point(0, 0), Size(mWidth, mHeight)));
+    // Force a full layer transaction since we didn't have a layer before
+    // and now we might need one.
+    if (mCanvasElement) {
+      mCanvasElement->InvalidateCanvas();
+    }
+    // Calling Redraw() tells our invalidation machinery that the entire
+    // canvas is already invalid, which can speed up future drawing.
+    Redraw();
+  } else {
+    EnsureErrorTarget();
+    mTarget = sErrorTarget;
+  }
+}
+
+NS_IMETHODIMP
+CanvasRenderingContext2D::SetDimensions(int32_t width, int32_t height)
+{
+  ClearTarget();
+
+  // Zero sized surfaces cause issues, so just go with 1x1.
+  if (height == 0 || width == 0) {
+    mZero = true;
+    mWidth = 1;
+    mHeight = 1;
+  } else {
+    mZero = false;
+    mWidth = width;
+    mHeight = height;
+  }
+
+  return NS_OK;
+}
+
+void
+CanvasRenderingContext2D::ClearTarget()
+{
+  Reset();
+
+  mResetLayer = true;
+
+  // set up the initial canvas defaults
+  mStyleStack.Clear();
+  mPathBuilder = nullptr;
+  mPath = nullptr;
+  mDSPathBuilder = nullptr;
+
+  ContextState *state = mStyleStack.AppendElement();
+  state->globalAlpha = 1.0;
+
+  state->colorStyles[STYLE_FILL] = NS_RGB(0,0,0);
+  state->colorStyles[STYLE_STROKE] = NS_RGB(0,0,0);
+  state->shadowColor = NS_RGBA(0,0,0,0);
+}
+
+NS_IMETHODIMP
+CanvasRenderingContext2D::InitializeWithSurface(nsIDocShell *shell,
+                                                gfxASurface *surface,
+                                                int32_t width,
+                                                int32_t height)
+{
+  mDocShell = shell;
+  mThebesSurface = surface;
+
+  SetDimensions(width, height);
+  mTarget = gfxPlatform::GetPlatform()->
+    CreateDrawTargetForSurface(surface, IntSize(width, height));
+  if (!mTarget) {
+    EnsureErrorTarget();
+    mTarget = sErrorTarget;
+  }
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+CanvasRenderingContext2D::SetIsOpaque(bool isOpaque)
+{
+  if (isOpaque != mOpaque) {
+    mOpaque = isOpaque;
+    ClearTarget();
+  }
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+CanvasRenderingContext2D::SetIsIPC(bool isIPC)
+{
+  if (isIPC != mIPC) {
+    mIPC = isIPC;
+    ClearTarget();
+  }
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+CanvasRenderingContext2D::Render(gfxContext *ctx, gfxPattern::GraphicsFilter aFilter, uint32_t aFlags)
+{
+  nsresult rv = NS_OK;
+
+  EnsureTarget();
+  if (!IsTargetValid()) {
+    return NS_ERROR_FAILURE;
+  }
+
+  nsRefPtr<gfxASurface> surface;
+
+  if (NS_FAILED(GetThebesSurface(getter_AddRefs(surface)))) {
+    return NS_ERROR_FAILURE;
+  }
+
+  nsRefPtr<gfxPattern> pat = new gfxPattern(surface);
+
+  pat->SetFilter(aFilter);
+  pat->SetExtend(gfxPattern::EXTEND_PAD);
+
+  gfxContext::GraphicsOperator op = ctx->CurrentOperator();
+  if (mOpaque)
+      ctx->SetOperator(gfxContext::OPERATOR_SOURCE);
+
+  // XXX I don't want to use PixelSnapped here, but layout doesn't guarantee
+  // pixel alignment for this stuff!
+  ctx->NewPath();
+  ctx->PixelSnappedRectangleAndSetPattern(gfxRect(0, 0, mWidth, mHeight), pat);
+  ctx->Fill();
+
+  if (mOpaque)
+      ctx->SetOperator(op);
+
+  if (!(aFlags & RenderFlagPremultAlpha)) {
+      nsRefPtr<gfxASurface> curSurface = ctx->CurrentSurface();
+      nsRefPtr<gfxImageSurface> gis = curSurface->GetAsImageSurface();
+      NS_ABORT_IF_FALSE(gis, "If non-premult alpha, must be able to get image surface!");
+
+      gfxUtils::UnpremultiplyImageSurface(gis);
+  }
+
+  return rv;
+}
+
+NS_IMETHODIMP
+CanvasRenderingContext2D::GetInputStream(const char *aMimeType,
+                                         const PRUnichar *aEncoderOptions,
+                                         nsIInputStream **aStream)
+{
+  EnsureTarget();
+  if (!IsTargetValid()) {
+    return NS_ERROR_FAILURE;
+  }
+
+  nsRefPtr<gfxASurface> surface;
+
+  if (NS_FAILED(GetThebesSurface(getter_AddRefs(surface)))) {
+    return NS_ERROR_FAILURE;
+  }
+
+  nsresult rv;
+  const char encoderPrefix[] = "@mozilla.org/image/encoder;2?type=";
+  nsAutoArrayPtr<char> conid(new (std::nothrow) char[strlen(encoderPrefix) + strlen(aMimeType) + 1]);
+
+  if (!conid) {
+    return NS_ERROR_OUT_OF_MEMORY;
+  }
+
+  strcpy(conid, encoderPrefix);
+  strcat(conid, aMimeType);
+
+  nsCOMPtr<imgIEncoder> encoder = do_CreateInstance(conid);
+  if (!encoder) {
+    return NS_ERROR_FAILURE;
+  }
+
+  nsAutoArrayPtr<uint8_t> imageBuffer(new (std::nothrow) uint8_t[mWidth * mHeight * 4]);
+  if (!imageBuffer) {
+    return NS_ERROR_OUT_OF_MEMORY;
+  }
+
+  nsRefPtr<gfxImageSurface> imgsurf =
+    new gfxImageSurface(imageBuffer.get(),
+                        gfxIntSize(mWidth, mHeight),
+                        mWidth * 4,
+                        gfxASurface::ImageFormatARGB32);
+
+  if (!imgsurf || imgsurf->CairoStatus()) {
+    return NS_ERROR_FAILURE;
+  }
+
+  nsRefPtr<gfxContext> ctx = new gfxContext(imgsurf);
+
+  if (!ctx || ctx->HasError()) {
+    return NS_ERROR_FAILURE;
+  }
+
+  ctx->SetOperator(gfxContext::OPERATOR_SOURCE);
+  ctx->SetSource(surface, gfxPoint(0, 0));
+  ctx->Paint();
+
+  rv = encoder->InitFromData(imageBuffer.get(),
+                              mWidth * mHeight * 4, mWidth, mHeight, mWidth * 4,
+                              imgIEncoder::INPUT_FORMAT_HOSTARGB,
+                              nsDependentString(aEncoderOptions));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  return CallQueryInterface(encoder, aStream);
+}
+
+SurfaceFormat
+CanvasRenderingContext2D::GetSurfaceFormat() const
+{
+  return mOpaque ? FORMAT_B8G8R8X8 : FORMAT_B8G8R8A8;
+}
+
+//
+// state
+//
+
+void
+CanvasRenderingContext2D::Save()
+{
+  EnsureTarget();
+  mStyleStack[mStyleStack.Length() - 1].transform = mTarget->GetTransform();
+  mStyleStack.SetCapacity(mStyleStack.Length() + 1);
+  mStyleStack.AppendElement(CurrentState());
+}
+
+void
+CanvasRenderingContext2D::Restore()
+{
+  if (mStyleStack.Length() - 1 == 0)
+    return;
+
+  TransformWillUpdate();
+
+  for (uint32_t i = 0; i < CurrentState().clipsPushed.size(); i++) {
+    mTarget->PopClip();
+  }
+
+  mStyleStack.RemoveElementAt(mStyleStack.Length() - 1);
+
+  mTarget->SetTransform(CurrentState().transform);
+}
+
+//
+// transformations
+//
+
+void
+CanvasRenderingContext2D::Scale(double x, double y, ErrorResult& error)
+{
+  if (!FloatValidate(x,y)) {
+    return;
+  }
+
+  TransformWillUpdate();
+  if (!IsTargetValid()) {
+    error.Throw(NS_ERROR_FAILURE);
+    return;
+  }
+
+  Matrix newMatrix = mTarget->GetTransform();
+  mTarget->SetTransform(newMatrix.Scale(x, y));
+}
+
+void
+CanvasRenderingContext2D::Rotate(double angle, ErrorResult& error)
+{
+  if (!FloatValidate(angle)) {
+    return;
+  }
+
+  TransformWillUpdate();
+  if (!IsTargetValid()) {
+    error.Throw(NS_ERROR_FAILURE);
+    return;
+  }
+
+
+  Matrix rotation = Matrix::Rotation(angle);
+  mTarget->SetTransform(rotation * mTarget->GetTransform());
+}
+
+void
+CanvasRenderingContext2D::Translate(double x, double y, ErrorResult& error)
+{
+  if (!FloatValidate(x,y)) {
+    return;
+  }
+
+  TransformWillUpdate();
+  if (!IsTargetValid()) {
+    error.Throw(NS_ERROR_FAILURE);
+    return;
+  }
+
+  Matrix newMatrix = mTarget->GetTransform();
+  mTarget->SetTransform(newMatrix.Translate(x, y));
+}
+
+void
+CanvasRenderingContext2D::Transform(double m11, double m12, double m21,
+                                    double m22, double dx, double dy,
+                                    ErrorResult& error)
+{
+  if (!FloatValidate(m11,m12,m21,m22,dx,dy)) {
+    return;
+  }
+
+  TransformWillUpdate();
+  if (!IsTargetValid()) {
+    error.Throw(NS_ERROR_FAILURE);
+    return;
+  }
+
+  Matrix matrix(m11, m12, m21, m22, dx, dy);
+  mTarget->SetTransform(matrix * mTarget->GetTransform());
+}
+
+void
+CanvasRenderingContext2D::SetTransform(double m11, double m12,
+                                       double m21, double m22,
+                                       double dx, double dy,
+                                       ErrorResult& error)
+{
+  if (!FloatValidate(m11,m12,m21,m22,dx,dy)) {
+    return;
+  }
+
+  TransformWillUpdate();
+  if (!IsTargetValid()) {
+    error.Throw(NS_ERROR_FAILURE);
+    return;
+  }
+
+  Matrix matrix(m11, m12, m21, m22, dx, dy);
+  mTarget->SetTransform(matrix);
+}
+
+JSObject*
+MatrixToJSObject(JSContext* cx, const Matrix& matrix, ErrorResult& error)
+{
+  jsval elts[] = {
+    DOUBLE_TO_JSVAL(matrix._11), DOUBLE_TO_JSVAL(matrix._12),
+    DOUBLE_TO_JSVAL(matrix._21), DOUBLE_TO_JSVAL(matrix._22),
+    DOUBLE_TO_JSVAL(matrix._31), DOUBLE_TO_JSVAL(matrix._32)
+  };
+
+  // XXX Should we enter GetWrapper()'s compartment?
+  JSObject* obj = JS_NewArrayObject(cx, 6, elts);
+  if  (!obj) {
+    error.Throw(NS_ERROR_OUT_OF_MEMORY);
+  }
+  return obj;
+}
+
+bool
+ObjectToMatrix(JSContext* cx, JSObject& obj, Matrix& matrix, ErrorResult& error)
+{
+  uint32_t length;
+  if (!JS_GetArrayLength(cx, &obj, &length) || length != 6) {
+    // Not an array-like thing or wrong size
+    error.Throw(NS_ERROR_INVALID_ARG);
+    return false;
+  }
+
+  Float* elts[] = { &matrix._11, &matrix._12, &matrix._21, &matrix._22,
+                    &matrix._31, &matrix._32 };
+  for (uint32_t i = 0; i < 6; ++i) {
+    jsval elt;
+    double d;
+    if (!JS_GetElement(cx, &obj, i, &elt)) {
+      error.Throw(NS_ERROR_FAILURE);
+      return false;
+    }
+    if (!CoerceDouble(elt, &d)) {
+      error.Throw(NS_ERROR_INVALID_ARG);
+      return false;
+    }
+    if (!FloatValidate(d)) {
+      // This is weird, but it's the behavior of SetTransform()
+      return false;
+    }
+    *elts[i] = Float(d);
+  }
+  return true;
+}
+
+void
+CanvasRenderingContext2D::SetMozCurrentTransform(JSContext* cx,
+                                                 JSObject& currentTransform,
+                                                 ErrorResult& error)
+{
+  EnsureTarget();
+  if (!IsTargetValid()) {
+    error.Throw(NS_ERROR_FAILURE);
+    return;
+  }
+
+  Matrix newCTM;
+  if (ObjectToMatrix(cx, currentTransform, newCTM, error)) {
+    mTarget->SetTransform(newCTM);
+  }
+}
+
+JSObject*
+CanvasRenderingContext2D::GetMozCurrentTransform(JSContext* cx,
+                                                 ErrorResult& error) const
+{
+  return MatrixToJSObject(cx, mTarget ? mTarget->GetTransform() : Matrix(), error);
+}
+
+void
+CanvasRenderingContext2D::SetMozCurrentTransformInverse(JSContext* cx,
+                                                        JSObject& currentTransform,
+                                                        ErrorResult& error)
+{
+  EnsureTarget();
+  if (!IsTargetValid()) {
+    error.Throw(NS_ERROR_FAILURE);
+    return;
+  }
+
+  Matrix newCTMInverse;
+  if (ObjectToMatrix(cx, currentTransform, newCTMInverse, error)) {
+    // XXX ERRMSG we need to report an error to developers here! (bug 329026)
+    if (newCTMInverse.Invert()) {
+      mTarget->SetTransform(newCTMInverse);
+    }
+  }
+}
+
+JSObject*
+CanvasRenderingContext2D::GetMozCurrentTransformInverse(JSContext* cx,
+                                                        ErrorResult& error) const
+{
+  if (!mTarget) {
+    return MatrixToJSObject(cx, Matrix(), error);
+  }
+
+  Matrix ctm = mTarget->GetTransform();
+
+  if (!ctm.Invert()) {
+    double NaN = JSVAL_TO_DOUBLE(JS_GetNaNValue(cx));
+    ctm = Matrix(NaN, NaN, NaN, NaN, NaN, NaN);
+  }
+
+  return MatrixToJSObject(cx, ctm, error);
+}
+
+//
+// colors
+//
+
+void
+CanvasRenderingContext2D::SetStyleFromJSValue(JSContext* cx,
+                                              JS::Value& value,
+                                              Style whichStyle)
+{
+  if (value.isString()) {
+    nsDependentJSString strokeStyle;
+    if (strokeStyle.init(cx, value.toString())) {
+      SetStyleFromString(strokeStyle, whichStyle);
+    }
+    return;
+  }
+
+  if (value.isObject()) {
+    nsCOMPtr<nsISupports> holder;
+
+    CanvasGradient* gradient;
+    nsresult rv = xpc_qsUnwrapArg<CanvasGradient>(cx, value, &gradient,
+                                                  static_cast<nsISupports**>(getter_AddRefs(holder)),
+                                                  &value);
+    if (NS_SUCCEEDED(rv)) {
+      SetStyleFromGradient(gradient, whichStyle);
+      return;
+    }
+
+    CanvasPattern* pattern;
+    rv = xpc_qsUnwrapArg<CanvasPattern>(cx, value, &pattern,
+                                        static_cast<nsISupports**>(getter_AddRefs(holder)),
+                                        &value);
+    if (NS_SUCCEEDED(rv)) {
+      SetStyleFromPattern(pattern, whichStyle);
+      return;
+    }
+  }
+
+  WarnAboutUnexpectedStyle(mCanvasElement);
+}
+
+static JS::Value
+WrapStyle(JSContext* cx, JSObject* obj,
+          CanvasRenderingContext2D::CanvasMultiGetterType type,
+          nsAString& str, nsISupports* supports, ErrorResult& error)
+{
+  JS::Value v;
+  bool ok;
+  switch (type) {
+    case CanvasRenderingContext2D::CMG_STYLE_STRING:
+    {
+      ok = xpc::StringToJsval(cx, str, &v);
+      break;
+    }
+    case CanvasRenderingContext2D::CMG_STYLE_PATTERN:
+    case CanvasRenderingContext2D::CMG_STYLE_GRADIENT:
+    {
+      ok = dom::WrapObject(cx, obj, supports, &v);
+      break;
+    }
+    default:
+      MOZ_NOT_REACHED("unexpected CanvasMultiGetterType");
+  }
+  if (!ok) {
+    error.Throw(NS_ERROR_FAILURE);
+  }
+  return v;
+}
+
+
+JS::Value
+CanvasRenderingContext2D::GetStrokeStyle(JSContext* cx,
+                                         ErrorResult& error)
+{
+  nsString str;
+  CanvasMultiGetterType t;
+  nsISupports* supports = GetStyleAsStringOrInterface(str, t, STYLE_STROKE);
+  return WrapStyle(cx, GetWrapper(), t, str, supports, error);
+}
+
+JS::Value
+CanvasRenderingContext2D::GetFillStyle(JSContext* cx,
+                                       ErrorResult& error)
+{
+  nsString str;
+  CanvasMultiGetterType t;
+  nsISupports* supports = GetStyleAsStringOrInterface(str, t, STYLE_FILL);
+  return WrapStyle(cx, GetWrapper(), t, str, supports, error);
+}
+
+void
+CanvasRenderingContext2D::SetFillRule(const nsAString& aString)
+{
+  FillRule rule;
+
+  if (aString.EqualsLiteral("evenodd"))
+    rule = FILL_EVEN_ODD;
+  else if (aString.EqualsLiteral("nonzero"))
+    rule = FILL_WINDING;
+  else
+    return;
+
+  CurrentState().fillRule = rule;
+}
+
+void
+CanvasRenderingContext2D::GetFillRule(nsAString& aString)
+{
+  switch (CurrentState().fillRule) {
+  case FILL_WINDING:
+    aString.AssignLiteral("nonzero"); break;
+  case FILL_EVEN_ODD:
+    aString.AssignLiteral("evenodd"); break;
+  }
+}
+//
+// gradients and patterns
+//
+already_AddRefed<nsIDOMCanvasGradient>
+CanvasRenderingContext2D::CreateLinearGradient(double x0, double y0, double x1, double y1,
+                                               ErrorResult& aError)
+{
+  if (!FloatValidate(x0,y0,x1,y1)) {
+    aError.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
+    return nullptr;
+  }
+
+  nsRefPtr<nsIDOMCanvasGradient> grad =
+    new CanvasLinearGradient(Point(x0, y0), Point(x1, y1));
+
+  return grad.forget();
+}
+
+already_AddRefed<nsIDOMCanvasGradient>
+CanvasRenderingContext2D::CreateRadialGradient(double x0, double y0, double r0,
+                                               double x1, double y1, double r1,
+                                               ErrorResult& aError)
+{
+  if (!FloatValidate(x0,y0,r0,x1,y1,r1)) {
+    aError.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
+    return nullptr;
+  }
+
+  if (r0 < 0.0 || r1 < 0.0) {
+    aError.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
+    return nullptr;
+  }
+
+  nsRefPtr<nsIDOMCanvasGradient> grad =
+    new CanvasRadialGradient(Point(x0, y0), r0, Point(x1, y1), r1);
+
+  return grad.forget();
+}
+
+already_AddRefed<nsIDOMCanvasPattern>
+CanvasRenderingContext2D::CreatePattern(const HTMLImageOrCanvasOrVideoElement& element,
+                                        const nsAString& repeat,
+                                        ErrorResult& error)
+{
+  CanvasPattern::RepeatMode repeatMode =
+    CanvasPattern::NOREPEAT;
+
+  if (repeat.IsEmpty() || repeat.EqualsLiteral("repeat")) {
+    repeatMode = CanvasPattern::REPEAT;
+  } else if (repeat.EqualsLiteral("repeat-x")) {
+    repeatMode = CanvasPattern::REPEATX;
+  } else if (repeat.EqualsLiteral("repeat-y")) {
+    repeatMode = CanvasPattern::REPEATY;
+  } else if (repeat.EqualsLiteral("no-repeat")) {
+    repeatMode = CanvasPattern::NOREPEAT;
+  } else {
+    error.Throw(NS_ERROR_DOM_SYNTAX_ERR);
+    return NULL;
+  }
+
+  Element* htmlElement;
+  if (element.IsHTMLCanvasElement()) {
+    nsHTMLCanvasElement* canvas = element.GetAsHTMLCanvasElement();
+    htmlElement = canvas;
+
+    nsIntSize size = canvas->GetSize();
+    if (size.width == 0 || size.height == 0) {
+      error.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
+      return NULL;
+    }
+
+    // Special case for Canvas, which could be an Azure canvas!
+    nsICanvasRenderingContextInternal *srcCanvas = canvas->GetContextAtIndex(0);
+    if (srcCanvas) {
+      // This might not be an Azure canvas!
+      RefPtr<SourceSurface> srcSurf = srcCanvas->GetSurfaceSnapshot();
+
+      nsRefPtr<CanvasPattern> pat =
+        new CanvasPattern(srcSurf, repeatMode, htmlElement->NodePrincipal(), canvas->IsWriteOnly(), false);
+
+      return pat.forget();
+    }
+  } else if (element.IsHTMLImageElement()) {
+    htmlElement = element.GetAsHTMLImageElement();
+  } else {
+    htmlElement = element.GetAsHTMLVideoElement();
+  }
+
+  // The canvas spec says that createPattern should use the first frame
+  // of animated images
+  nsLayoutUtils::SurfaceFromElementResult res =
+    nsLayoutUtils::SurfaceFromElement(htmlElement,
+      nsLayoutUtils::SFE_WANT_FIRST_FRAME | nsLayoutUtils::SFE_WANT_NEW_SURFACE);
+
+  if (!res.mSurface) {
+    error.Throw(NS_ERROR_NOT_AVAILABLE);
+    return NULL;
+  }
+
+  // Ignore nullptr cairo surfaces! See bug 666312.
+  if (!res.mSurface->CairoSurface() || res.mSurface->CairoStatus()) {
+    return NULL;
+  }
+
+  EnsureTarget();
+  RefPtr<SourceSurface> srcSurf =
+    gfxPlatform::GetPlatform()->GetSourceSurfaceForSurface(mTarget, res.mSurface);
+
+  nsRefPtr<CanvasPattern> pat =
+    new CanvasPattern(srcSurf, repeatMode, res.mPrincipal,
+                             res.mIsWriteOnly, res.mCORSUsed);
+
+  return pat.forget();
+}
+
+//
+// shadows
+//
+void
+CanvasRenderingContext2D::SetShadowColor(const nsAString& shadowColor)
+{
+  nscolor color;
+  if (!ParseColor(shadowColor, &color)) {
+    return;
+  }
+
+  CurrentState().shadowColor = color;
+}
+
+//
+// rects
+//
+
+void
+CanvasRenderingContext2D::ClearRect(double x, double y, double w,
+                                    double h)
+{
+  if (!FloatValidate(x,y,w,h) || !mTarget) {
+    return;
+  }
+
+  mTarget->ClearRect(mgfx::Rect(x, y, w, h));
+
+  RedrawUser(gfxRect(x, y, w, h));
+}
+
+void
+CanvasRenderingContext2D::FillRect(double x, double y, double w,
+                                   double h)
+{
+  if (!FloatValidate(x,y,w,h)) {
+    return;
+  }
+
+  const ContextState &state = CurrentState();
+
+  if (state.patternStyles[STYLE_FILL]) {
+    CanvasPattern::RepeatMode repeat =
+      state.patternStyles[STYLE_FILL]->mRepeat;
+    // In the FillRect case repeat modes are easy to deal with.
+    bool limitx = repeat == CanvasPattern::NOREPEAT || repeat == CanvasPattern::REPEATY;
+    bool limity = repeat == CanvasPattern::NOREPEAT || repeat == CanvasPattern::REPEATX;
+
+    IntSize patternSize =
+      state.patternStyles[STYLE_FILL]->mSurface->GetSize();
+
+    // We always need to execute painting for non-over operators, even if
+    // we end up with w/h = 0.
+    if (limitx) {
+      if (x < 0) {
+        w += x;
+        if (w < 0) {
+          w = 0;
+        }
+
+        x = 0;
+      }
+      if (x + w > patternSize.width) {
+        w = patternSize.width - x;
+        if (w < 0) {
+          w = 0;
+        }
+      }
+    }
+    if (limity) {
+      if (y < 0) {
+        h += y;
+        if (h < 0) {
+          h = 0;
+        }
+
+        y = 0;
+      }
+      if (y + h > patternSize.height) {
+        h = patternSize.height - y;
+        if (h < 0) {
+          h = 0;
+        }
+      }
+    }
+  }
+
+  mgfx::Rect bounds;
+
+  EnsureTarget();
+  if (NeedToDrawShadow()) {
+    bounds = mgfx::Rect(x, y, w, h);
+    bounds = mTarget->GetTransform().TransformBounds(bounds);
+  }
+
+  AdjustedTarget(this, bounds.IsEmpty() ? nullptr : &bounds)->
+    FillRect(mgfx::Rect(x, y, w, h),
+             CanvasGeneralPattern().ForStyle(this, STYLE_FILL, mTarget),
+             DrawOptions(state.globalAlpha, UsedOperation()));
+
+  RedrawUser(gfxRect(x, y, w, h));
+}
+
+void
+CanvasRenderingContext2D::StrokeRect(double x, double y, double w,
+                                     double h)
+{
+  if (!FloatValidate(x,y,w,h)) {
+    return;
+  }
+
+  const ContextState &state = CurrentState();
+
+  mgfx::Rect bounds;
+
+  if (!w && !h) {
+    return;
+  }
+
+  EnsureTarget();
+  if (!IsTargetValid()) {
+    return;
+  }
+
+  if (NeedToDrawShadow()) {
+    bounds = mgfx::Rect(x - state.lineWidth / 2.0f, y - state.lineWidth / 2.0f,
+                        w + state.lineWidth, h + state.lineWidth);
+    bounds = mTarget->GetTransform().TransformBounds(bounds);
+  }
+
+  if (!h) {
+    CapStyle cap = CAP_BUTT;
+    if (state.lineJoin == JOIN_ROUND) {
+      cap = CAP_ROUND;
+    }
+    AdjustedTarget(this, bounds.IsEmpty() ? nullptr : &bounds)->
+      StrokeLine(Point(x, y), Point(x + w, y),
+                  CanvasGeneralPattern().ForStyle(this, STYLE_STROKE, mTarget),
+                  StrokeOptions(state.lineWidth, state.lineJoin,
+                                cap, state.miterLimit,
+                                state.dash.Length(),
+                                state.dash.Elements(),
+                                state.dashOffset),
+                  DrawOptions(state.globalAlpha, UsedOperation()));
+    return;
+  }
+
+  if (!w) {
+    CapStyle cap = CAP_BUTT;
+    if (state.lineJoin == JOIN_ROUND) {
+      cap = CAP_ROUND;
+    }
+    AdjustedTarget(this, bounds.IsEmpty() ? nullptr : &bounds)->
+      StrokeLine(Point(x, y), Point(x, y + h),
+                  CanvasGeneralPattern().ForStyle(this, STYLE_STROKE, mTarget),
+                  StrokeOptions(state.lineWidth, state.lineJoin,
+                                cap, state.miterLimit,
+                                state.dash.Length(),
+                                state.dash.Elements(),
+                                state.dashOffset),
+                  DrawOptions(state.globalAlpha, UsedOperation()));
+    return;
+  }
+
+  AdjustedTarget(this, bounds.IsEmpty() ? nullptr : &bounds)->
+    StrokeRect(mgfx::Rect(x, y, w, h),
+                CanvasGeneralPattern().ForStyle(this, STYLE_STROKE, mTarget),
+                StrokeOptions(state.lineWidth, state.lineJoin,
+                              state.lineCap, state.miterLimit,
+                              state.dash.Length(),
+                              state.dash.Elements(),
+                              state.dashOffset),
+                DrawOptions(state.globalAlpha, UsedOperation()));
+
+  Redraw();
+}
+
+//
+// path bits
+//
+
+void
+CanvasRenderingContext2D::BeginPath()
+{
+  mPath = nullptr;
+  mPathBuilder = nullptr;
+  mDSPathBuilder = nullptr;
+  mPathTransformWillUpdate = false;
+}
+
+void
+CanvasRenderingContext2D::Fill()
+{
+  EnsureUserSpacePath();
+
+  if (!mPath) {
+    return;
+  }
+
+  mgfx::Rect bounds;
+
+  if (NeedToDrawShadow()) {
+    bounds = mPath->GetBounds(mTarget->GetTransform());
+  }
+
+  AdjustedTarget(this, bounds.IsEmpty() ? nullptr : &bounds)->
+    Fill(mPath, CanvasGeneralPattern().ForStyle(this, STYLE_FILL, mTarget),
+         DrawOptions(CurrentState().globalAlpha, UsedOperation()));
+
+  Redraw();
+}
+
+void
+CanvasRenderingContext2D::Stroke()
+{
+  EnsureUserSpacePath();
+
+  if (!mPath) {
+    return;
+  }
+
+  const ContextState &state = CurrentState();
+
+  StrokeOptions strokeOptions(state.lineWidth, state.lineJoin,
+                              state.lineCap, state.miterLimit,
+                              state.dash.Length(), state.dash.Elements(),
+                              state.dashOffset);
+
+  mgfx::Rect bounds;
+  if (NeedToDrawShadow()) {
+    bounds =
+      mPath->GetStrokedBounds(strokeOptions, mTarget->GetTransform());
+  }
+
+  AdjustedTarget(this, bounds.IsEmpty() ? nullptr : &bounds)->
+    Stroke(mPath, CanvasGeneralPattern().ForStyle(this, STYLE_STROKE, mTarget),
+           strokeOptions, DrawOptions(state.globalAlpha, UsedOperation()));
+
+  Redraw();
+}
+
+void
+CanvasRenderingContext2D::Clip()
+{
+  EnsureUserSpacePath();
+
+  if (!mPath) {
+    return;
+  }
+
+  mTarget->PushClip(mPath);
+  CurrentState().clipsPushed.push_back(mPath);
+}
+
+void
+CanvasRenderingContext2D::ArcTo(double x1, double y1, double x2,
+                                double y2, double radius,
+                                ErrorResult& error)
+{
+  if (!FloatValidate(x1, y1, x2, y2, radius)) {
+    return;
+  }
+
+  if (radius < 0) {
+    error.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
+    return;
+  }
+
+  EnsureWritablePath();
+
+  // Current point in user space!
+  Point p0;
+  if (mPathBuilder) {
+    p0 = mPathBuilder->CurrentPoint();
+  } else {
+    Matrix invTransform = mTarget->GetTransform();
+    if (!invTransform.Invert()) {
+      return;
+    }
+
+    p0 = invTransform * mDSPathBuilder->CurrentPoint();
+  }
+
+  Point p1(x1, y1);
+  Point p2(x2, y2);
+
+  // Execute these calculations in double precision to avoid cumulative
+  // rounding errors.
+  double dir, a2, b2, c2, cosx, sinx, d, anx, any,
+         bnx, bny, x3, y3, x4, y4, cx, cy, angle0, angle1;
+  bool anticlockwise;
+
+  if (p0 == p1 || p1 == p2 || radius == 0) {
+    LineTo(p1.x, p1.y);
+    return;
+  }
+
+  // Check for colinearity
+  dir = (p2.x - p1.x) * (p0.y - p1.y) + (p2.y - p1.y) * (p1.x - p0.x);
+  if (dir == 0) {
+    LineTo(p1.x, p1.y);
+    return;
+  }
+
+
+  // XXX - Math for this code was already available from the non-azure code
+  // and would be well tested. Perhaps converting to bezier directly might
+  // be more efficient longer run.
+  a2 = (p0.x-x1)*(p0.x-x1) + (p0.y-y1)*(p0.y-y1);
+  b2 = (x1-x2)*(x1-x2) + (y1-y2)*(y1-y2);
+  c2 = (p0.x-x2)*(p0.x-x2) + (p0.y-y2)*(p0.y-y2);
+  cosx = (a2+b2-c2)/(2*sqrt(a2*b2));
+
+  sinx = sqrt(1 - cosx*cosx);
+  d = radius / ((1 - cosx) / sinx);
+
+  anx = (x1-p0.x) / sqrt(a2);
+  any = (y1-p0.y) / sqrt(a2);
+  bnx = (x1-x2) / sqrt(b2);
+  bny = (y1-y2) / sqrt(b2);
+  x3 = x1 - anx*d;
+  y3 = y1 - any*d;
+  x4 = x1 - bnx*d;
+  y4 = y1 - bny*d;
+  anticlockwise = (dir < 0);
+  cx = x3 + any*radius*(anticlockwise ? 1 : -1);
+  cy = y3 - anx*radius*(anticlockwise ? 1 : -1);
+  angle0 = atan2((y3-cy), (x3-cx));
+  angle1 = atan2((y4-cy), (x4-cx));
+
+
+  LineTo(x3, y3);
+
+  Arc(cx, cy, radius, angle0, angle1, anticlockwise, error);
+}
+
+void
+CanvasRenderingContext2D::Arc(double x, double y, double r,
+                              double startAngle, double endAngle,
+                              bool anticlockwise, ErrorResult& error)
+{
+  if (!FloatValidate(x, y, r, startAngle, endAngle)) {
+    return;
+  }
+
+  if (r < 0.0) {
+    error.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
+    return;
+  }
+
+  EnsureWritablePath();
+
+  ArcToBezier(this, Point(x, y), r, startAngle, endAngle, anticlockwise);
+}
+
+void
+CanvasRenderingContext2D::Rect(double x, double y, double w, double h)
+{
+  if (!FloatValidate(x, y, w, h)) {
+    return;
+  }
+
+  EnsureWritablePath();
+
+  if (mPathBuilder) {
+    mPathBuilder->MoveTo(Point(x, y));
+    mPathBuilder->LineTo(Point(x + w, y));
+    mPathBuilder->LineTo(Point(x + w, y + h));
+    mPathBuilder->LineTo(Point(x, y + h));
+    mPathBuilder->Close();
+  } else {
+    mDSPathBuilder->MoveTo(mTarget->GetTransform() * Point(x, y));
+    mDSPathBuilder->LineTo(mTarget->GetTransform() * Point(x + w, y));
+    mDSPathBuilder->LineTo(mTarget->GetTransform() * Point(x + w, y + h));
+    mDSPathBuilder->LineTo(mTarget->GetTransform() * Point(x, y + h));
+    mDSPathBuilder->Close();
+  }
+}
+
+void
+CanvasRenderingContext2D::EnsureWritablePath()
+{
+  if (mDSPathBuilder) {
+    return;
+  }
+
+  FillRule fillRule = CurrentState().fillRule;
+
+  if (mPathBuilder) {
+    if (mPathTransformWillUpdate) {
+      mPath = mPathBuilder->Finish();
+      mDSPathBuilder =
+        mPath->TransformedCopyToBuilder(mPathToDS, fillRule);
+      mPath = nullptr;
+      mPathBuilder = nullptr;
+      mPathTransformWillUpdate = false;
+    }
+    return;
+  }
+
+  EnsureTarget();
+  if (!mPath) {
+    NS_ASSERTION(!mPathTransformWillUpdate, "mPathTransformWillUpdate should be false, if all paths are null");
+    mPathBuilder = mTarget->CreatePathBuilder(fillRule);
+  } else if (!mPathTransformWillUpdate) {
+    mPathBuilder = mPath->CopyToBuilder(fillRule);
+  } else {
+    mDSPathBuilder =
+      mPath->TransformedCopyToBuilder(mPathToDS, fillRule);
+    mPathTransformWillUpdate = false;
+  }
+}
+
+void
+CanvasRenderingContext2D::EnsureUserSpacePath(bool aCommitTransform /* = true */)
+{
+  FillRule fillRule = CurrentState().fillRule;
+
+  if (!mPath && !mPathBuilder && !mDSPathBuilder) {
+    EnsureTarget();
+    mPathBuilder = mTarget->CreatePathBuilder(fillRule);
+  }
+
+  if (mPathBuilder) {
+    mPath = mPathBuilder->Finish();
+    mPathBuilder = nullptr;
+  }
+
+  if (aCommitTransform &&
+      mPath &&
+      mPathTransformWillUpdate) {
+    mDSPathBuilder =
+      mPath->TransformedCopyToBuilder(mPathToDS, fillRule);
+    mPath = nullptr;
+    mPathTransformWillUpdate = false;
+  }
+
+  if (mDSPathBuilder) {
+    RefPtr<Path> dsPath;
+    dsPath = mDSPathBuilder->Finish();
+    mDSPathBuilder = nullptr;
+
+    Matrix inverse = mTarget->GetTransform();
+    if (!inverse.Invert()) {
+      NS_WARNING("Could not invert transform");
+      return;
+    }
+
+    mPathBuilder =
+      dsPath->TransformedCopyToBuilder(inverse, fillRule);
+    mPath = mPathBuilder->Finish();
+    mPathBuilder = nullptr;
+  }
+
+  if (mPath && mPath->GetFillRule() != fillRule) {
+    mPathBuilder = mPath->CopyToBuilder(fillRule);
+    mPath = mPathBuilder->Finish();
+  }
+
+  NS_ASSERTION(mPath, "mPath should exist");
+}
+
+void
+CanvasRenderingContext2D::TransformWillUpdate()
+{
+  EnsureTarget();
+
+  // Store the matrix that would transform the current path to device
+  // space.
+  if (mPath || mPathBuilder) {
+    if (!mPathTransformWillUpdate) {
+      // If the transform has already been updated, but a device space builder
+      // has not been created yet mPathToDS contains the right transform to
+      // transform the current mPath into device space.
+      // We should leave it alone.
+      mPathToDS = mTarget->GetTransform();
+    }
+    mPathTransformWillUpdate = true;
+  }
+}
+
+//
+// text
+//
+
+/**
+ * Helper function for SetFont that creates a style rule for the given font.
+ * @param aFont The CSS font string
+ * @param aNode The canvas element
+ * @param aResult Pointer in which to place the new style rule.
+ * @remark Assumes all pointer arguments are non-null.
+ */
+static nsresult
+CreateFontStyleRule(const nsAString& aFont,
+                    nsINode* aNode,
+                    StyleRule** aResult)
+{
+  nsRefPtr<StyleRule> rule;
+  bool changed;
+
+  nsIPrincipal* principal = aNode->NodePrincipal();
+  nsIDocument* document = aNode->OwnerDoc();
+
+  nsIURI* docURL = document->GetDocumentURI();
+  nsIURI* baseURL = document->GetDocBaseURI();
+
+  // Pass the CSS Loader object to the parser, to allow parser error reports
+  // to include the outer window ID.
+  nsCSSParser parser(document->CSSLoader());
+
+  nsresult rv = parser.ParseStyleAttribute(EmptyString(), docURL, baseURL,
+                                           principal, getter_AddRefs(rule));
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+
+  rv = parser.ParseProperty(eCSSProperty_font, aFont, docURL, baseURL,
+                            principal, rule->GetDeclaration(), &changed,
+                            false);
+  if (NS_FAILED(rv))
+    return rv;
+
+  rv = parser.ParseProperty(eCSSProperty_line_height,
+                            NS_LITERAL_STRING("normal"), docURL, baseURL,
+                            principal, rule->GetDeclaration(), &changed,
+                            false);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+
+  rule->RuleMatched();
+
+  rule.forget(aResult);
+  return NS_OK;
+}
+
+void
+CanvasRenderingContext2D::SetFont(const nsAString& font,
+                                  ErrorResult& error)
+{
+  /*
+    * If font is defined with relative units (e.g. ems) and the parent
+    * style context changes in between calls, setting the font to the
+    * same value as previous could result in a different computed value,
+    * so we cannot have the optimization where we check if the new font
+    * string is equal to the old one.
+    */
+
+  if (!mCanvasElement && !mDocShell) {
+    NS_WARNING("Canvas element must be non-null or a docshell must be provided");
+    error.Throw(NS_ERROR_FAILURE);
+    return;
+  }
+
+  nsIPresShell* presShell = GetPresShell();
+  if (!presShell) {
+    error.Throw(NS_ERROR_FAILURE);
+    return;
+  }
+  nsIDocument* document = presShell->GetDocument();
+
+  nsRefPtr<css::StyleRule> rule;
+  error = CreateFontStyleRule(font, document, getter_AddRefs(rule));
+
+  if (error.Failed()) {
+    return;
+  }
+
+  css::Declaration *declaration = rule->GetDeclaration();
+  // The easiest way to see whether we got a syntax error or whether
+  // we got 'inherit' or 'initial' is to look at font-size-adjust,
+  // which the shorthand resets to either 'none' or
+  // '-moz-system-font'.
+  // We know the declaration is not !important, so we can use
+  // GetNormalBlock().
+  const nsCSSValue *fsaVal =
+    declaration->GetNormalBlock()->ValueFor(eCSSProperty_font_size_adjust);
+  if (!fsaVal || (fsaVal->GetUnit() != eCSSUnit_None &&
+                  fsaVal->GetUnit() != eCSSUnit_System_Font)) {
+      // We got an all-property value or a syntax error.  The spec says
+      // this value must be ignored.
+    return;
+  }
+
+  nsTArray< nsCOMPtr<nsIStyleRule> > rules;
+  rules.AppendElement(rule);
+
+  nsStyleSet* styleSet = presShell->StyleSet();
+
+  // have to get a parent style context for inherit-like relative
+  // values (2em, bolder, etc.)
+  nsRefPtr<nsStyleContext> parentContext;
+
+  if (mCanvasElement && mCanvasElement->IsInDoc()) {
+      // inherit from the canvas element
+      parentContext = nsComputedDOMStyle::GetStyleContextForElement(
+              mCanvasElement,
+              nullptr,
+              presShell);
+  } else {
+    // otherwise inherit from default (10px sans-serif)
+    nsRefPtr<css::StyleRule> parentRule;
+    error = CreateFontStyleRule(NS_LITERAL_STRING("10px sans-serif"),
+                                document,
+                                getter_AddRefs(parentRule));
+
+    if (error.Failed()) {
+      return;
+    }
+
+    nsTArray< nsCOMPtr<nsIStyleRule> > parentRules;
+    parentRules.AppendElement(parentRule);
+    parentContext = styleSet->ResolveStyleForRules(nullptr, parentRules);
+  }
+
+  if (!parentContext) {
+    error.Throw(NS_ERROR_FAILURE);
+    return;
+  }
+
+  nsRefPtr<nsStyleContext> sc =
+      styleSet->ResolveStyleForRules(parentContext, rules);
+  if (!sc) {
+    error.Throw(NS_ERROR_FAILURE);
+    return;
+  }
+
+  const nsStyleFont* fontStyle = sc->GetStyleFont();
+
+  NS_ASSERTION(fontStyle, "Could not obtain font style");
+
+  nsIAtom* language = sc->GetStyleFont()->mLanguage;
+  if (!language) {
+    language = presShell->GetPresContext()->GetLanguageFromCharset();
+  }
+
+  // use CSS pixels instead of dev pixels to avoid being affected by page zoom
+  const uint32_t aupcp = nsPresContext::AppUnitsPerCSSPixel();
+  // un-zoom the font size to avoid being affected by text-only zoom
+  //
+  // Purposely ignore the font size that respects the user's minimum
+  // font preference (fontStyle->mFont.size) in favor of the computed
+  // size (fontStyle->mSize).  See
+  // https://bugzilla.mozilla.org/show_bug.cgi?id=698652.
+  const nscoord fontSize = nsStyleFont::UnZoomText(parentContext->PresContext(), fontStyle->mSize);
+
+  bool printerFont = (presShell->GetPresContext()->Type() == nsPresContext::eContext_PrintPreview ||
+                      presShell->GetPresContext()->Type() == nsPresContext::eContext_Print);
+
+  gfxFontStyle style(fontStyle->mFont.style,
+                     fontStyle->mFont.weight,
+                     fontStyle->mFont.stretch,
+                     NSAppUnitsToFloatPixels(fontSize, float(aupcp)),
+                     language,
+                     fontStyle->mFont.sizeAdjust,
+                     fontStyle->mFont.systemFont,
+                     printerFont,
+                     fontStyle->mFont.languageOverride);
+
+  fontStyle->mFont.AddFontFeaturesToStyle(&style);
+
+  CurrentState().fontGroup =
+      gfxPlatform::GetPlatform()->CreateFontGroup(fontStyle->mFont.name,
+                                                  &style,
+                                                  presShell->GetPresContext()->GetUserFontSet());
+  NS_ASSERTION(CurrentState().fontGroup, "Could not get font group");
+
+  // The font getter is required to be reserialized based on what we
+  // parsed (including having line-height removed).  (Older drafts of
+  // the spec required font sizes be converted to pixels, but that no
+  // longer seems to be required.)
+  declaration->GetValue(eCSSProperty_font, CurrentState().font);
+}
+
+void
+CanvasRenderingContext2D::SetTextAlign(const nsAString& ta)
+{
+  if (ta.EqualsLiteral("start"))
+    CurrentState().textAlign = TEXT_ALIGN_START;
+  else if (ta.EqualsLiteral("end"))
+    CurrentState().textAlign = TEXT_ALIGN_END;
+  else if (ta.EqualsLiteral("left"))
+    CurrentState().textAlign = TEXT_ALIGN_LEFT;
+  else if (ta.EqualsLiteral("right"))
+    CurrentState().textAlign = TEXT_ALIGN_RIGHT;
+  else if (ta.EqualsLiteral("center"))
+    CurrentState().textAlign = TEXT_ALIGN_CENTER;
+}
+
+void
+CanvasRenderingContext2D::GetTextAlign(nsAString& ta)
+{
+  switch (CurrentState().textAlign)
+  {
+  case TEXT_ALIGN_START:
+    ta.AssignLiteral("start");
+    break;
+  case TEXT_ALIGN_END:
+    ta.AssignLiteral("end");
+    break;
+  case TEXT_ALIGN_LEFT:
+    ta.AssignLiteral("left");
+    break;
+  case TEXT_ALIGN_RIGHT:
+    ta.AssignLiteral("right");
+    break;
+  case TEXT_ALIGN_CENTER:
+    ta.AssignLiteral("center");
+    break;
+  }
+}
+
+void
+CanvasRenderingContext2D::SetTextBaseline(const nsAString& tb)
+{
+  if (tb.EqualsLiteral("top"))
+    CurrentState().textBaseline = TEXT_BASELINE_TOP;
+  else if (tb.EqualsLiteral("hanging"))
+    CurrentState().textBaseline = TEXT_BASELINE_HANGING;
+  else if (tb.EqualsLiteral("middle"))
+    CurrentState().textBaseline = TEXT_BASELINE_MIDDLE;
+  else if (tb.EqualsLiteral("alphabetic"))
+    CurrentState().textBaseline = TEXT_BASELINE_ALPHABETIC;
+  else if (tb.EqualsLiteral("ideographic"))
+    CurrentState().textBaseline = TEXT_BASELINE_IDEOGRAPHIC;
+  else if (tb.EqualsLiteral("bottom"))
+    CurrentState().textBaseline = TEXT_BASELINE_BOTTOM;
+}
+
+void
+CanvasRenderingContext2D::GetTextBaseline(nsAString& tb)
+{
+  switch (CurrentState().textBaseline)
+  {
+  case TEXT_BASELINE_TOP:
+    tb.AssignLiteral("top");
+    break;
+  case TEXT_BASELINE_HANGING:
+    tb.AssignLiteral("hanging");
+    break;
+  case TEXT_BASELINE_MIDDLE:
+    tb.AssignLiteral("middle");
+    break;
+  case TEXT_BASELINE_ALPHABETIC:
+    tb.AssignLiteral("alphabetic");
+    break;
+  case TEXT_BASELINE_IDEOGRAPHIC:
+    tb.AssignLiteral("ideographic");
+    break;
+  case TEXT_BASELINE_BOTTOM:
+    tb.AssignLiteral("bottom");
+    break;
+  }
+}
+
+/*
+ * Helper function that replaces the whitespace characters in a string
+ * with U+0020 SPACE. The whitespace characters are defined as U+0020 SPACE,
+ * U+0009 CHARACTER TABULATION (tab), U+000A LINE FEED (LF), U+000B LINE
+ * TABULATION, U+000C FORM FEED (FF), and U+000D CARRIAGE RETURN (CR).
+ * @param str The string whose whitespace characters to replace.
+ */
+static inline void
+TextReplaceWhitespaceCharacters(nsAutoString& str)
+{
+  str.ReplaceChar("\x09\x0A\x0B\x0C\x0D", PRUnichar(' '));
+}
+
+void
+CanvasRenderingContext2D::FillText(const nsAString& text, double x,
+                                   double y,
+                                   const Optional<double>& maxWidth,
+                                   ErrorResult& error)
+{
+  error = DrawOrMeasureText(text, x, y, maxWidth, TEXT_DRAW_OPERATION_FILL, nullptr);
+}
+
+void
+CanvasRenderingContext2D::StrokeText(const nsAString& text, double x,
+                                     double y,
+                                     const Optional<double>& maxWidth,
+                                     ErrorResult& error)
+{
+  error = DrawOrMeasureText(text, x, y, maxWidth, TEXT_DRAW_OPERATION_STROKE, nullptr);
+}
+
+already_AddRefed<nsIDOMTextMetrics>
+CanvasRenderingContext2D::MeasureText(const nsAString& rawText,
+                                      ErrorResult& error)
+{
+  float width;
+  Optional<double> maxWidth;
+  error = DrawOrMeasureText(rawText, 0, 0, maxWidth, TEXT_DRAW_OPERATION_MEASURE, &width);
+  if (error.Failed()) {
+    return NULL;
+  }
+
+  nsRefPtr<nsIDOMTextMetrics> textMetrics = new TextMetrics(width);
+
+  return textMetrics.forget();
+}
+
+/**
+ * Used for nsBidiPresUtils::ProcessText
+ */
+struct NS_STACK_CLASS CanvasBidiProcessor : public nsBidiPresUtils::BidiProcessor
+{
+  typedef CanvasRenderingContext2D::ContextState ContextState;
+
+  virtual void SetText(const PRUnichar* text, int32_t length, nsBidiDirection direction)
+  {
+    mFontgrp->UpdateFontList(); // ensure user font generation is current
+    mTextRun = mFontgrp->MakeTextRun(text,
+                                     length,
+                                     mThebes,
+                                     mAppUnitsPerDevPixel,
+                                     direction==NSBIDI_RTL ? gfxTextRunFactory::TEXT_IS_RTL : 0);
+  }
+
+  virtual nscoord GetWidth()
+  {
+    gfxTextRun::Metrics textRunMetrics = mTextRun->MeasureText(0,
+                                                               mTextRun->GetLength(),
+                                                               mDoMeasureBoundingBox ?
+                                                                 gfxFont::TIGHT_INK_EXTENTS :
+                                                                 gfxFont::LOOSE_INK_EXTENTS,
+                                                               mThebes,
+                                                               nullptr);
+
+    // this only measures the height; the total width is gotten from the
+    // the return value of ProcessText.
+    if (mDoMeasureBoundingBox) {
+      textRunMetrics.mBoundingBox.Scale(1.0 / mAppUnitsPerDevPixel);
+      mBoundingBox = mBoundingBox.Union(textRunMetrics.mBoundingBox);
+    }
+
+    return NSToCoordRound(textRunMetrics.mAdvanceWidth);
+  }
+
+  virtual void DrawText(nscoord xOffset, nscoord width)
+  {
+    gfxPoint point = mPt;
+    point.x += xOffset;
+
+    // offset is given in terms of left side of string
+    if (mTextRun->IsRightToLeft()) {
+      // Bug 581092 - don't use rounded pixel width to advance to
+      // right-hand end of run, because this will cause different
+      // glyph positioning for LTR vs RTL drawing of the same
+      // glyph string on OS X and DWrite where textrun widths may
+      // involve fractional pixels.
+      gfxTextRun::Metrics textRunMetrics =
+        mTextRun->MeasureText(0,
+                              mTextRun->GetLength(),
+                              mDoMeasureBoundingBox ?
+                                  gfxFont::TIGHT_INK_EXTENTS :
+                                  gfxFont::LOOSE_INK_EXTENTS,
+                              mThebes,
+                              nullptr);
+      point.x += textRunMetrics.mAdvanceWidth;
+      // old code was:
+      //   point.x += width * mAppUnitsPerDevPixel;
+      // TODO: restore this if/when we move to fractional coords
+      // throughout the text layout process
+    }
+
+    uint32_t numRuns;
+    const gfxTextRun::GlyphRun *runs = mTextRun->GetGlyphRuns(&numRuns);
+    const uint32_t appUnitsPerDevUnit = mAppUnitsPerDevPixel;
+    const double devUnitsPerAppUnit = 1.0/double(appUnitsPerDevUnit);
+    Point baselineOrigin =
+      Point(point.x * devUnitsPerAppUnit, point.y * devUnitsPerAppUnit);
+
+    float advanceSum = 0;
+
+    mCtx->EnsureTarget();
+    for (uint32_t c = 0; c < numRuns; c++) {
+      gfxFont *font = runs[c].mFont;
+      uint32_t endRun = 0;
+      if (c + 1 < numRuns) {
+        endRun = runs[c + 1].mCharacterOffset;
+      } else {
+        endRun = mTextRun->GetLength();
+      }
+
+      const gfxTextRun::CompressedGlyph *glyphs = mTextRun->GetCharacterGlyphs();
+
+      RefPtr<ScaledFont> scaledFont =
+        gfxPlatform::GetPlatform()->GetScaledFontForFont(mCtx->mTarget, font);
+
+      if (!scaledFont) {
+        // This can occur when something switched DirectWrite off.
+        return;
+      }
+
+      GlyphBuffer buffer;
+
+      std::vector<Glyph> glyphBuf;
+
+      for (uint32_t i = runs[c].mCharacterOffset; i < endRun; i++) {
+        Glyph newGlyph;
+        if (glyphs[i].IsSimpleGlyph()) {
+          newGlyph.mIndex = glyphs[i].GetSimpleGlyph();
+          if (mTextRun->IsRightToLeft()) {
+            newGlyph.mPosition.x = baselineOrigin.x - advanceSum -
+              glyphs[i].GetSimpleAdvance() * devUnitsPerAppUnit;
+          } else {
+            newGlyph.mPosition.x = baselineOrigin.x + advanceSum;
+          }
+          newGlyph.mPosition.y = baselineOrigin.y;
+          advanceSum += glyphs[i].GetSimpleAdvance() * devUnitsPerAppUnit;
+          glyphBuf.push_back(newGlyph);
+          continue;
+        }
+
+        if (!glyphs[i].GetGlyphCount()) {
+          continue;
+        }
+
+        gfxTextRun::DetailedGlyph *detailedGlyphs =
+          mTextRun->GetDetailedGlyphs(i);
+
+        if (glyphs[i].IsMissing()) {
+          float xpos;
+          float advance = detailedGlyphs[0].mAdvance * devUnitsPerAppUnit;
+          if (mTextRun->IsRightToLeft()) {
+            xpos = baselineOrigin.x - advanceSum - advance;
+          } else {
+            xpos = baselineOrigin.x + advanceSum;
+          }
+          advanceSum += advance;
+
+          // default-ignorable characters will have zero advance width.
+          // we don't draw a hexbox for them, just leave them invisible
+          if (advance > 0) {
+            // for now, we use gfxFontMissingGlyphs to draw the hexbox;
+            // some day we should replace this with a direct Azure version
+
+            // get the DrawTarget's transform, so we can apply it to the
+            // thebes context for gfxFontMissingGlyphs
+            Matrix matrix = mCtx->mTarget->GetTransform();
+            nsRefPtr<gfxContext> thebes;
+            if (gfxPlatform::GetPlatform()->SupportsAzureContent()) {
+              // XXX See bug 808288 comment 5 - Bas says:
+              // This is a little tricky, potentially this could go wrong if
+              // we fell back to a Cairo context because of for example
+              // extremely large Canvas size. Cairo content is technically
+              // -not- supported, but SupportsAzureContent would return true
+              // as the browser uses D2D content.
+              // I'm thinking Cairo content will be good enough to do
+              // DrawMissingGlyph though.
+              thebes = new gfxContext(mCtx->mTarget);
+            } else {
+              nsRefPtr<gfxASurface> drawSurf;
+              mCtx->GetThebesSurface(getter_AddRefs(drawSurf));
+              thebes = new gfxContext(drawSurf);
+            }
+            thebes->SetMatrix(gfxMatrix(matrix._11, matrix._12, matrix._21,
+                                        matrix._22, matrix._31, matrix._32));
+
+            gfxFloat height = font->GetMetrics().maxAscent;
+            gfxRect glyphRect(xpos, baselineOrigin.y - height,
+                              advance, height);
+            gfxFontMissingGlyphs::DrawMissingGlyph(thebes, glyphRect,
+                                                   detailedGlyphs[0].mGlyphID);
+
+            mCtx->mTarget->SetTransform(matrix);
+          }
+          continue;
+        }
+
+        for (uint32_t c = 0; c < glyphs[i].GetGlyphCount(); c++) {
+          newGlyph.mIndex = detailedGlyphs[c].mGlyphID;
+          if (mTextRun->IsRightToLeft()) {
+            newGlyph.mPosition.x = baselineOrigin.x + detailedGlyphs[c].mXOffset * devUnitsPerAppUnit -
+              advanceSum - detailedGlyphs[c].mAdvance * devUnitsPerAppUnit;
+          } else {
+            newGlyph.mPosition.x = baselineOrigin.x + detailedGlyphs[c].mXOffset * devUnitsPerAppUnit + advanceSum;
+          }
+          newGlyph.mPosition.y = baselineOrigin.y + detailedGlyphs[c].mYOffset * devUnitsPerAppUnit;
+          glyphBuf.push_back(newGlyph);
+          advanceSum += detailedGlyphs[c].mAdvance * devUnitsPerAppUnit;
+        }
+      }
+
+      if (!glyphBuf.size()) {
+        // This may happen for glyph runs for a 0 size font.
+        continue;
+      }
+
+      buffer.mGlyphs = &glyphBuf.front();
+      buffer.mNumGlyphs = glyphBuf.size();
+
+      Rect bounds = mCtx->mTarget->GetTransform().
+        TransformBounds(Rect(mBoundingBox.x, mBoundingBox.y,
+                             mBoundingBox.width, mBoundingBox.height));
+      if (mOp == CanvasRenderingContext2D::TEXT_DRAW_OPERATION_FILL) {
+        AdjustedTarget(mCtx, &bounds)->
+          FillGlyphs(scaledFont, buffer,
+                     CanvasGeneralPattern().
+                       ForStyle(mCtx, CanvasRenderingContext2D::STYLE_FILL, mCtx->mTarget),
+                     DrawOptions(mState->globalAlpha, mCtx->UsedOperation()));
+      } else if (mOp == CanvasRenderingContext2D::TEXT_DRAW_OPERATION_STROKE) {
+        RefPtr<Path> path = scaledFont->GetPathForGlyphs(buffer, mCtx->mTarget);
+
+        const ContextState& state = *mState;
+        AdjustedTarget(mCtx, &bounds)->
+          Stroke(path, CanvasGeneralPattern().
+                   ForStyle(mCtx, CanvasRenderingContext2D::STYLE_STROKE, mCtx->mTarget),
+                 StrokeOptions(state.lineWidth, state.lineJoin,
+                               state.lineCap, state.miterLimit,
+                               state.dash.Length(),
+                               state.dash.Elements(),
+                               state.dashOffset),
+                 DrawOptions(state.globalAlpha, mCtx->UsedOperation()));
+
+      }
+    }
+  }
+
+  // current text run
+  nsAutoPtr<gfxTextRun> mTextRun;
+
+  // pointer to a screen reference context used to measure text and such
+  nsRefPtr<gfxContext> mThebes;
+
+  // Pointer to the draw target we should fill our text to
+  CanvasRenderingContext2D *mCtx;
+
+  // position of the left side of the string, alphabetic baseline
+  gfxPoint mPt;
+
+  // current font
+  gfxFontGroup* mFontgrp;
+
+  // dev pixel conversion factor
+  uint32_t mAppUnitsPerDevPixel;
+
+  // operation (fill or stroke)
+  CanvasRenderingContext2D::TextDrawOperation mOp;
+
+  // context state
+  ContextState *mState;
+
+  // union of bounding boxes of all runs, needed for shadows
+  gfxRect mBoundingBox;
+
+  // true iff the bounding box should be measured
+  bool mDoMeasureBoundingBox;
+};
+
+nsresult
+CanvasRenderingContext2D::DrawOrMeasureText(const nsAString& aRawText,
+                                            float aX,
+                                            float aY,
+                                            const Optional<double>& aMaxWidth,
+                                            TextDrawOperation aOp,
+                                            float* aWidth)
+{
+  nsresult rv;
+
+  if (!FloatValidate(aX, aY) ||
+      (aMaxWidth.WasPassed() && !FloatValidate(aMaxWidth.Value())))
+      return NS_ERROR_DOM_SYNTAX_ERR;
+
+  // spec isn't clear on what should happen if aMaxWidth <= 0, so
+  // treat it as an invalid argument
+  // technically, 0 should be an invalid value as well, but 0 is the default
+  // arg, and there is no way to tell if the default was used
+  if (aMaxWidth.WasPassed() && aMaxWidth.Value() < 0)
+    return NS_ERROR_INVALID_ARG;
+
+  if (!mCanvasElement && !mDocShell) {
+    NS_WARNING("Canvas element must be non-null or a docshell must be provided");
+    return NS_ERROR_FAILURE;
+  }
+
+  nsCOMPtr<nsIPresShell> presShell = GetPresShell();
+  if (!presShell)
+    return NS_ERROR_FAILURE;
+
+  nsIDocument* document = presShell->GetDocument();
+
+  // replace all the whitespace characters with U+0020 SPACE
+  nsAutoString textToDraw(aRawText);
+  TextReplaceWhitespaceCharacters(textToDraw);
+
+  // for now, default to ltr if not in doc
+  bool isRTL = false;
+
+  if (mCanvasElement && mCanvasElement->IsInDoc()) {
+    // try to find the closest context
+    nsRefPtr<nsStyleContext> canvasStyle =
+      nsComputedDOMStyle::GetStyleContextForElement(mCanvasElement,
+                                                    nullptr,
+                                                    presShell);
+    if (!canvasStyle) {
+      return NS_ERROR_FAILURE;
+    }
+
+    isRTL = canvasStyle->GetStyleVisibility()->mDirection ==
+      NS_STYLE_DIRECTION_RTL;
+  } else {
+    isRTL = GET_BIDI_OPTION_DIRECTION(document->GetBidiOptions()) == IBMBIDI_TEXTDIRECTION_RTL;
+  }
+
+  gfxFontGroup* currentFontStyle = GetCurrentFontStyle();
+  NS_ASSERTION(currentFontStyle, "font group is null");
+
+  if (currentFontStyle->GetStyle()->size == 0.0F) {
+    if (aWidth) {
+      *aWidth = 0;
+    }
+    return NS_OK;
+  }
+
+  const ContextState &state = CurrentState();
+
+  // This is only needed to know if we can know the drawing bounding box easily.
+  bool doDrawShadow = aOp == TEXT_DRAW_OPERATION_FILL && NeedToDrawShadow();
+
+  CanvasBidiProcessor processor;
+
+  GetAppUnitsValues(&processor.mAppUnitsPerDevPixel, nullptr);
+  processor.mPt = gfxPoint(aX, aY);
+  processor.mThebes =
+    new gfxContext(gfxPlatform::GetPlatform()->ScreenReferenceSurface());
+
+  // If we don't have a target then we don't have a transform. A target won't
+  // be needed in the case where we're measuring the text size. This allows
+  // to avoid creating a target if it's only being used to measure text sizes.
+  if (mTarget) {
+    Matrix matrix = mTarget->GetTransform();
+    processor.mThebes->SetMatrix(gfxMatrix(matrix._11, matrix._12, matrix._21, matrix._22, matrix._31, matrix._32));
+  }
+  processor.mCtx = this;
+  processor.mOp = aOp;
+  processor.mBoundingBox = gfxRect(0, 0, 0, 0);
+  processor.mDoMeasureBoundingBox = doDrawShadow || !mIsEntireFrameInvalid;
+  processor.mState = &CurrentState();
+  processor.mFontgrp = currentFontStyle;
+
+  nscoord totalWidthCoord;
+
+  // calls bidi algo twice since it needs the full text width and the
+  // bounding boxes before rendering anything
+  nsBidi bidiEngine;
+  rv = nsBidiPresUtils::ProcessText(textToDraw.get(),
+                                    textToDraw.Length(),
+                                    isRTL ? NSBIDI_RTL : NSBIDI_LTR,
+                                    presShell->GetPresContext(),
+                                    processor,
+                                    nsBidiPresUtils::MODE_MEASURE,
+                                    nullptr,
+                                    0,
+                                    &totalWidthCoord,
+                                    &bidiEngine);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+
+  float totalWidth = float(totalWidthCoord) / processor.mAppUnitsPerDevPixel;
+  if (aWidth) {
+    *aWidth = totalWidth;
+  }
+
+  // if only measuring, don't need to do any more work
+  if (aOp==TEXT_DRAW_OPERATION_MEASURE) {
+    return NS_OK;
+  }
+
+  // offset pt.x based on text align
+  gfxFloat anchorX;
+
+  if (state.textAlign == TEXT_ALIGN_CENTER) {
+    anchorX = .5;
+  } else if (state.textAlign == TEXT_ALIGN_LEFT ||
+            (!isRTL && state.textAlign == TEXT_ALIGN_START) ||
+            (isRTL && state.textAlign == TEXT_ALIGN_END)) {
+    anchorX = 0;
+  } else {
+    anchorX = 1;
+  }
+
+  processor.mPt.x -= anchorX * totalWidth;
+
+  // offset pt.y based on text baseline
+  processor.mFontgrp->UpdateFontList(); // ensure user font generation is current
+  NS_ASSERTION(processor.mFontgrp->FontListLength()>0, "font group contains no fonts");
+  const gfxFont::Metrics& fontMetrics = processor.mFontgrp->GetFontAt(0)->GetMetrics();
+
+  gfxFloat anchorY;
+
+  switch (state.textBaseline)
+  {
+  case TEXT_BASELINE_HANGING:
+      // fall through; best we can do with the information available
+  case TEXT_BASELINE_TOP:
+    anchorY = fontMetrics.emAscent;
+    break;
+  case TEXT_BASELINE_MIDDLE:
+    anchorY = (fontMetrics.emAscent - fontMetrics.emDescent) * .5f;
+    break;
+  case TEXT_BASELINE_IDEOGRAPHIC:
+    // fall through; best we can do with the information available
+  case TEXT_BASELINE_ALPHABETIC:
+    anchorY = 0;
+    break;
+  case TEXT_BASELINE_BOTTOM:
+    anchorY = -fontMetrics.emDescent;
+    break;
+  default:
+      MOZ_NOT_REACHED("unexpected TextBaseline");
+  }
+
+  processor.mPt.y += anchorY;
+
+  // correct bounding box to get it to be the correct size/position
+  processor.mBoundingBox.width = totalWidth;
+  processor.mBoundingBox.MoveBy(processor.mPt);
+
+  processor.mPt.x *= processor.mAppUnitsPerDevPixel;
+  processor.mPt.y *= processor.mAppUnitsPerDevPixel;
+
+  EnsureTarget();
+  Matrix oldTransform = mTarget->GetTransform();
+  // if text is over aMaxWidth, then scale the text horizontally such that its
+  // width is precisely aMaxWidth
+  if (aMaxWidth.WasPassed() && aMaxWidth.Value() > 0 &&
+      totalWidth > aMaxWidth.Value()) {
+    Matrix newTransform = oldTransform;
+
+    // Translate so that the anchor point is at 0,0, then scale and then
+    // translate back.
+    newTransform.Translate(aX, 0);
+    newTransform.Scale(aMaxWidth.Value() / totalWidth, 1);
+    newTransform.Translate(-aX, 0);
+    /* we do this to avoid an ICE in the android compiler */
+    Matrix androidCompilerBug = newTransform;
+    mTarget->SetTransform(androidCompilerBug);
+  }
+
+  // save the previous bounding box
+  gfxRect boundingBox = processor.mBoundingBox;
+
+  // don't ever need to measure the bounding box twice
+  processor.mDoMeasureBoundingBox = false;
+
+  rv = nsBidiPresUtils::ProcessText(textToDraw.get(),
+                                    textToDraw.Length(),
+                                    isRTL ? NSBIDI_RTL : NSBIDI_LTR,
+                                    presShell->GetPresContext(),
+                                    processor,
+                                    nsBidiPresUtils::MODE_DRAW,
+                                    nullptr,
+                                    0,
+                                    nullptr,
+                                    &bidiEngine);
+
+
+  mTarget->SetTransform(oldTransform);
+
+  if (aOp == CanvasRenderingContext2D::TEXT_DRAW_OPERATION_FILL &&
+      !doDrawShadow) {
+    RedrawUser(boundingBox);
+    return NS_OK;
+  }
+
+  Redraw();
+  return NS_OK;
+}
+
+gfxFontGroup *CanvasRenderingContext2D::GetCurrentFontStyle()
+{
+  // use lazy initilization for the font group since it's rather expensive
+  if (!CurrentState().fontGroup) {
+    ErrorResult err;
+    SetFont(kDefaultFontStyle, err);
+    if (err.Failed()) {
+      gfxFontStyle style;
+      style.size = kDefaultFontSize;
+      CurrentState().fontGroup =
+        gfxPlatform::GetPlatform()->CreateFontGroup(kDefaultFontName,
+                                                    &style,
+                                                    nullptr);
+      if (CurrentState().fontGroup) {
+        CurrentState().font = kDefaultFontStyle;
+      } else {
+        NS_ERROR("Default canvas font is invalid");
+      }
+    }
+
+  }
+
+  return CurrentState().fontGroup;
+}
+
+//
+// line caps/joins
+//
+
+void
+CanvasRenderingContext2D::SetLineCap(const nsAString& capstyle)
+{
+  CapStyle cap;
+
+  if (capstyle.EqualsLiteral("butt")) {
+    cap = CAP_BUTT;
+  } else if (capstyle.EqualsLiteral("round")) {
+    cap = CAP_ROUND;
+  } else if (capstyle.EqualsLiteral("square")) {
+    cap = CAP_SQUARE;
+  } else {
+    // XXX ERRMSG we need to report an error to developers here! (bug 329026)
+    return;
+  }
+
+  CurrentState().lineCap = cap;
+}
+
+void
+CanvasRenderingContext2D::GetLineCap(nsAString& capstyle)
+{
+  switch (CurrentState().lineCap) {
+  case CAP_BUTT:
+    capstyle.AssignLiteral("butt");
+    break;
+  case CAP_ROUND:
+    capstyle.AssignLiteral("round");
+    break;
+  case CAP_SQUARE:
+    capstyle.AssignLiteral("square");
+    break;
+  }
+}
+
+void
+CanvasRenderingContext2D::SetLineJoin(const nsAString& joinstyle)
+{
+  JoinStyle j;
+
+  if (joinstyle.EqualsLiteral("round")) {
+    j = JOIN_ROUND;
+  } else if (joinstyle.EqualsLiteral("bevel")) {
+    j = JOIN_BEVEL;
+  } else if (joinstyle.EqualsLiteral("miter")) {
+    j = JOIN_MITER_OR_BEVEL;
+  } else {
+    // XXX ERRMSG we need to report an error to developers here! (bug 329026)
+    return;
+  }
+
+  CurrentState().lineJoin = j;
+}
+
+void
+CanvasRenderingContext2D::GetLineJoin(nsAString& joinstyle, ErrorResult& error)
+{
+  switch (CurrentState().lineJoin) {
+  case JOIN_ROUND:
+    joinstyle.AssignLiteral("round");
+    break;
+  case JOIN_BEVEL:
+    joinstyle.AssignLiteral("bevel");
+    break;
+  case JOIN_MITER_OR_BEVEL:
+    joinstyle.AssignLiteral("miter");
+    break;
+  default:
+    error.Throw(NS_ERROR_FAILURE);
+  }
+}
+
+void
+CanvasRenderingContext2D::SetMozDash(JSContext* cx,
+                                     const JS::Value& mozDash,
+                                     ErrorResult& error)
+{
+  FallibleTArray<Float> dash;
+  error = JSValToDashArray(cx, mozDash, dash);
+  if (!error.Failed()) {
+    ContextState& state = CurrentState();
+    state.dash = dash;
+    if (state.dash.IsEmpty()) {
+      state.dashOffset = 0;
+    }
+  }
+}
+
+JS::Value
+CanvasRenderingContext2D::GetMozDash(JSContext* cx, ErrorResult& error)
+{
+  JS::Value mozDash;
+  error = DashArrayToJSVal(CurrentState().dash, cx, &mozDash);
+  return mozDash;
+}
+
+void
+CanvasRenderingContext2D::SetMozDashOffset(double mozDashOffset)
+{
+  if (!FloatValidate(mozDashOffset)) {
+    return;
+  }
+
+  ContextState& state = CurrentState();
   if (!state.dash.IsEmpty()) {
     state.dashOffset = mozDashOffset;
-  }
-}
-
-bool
-CanvasRenderingContext2D::IsPointInPath(double x, double y)
-{
-  if (!FloatValidate(x,y)) {
-    return false;
-  }
-
-  EnsureUserSpacePath(false);
-  if (!mPath) {
-    return false;
-  }
-  if (mPathTransformWillUpdate) {
-    return mPath->ContainsPoint(Point(x, y), mPathToDS);
-  }
-  return mPath->ContainsPoint(Point(x, y), mTarget->GetTransform());
-}
-
-bool
-CanvasRenderingContext2D::MozIsPointInStroke(double x, double y)
-{
-  if (!FloatValidate(x,y)) {
-    return false;
-  }
-
-  EnsureUserSpacePath(false);
-  if (!mPath) {
-    return false;
-  }
-
-  const ContextState &state = CurrentState();
-
-  StrokeOptions strokeOptions(state.lineWidth,
-                              state.lineJoin,
-                              state.lineCap,
-                              state.miterLimit,
-                              state.dash.Length(),
-                              state.dash.Elements(),
-                              state.dashOffset);
-
-  if (mPathTransformWillUpdate) {
-    return mPath->StrokeContainsPoint(strokeOptions, Point(x, y), mPathToDS);
-  }
-  return mPath->StrokeContainsPoint(strokeOptions, Point(x, y), mTarget->GetTransform());
-}
-
-//
-// image
-//
-
-// drawImage(in HTMLImageElement image, in float dx, in float dy);
-//   -- render image from 0,0 at dx,dy top-left coords
-// drawImage(in HTMLImageElement image, in float dx, in float dy, in float sw, in float sh);
-//   -- render image from 0,0 at dx,dy top-left coords clipping it to sw,sh
-// drawImage(in HTMLImageElement image, in float sx, in float sy, in float sw, in float sh, in float dx, in float dy, in float dw, in float dh);
-//   -- render the region defined by (sx,sy,sw,wh) in image-local space into the region (dx,dy,dw,dh) on the canvas
-
-// If only dx and dy are passed in then optional_argc should be 0. If only
-// dx, dy, dw and dh are passed in then optional_argc should be 2. The only
-// other valid value for optional_argc is 6 if sx, sy, sw, sh, dx, dy, dw and dh
-// are all passed in.
-
-void
-CanvasRenderingContext2D::DrawImage(const HTMLImageOrCanvasOrVideoElement& image,
-                                    double sx, double sy, double sw,
-                                    double sh, double dx, double dy,
-                                    double dw, double dh,
-                                    uint8_t optional_argc,
-                                    ErrorResult& error)
-{
-  MOZ_ASSERT(optional_argc == 0 || optional_argc == 2 || optional_argc == 6);
-
-  RefPtr<SourceSurface> srcSurf;
-  gfxIntSize imgSize;
-
-  Element* element;
-
-  EnsureTarget();
-  if (image.IsHTMLCanvasElement()) {
-    nsHTMLCanvasElement* canvas = image.GetAsHTMLCanvasElement();
-    element = canvas;
-    nsIntSize size = canvas->GetSize();
-    if (size.width == 0 || size.height == 0) {
-      error.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
-      return;
-    }
-
-    // Special case for Canvas, which could be an Azure canvas!
-    nsICanvasRenderingContextInternal *srcCanvas = canvas->GetContextAtIndex(0);
-    if (srcCanvas == this) {
-      // Self-copy.
-      srcSurf = mTarget->Snapshot();
-      imgSize = gfxIntSize(mWidth, mHeight);
-    } else if (srcCanvas) {
-      // This might not be an Azure canvas!
-      srcSurf = srcCanvas->GetSurfaceSnapshot();
-
-      if (srcSurf) {
-        if (mCanvasElement) {
-          // Do security check here.
-          CanvasUtils::DoDrawImageSecurityCheck(mCanvasElement,
-                                                element->NodePrincipal(),
-                                                canvas->IsWriteOnly(),
-                                                false);
-        }
-        imgSize = gfxIntSize(srcSurf->GetSize().width, srcSurf->GetSize().height);
-      }
-    }
-  } else {
-    if (image.IsHTMLImageElement()) {
-      nsHTMLImageElement* img = image.GetAsHTMLImageElement();
-      element = img;
-    } else {
-      nsHTMLVideoElement* video = image.GetAsHTMLVideoElement();
-      element = video;
-    }
-
-    gfxASurface* imgsurf =
-      CanvasImageCache::Lookup(element, mCanvasElement, &imgSize);
-    if (imgsurf) {
-      srcSurf = gfxPlatform::GetPlatform()->GetSourceSurfaceForSurface(mTarget, imgsurf);
-    }
-  }
-
-  if (!srcSurf) {
-    // The canvas spec says that drawImage should draw the first frame
-    // of animated images
-    uint32_t sfeFlags = nsLayoutUtils::SFE_WANT_FIRST_FRAME;
-    nsLayoutUtils::SurfaceFromElementResult res =
-      nsLayoutUtils::SurfaceFromElement(element, sfeFlags);
-
-    if (!res.mSurface) {
-      // Spec says to silently do nothing if the element is still loading.
-      if (!res.mIsStillLoading) {
-        error.Throw(NS_ERROR_NOT_AVAILABLE);
-      }
-      return;
-    }
-
-    // Ignore cairo surfaces that are bad! See bug 666312.
-    if (res.mSurface->CairoStatus()) {
-      return;
-    }
-
-    imgSize = res.mSize;
-
-    if (mCanvasElement) {
-      CanvasUtils::DoDrawImageSecurityCheck(mCanvasElement,
-                                            res.mPrincipal, res.mIsWriteOnly,
-                                            res.mCORSUsed);
-    }
-
-    if (res.mImageRequest) {
-      CanvasImageCache::NotifyDrawImage(element, mCanvasElement,
-                                        res.mImageRequest, res.mSurface, imgSize);
-    }
-
-    srcSurf = gfxPlatform::GetPlatform()->GetSourceSurfaceForSurface(mTarget, res.mSurface);
-  }
-
-  if (optional_argc == 0) {
-    sx = sy = 0.0;
-    dw = sw = (double) imgSize.width;
-    dh = sh = (double) imgSize.height;
-  } else if (optional_argc == 2) {
-    sx = sy = 0.0;
-    sw = (double) imgSize.width;
-    sh = (double) imgSize.height;
-  }
-
-  if (sw == 0.0 || sh == 0.0) {
-    error.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
-    return;
-  }
-
-  if (dw == 0.0 || dh == 0.0) {
-    // not really failure, but nothing to do --
-    // and noone likes a divide-by-zero
-    return;
-  }
-
-  if (sx < 0.0 || sy < 0.0 ||
-      sw < 0.0 || sw > (double) imgSize.width ||
-      sh < 0.0 || sh > (double) imgSize.height ||
-      dw < 0.0 || dh < 0.0) {
-    // XXX - Unresolved spec issues here, for now return error.
-    error.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
-    return;
-  }
-
-  Filter filter;
-
-  if (CurrentState().imageSmoothingEnabled)
-    filter = mgfx::FILTER_LINEAR;
-  else
-    filter = mgfx::FILTER_POINT;
-
-  mgfx::Rect bounds;
-
-  if (NeedToDrawShadow()) {
-    bounds = mgfx::Rect(dx, dy, dw, dh);
-    bounds = mTarget->GetTransform().TransformBounds(bounds);
-  }
-
-  AdjustedTarget(this, bounds.IsEmpty() ? nullptr : &bounds)->
-    DrawSurface(srcSurf,
-                mgfx::Rect(dx, dy, dw, dh),
-                mgfx::Rect(sx, sy, sw, sh),
-                DrawSurfaceOptions(filter),
-                DrawOptions(CurrentState().globalAlpha, UsedOperation()));
-
-  RedrawUser(gfxRect(dx, dy, dw, dh));
-}
-
-void
-CanvasRenderingContext2D::SetGlobalCompositeOperation(const nsAString& op,
-                                                      ErrorResult& error)
-{
-  CompositionOp comp_op;
-
-#define CANVAS_OP_TO_GFX_OP(cvsop, op2d) \
-  if (op.EqualsLiteral(cvsop))   \
-    comp_op = OP_##op2d;
-
-  CANVAS_OP_TO_GFX_OP("copy", SOURCE)
-  else CANVAS_OP_TO_GFX_OP("source-atop", ATOP)
-  else CANVAS_OP_TO_GFX_OP("source-in", IN)
-  else CANVAS_OP_TO_GFX_OP("source-out", OUT)
-  else CANVAS_OP_TO_GFX_OP("source-over", OVER)
-  else CANVAS_OP_TO_GFX_OP("destination-in", DEST_IN)
-  else CANVAS_OP_TO_GFX_OP("destination-out", DEST_OUT)
-  else CANVAS_OP_TO_GFX_OP("destination-over", DEST_OVER)
-  else CANVAS_OP_TO_GFX_OP("destination-atop", DEST_ATOP)
-  else CANVAS_OP_TO_GFX_OP("lighter", ADD)
-  else CANVAS_OP_TO_GFX_OP("xor", XOR)
-  // XXX ERRMSG we need to report an error to developers here! (bug 329026)
-  else return;
-
-#undef CANVAS_OP_TO_GFX_OP
-  CurrentState().op = comp_op;
-}
-
-void
-CanvasRenderingContext2D::GetGlobalCompositeOperation(nsAString& op,
-                                                      ErrorResult& error)
-{
-  CompositionOp comp_op = CurrentState().op;
-
-#define CANVAS_OP_TO_GFX_OP(cvsop, op2d) \
-  if (comp_op == OP_##op2d) \
-    op.AssignLiteral(cvsop);
-
-  CANVAS_OP_TO_GFX_OP("copy", SOURCE)
-  else CANVAS_OP_TO_GFX_OP("destination-atop", DEST_ATOP)
-  else CANVAS_OP_TO_GFX_OP("destination-in", DEST_IN)
-  else CANVAS_OP_TO_GFX_OP("destination-out", DEST_OUT)
-  else CANVAS_OP_TO_GFX_OP("destination-over", DEST_OVER)
-  else CANVAS_OP_TO_GFX_OP("lighter", ADD)
-  else CANVAS_OP_TO_GFX_OP("source-atop", ATOP)
-  else CANVAS_OP_TO_GFX_OP("source-in", IN)
-  else CANVAS_OP_TO_GFX_OP("source-out", OUT)
-  else CANVAS_OP_TO_GFX_OP("source-over", OVER)
-  else CANVAS_OP_TO_GFX_OP("xor", XOR)
-  else {
-    error.Throw(NS_ERROR_FAILURE);
-  }
-
-#undef CANVAS_OP_TO_GFX_OP
-}
-
-void
-CanvasRenderingContext2D::DrawWindow(nsIDOMWindow* window, double x,
-                                     double y, double w, double h,
-                                     const nsAString& bgColor,
-                                     uint32_t flags, ErrorResult& error)
-{
-  // protect against too-large surfaces that will cause allocation
-  // or overflow issues
-  if (!gfxASurface::CheckSurfaceSize(gfxIntSize(int32_t(w), int32_t(h)),
-                                     0xffff)) {
-    error.Throw(NS_ERROR_FAILURE);
-    return;
-  }
-
-  EnsureTarget();
-  // We can't allow web apps to call this until we fix at least the
-  // following potential security issues:
-  // -- rendering cross-domain IFRAMEs and then extracting the results
-  // -- rendering the user's theme and then extracting the results
-  // -- rendering native anonymous content (e.g., file input paths;
-  // scrollbars should be allowed)
-  if (!nsContentUtils::IsCallerChrome()) {
-    // not permitted to use DrawWindow
-    // XXX ERRMSG we need to report an error to developers here! (bug 329026)
-    error.Throw(NS_ERROR_DOM_SECURITY_ERR);
-    return;
-  }
-
-  // Flush layout updates
-  if (!(flags & nsIDOMCanvasRenderingContext2D::DRAWWINDOW_DO_NOT_FLUSH)) {
-    nsContentUtils::FlushLayoutForTree(window);
-  }
-
-  nsRefPtr<nsPresContext> presContext;
-  nsCOMPtr<nsPIDOMWindow> win = do_QueryInterface(window);
-  if (win) {
-    nsIDocShell* docshell = win->GetDocShell();
-    if (docshell) {
-      docshell->GetPresContext(getter_AddRefs(presContext));
-    }
-  }
-  if (!presContext) {
-    error.Throw(NS_ERROR_FAILURE);
-    return;
-  }
-
-  nscolor backgroundColor;
-  if (!ParseColor(bgColor, &backgroundColor)) {
-    error.Throw(NS_ERROR_FAILURE);
-    return;
-  }
-
-  nsRect r(nsPresContext::CSSPixelsToAppUnits((float)x),
-           nsPresContext::CSSPixelsToAppUnits((float)y),
-           nsPresContext::CSSPixelsToAppUnits((float)w),
-           nsPresContext::CSSPixelsToAppUnits((float)h));
-  uint32_t renderDocFlags = (nsIPresShell::RENDER_IGNORE_VIEWPORT_SCROLLING |
-                             nsIPresShell::RENDER_DOCUMENT_RELATIVE);
-  if (flags & nsIDOMCanvasRenderingContext2D::DRAWWINDOW_DRAW_CARET) {
-    renderDocFlags |= nsIPresShell::RENDER_CARET;
-  }
-  if (flags & nsIDOMCanvasRenderingContext2D::DRAWWINDOW_DRAW_VIEW) {
-    renderDocFlags &= ~(nsIPresShell::RENDER_IGNORE_VIEWPORT_SCROLLING |
-                        nsIPresShell::RENDER_DOCUMENT_RELATIVE);
-  }
-  if (flags & nsIDOMCanvasRenderingContext2D::DRAWWINDOW_USE_WIDGET_LAYERS) {
-    renderDocFlags |= nsIPresShell::RENDER_USE_WIDGET_LAYERS;
-  }
-  if (flags & nsIDOMCanvasRenderingContext2D::DRAWWINDOW_ASYNC_DECODE_IMAGES) {
-    renderDocFlags |= nsIPresShell::RENDER_ASYNC_DECODE_IMAGES;
-  }
-
-  // gfxContext-over-Azure may modify the DrawTarget's transform, so
-  // save and restore it
-  Matrix matrix = mTarget->GetTransform();
-  nsRefPtr<gfxContext> thebes;
-  if (gfxPlatform::GetPlatform()->SupportsAzureContent()) {
-    thebes = new gfxContext(mTarget);
-  } else {
-    nsRefPtr<gfxASurface> drawSurf;
-    GetThebesSurface(getter_AddRefs(drawSurf));
-    thebes = new gfxContext(drawSurf);
-  }
-  thebes->SetMatrix(gfxMatrix(matrix._11, matrix._12, matrix._21,
-                              matrix._22, matrix._31, matrix._32));
-  nsCOMPtr<nsIPresShell> shell = presContext->PresShell();
-  unused << shell->RenderDocument(r, renderDocFlags, backgroundColor, thebes);
-  mTarget->SetTransform(matrix);
-
-  // note that x and y are coordinates in the document that
-  // we're drawing; x and y are drawn to 0,0 in current user
-  // space.
-  RedrawUser(gfxRect(0, 0, w, h));
-}
-
-void
-CanvasRenderingContext2D::AsyncDrawXULElement(nsIDOMXULElement* elem,
-                                              double x, double y,
-                                              double w, double h,
-                                              const nsAString& bgColor,
-                                              uint32_t flags,
-                                              ErrorResult& error)
-{
-  // We can't allow web apps to call this until we fix at least the
-  // following potential security issues:
-  // -- rendering cross-domain IFRAMEs and then extracting the results
-  // -- rendering the user's theme and then extracting the results
-  // -- rendering native anonymous content (e.g., file input paths;
-  // scrollbars should be allowed)
-  if (!nsContentUtils::IsCallerChrome()) {
-    // not permitted to use DrawWindow
-    // XXX ERRMSG we need to report an error to developers here! (bug 329026)
-    error.Throw(NS_ERROR_DOM_SECURITY_ERR);
-    return;
-  }
-
-#if 0
-  nsCOMPtr<nsIFrameLoaderOwner> loaderOwner = do_QueryInterface(elem);
-  if (!loaderOwner) {
-    error.Throw(NS_ERROR_FAILURE);
-    return;
-  }
-
-  nsRefPtr<nsFrameLoader> frameloader = loaderOwner->GetFrameLoader();
-  if (!frameloader) {
-    error.Throw(NS_ERROR_FAILURE);
-    return;
-  }
-
-  PBrowserParent *child = frameloader->GetRemoteBrowser();
-  if (!child) {
-    nsCOMPtr<nsIDOMWindow> window =
-      do_GetInterface(frameloader->GetExistingDocShell());
-    if (!window) {
-      error.Throw(NS_ERROR_FAILURE);
-      return;
-    }
-
-    return DrawWindow(window, x, y, w, h, bgColor, flags);
-  }
-
-  // protect against too-large surfaces that will cause allocation
-  // or overflow issues
-  if (!gfxASurface::CheckSurfaceSize(gfxIntSize(w, h), 0xffff)) {
-    error.Throw(NS_ERROR_FAILURE);
-    return;
-  }
-
-  bool flush =
-    (flags & nsIDOMCanvasRenderingContext2D::DRAWWINDOW_DO_NOT_FLUSH) == 0;
-
-  uint32_t renderDocFlags = nsIPresShell::RENDER_IGNORE_VIEWPORT_SCROLLING;
-  if (flags & nsIDOMCanvasRenderingContext2D::DRAWWINDOW_DRAW_CARET) {
-    renderDocFlags |= nsIPresShell::RENDER_CARET;
-  }
-  if (flags & nsIDOMCanvasRenderingContext2D::DRAWWINDOW_DRAW_VIEW) {
-    renderDocFlags &= ~nsIPresShell::RENDER_IGNORE_VIEWPORT_SCROLLING;
-  }
-
-  nsRect rect(nsPresContext::CSSPixelsToAppUnits(x),
-              nsPresContext::CSSPixelsToAppUnits(y),
-              nsPresContext::CSSPixelsToAppUnits(w),
-              nsPresContext::CSSPixelsToAppUnits(h));
-  if (mIPC) {
-    PDocumentRendererParent *pdocrender =
-      child->SendPDocumentRendererConstructor(rect,
-                                              mThebes->CurrentMatrix(),
-                                              nsString(aBGColor),
-                                              renderDocFlags, flush,
-                                              nsIntSize(mWidth, mHeight));
-    if (!pdocrender)
-      return NS_ERROR_FAILURE;
-
-    DocumentRendererParent *docrender =
-      static_cast<DocumentRendererParent *>(pdocrender);
-
-    docrender->SetCanvasContext(this, mThebes);
-  }
-#endif
-}
-
-//
-// device pixel getting/setting
-//
-
-void
-CanvasRenderingContext2D::EnsureUnpremultiplyTable() {
-  if (sUnpremultiplyTable)
-    return;
-
-  // Infallably alloc the unpremultiply table.
-  sUnpremultiplyTable = new uint8_t[256][256];
-
-  // It's important that the array be indexed first by alpha and then by rgb
-  // value.  When we unpremultiply a pixel, we're guaranteed to do three
-  // lookups with the same alpha; indexing by alpha first makes it likely that
-  // those three lookups will be close to one another in memory, thus
-  // increasing the chance of a cache hit.
-
-  // a == 0 case
-  for (uint32_t c = 0; c <= 255; c++) {
-    sUnpremultiplyTable[0][c] = c;
-  }
-
-  for (int a = 1; a <= 255; a++) {
-    for (int c = 0; c <= 255; c++) {
-      sUnpremultiplyTable[a][c] = (uint8_t)((c * 255) / a);
-    }
-  }
-}
-
-
-already_AddRefed<ImageData>
-CanvasRenderingContext2D::GetImageData(JSContext* aCx, double aSx,
-                                       double aSy, double aSw,
-                                       double aSh, ErrorResult& error)
-{
-  EnsureTarget();
-  if (!IsTargetValid()) {
-    error.Throw(NS_ERROR_FAILURE);
-    return NULL;
-  }
-
-  if (!mCanvasElement && !mDocShell) {
-    NS_ERROR("No canvas element and no docshell in GetImageData!!!");
-    error.Throw(NS_ERROR_DOM_SECURITY_ERR);
-    return NULL;
-  }
-
-  // Check only if we have a canvas element; if we were created with a docshell,
-  // then it's special internal use.
-  if (mCanvasElement && mCanvasElement->IsWriteOnly() &&
-      !nsContentUtils::IsCallerChrome())
-  {
-    // XXX ERRMSG we need to report an error to developers here! (bug 329026)
-    error.Throw(NS_ERROR_DOM_SECURITY_ERR);
-    return NULL;
-  }
-
-  if (!NS_finite(aSx) || !NS_finite(aSy) ||
-      !NS_finite(aSw) || !NS_finite(aSh)) {
-    error.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
-    return NULL;
-  }
-
-  if (!aSw || !aSh) {
-    error.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
-    return NULL;
-  }
-
-  int32_t x = JS_DoubleToInt32(aSx);
-  int32_t y = JS_DoubleToInt32(aSy);
-  int32_t wi = JS_DoubleToInt32(aSw);
-  int32_t hi = JS_DoubleToInt32(aSh);
-
-  // Handle negative width and height by flipping the rectangle over in the
-  // relevant direction.
-  uint32_t w, h;
-  if (aSw < 0) {
-    w = -wi;
-    x -= w;
-  } else {
-    w = wi;
-  }
-  if (aSh < 0) {
-    h = -hi;
-    y -= h;
-  } else {
-    h = hi;
-  }
-
-  if (w == 0) {
-    w = 1;
-  }
-  if (h == 0) {
-    h = 1;
-  }
-
-  JSObject* array;
-  error = GetImageDataArray(aCx, x, y, w, h, &array);
-  if (error.Failed()) {
-    return NULL;
-  }
-  MOZ_ASSERT(array);
-
-  nsRefPtr<ImageData> imageData = new ImageData(w, h, *array);
-  return imageData.forget();
-}
-
-nsresult
-CanvasRenderingContext2D::GetImageDataArray(JSContext* aCx,
-                                            int32_t aX,
-                                            int32_t aY,
-                                            uint32_t aWidth,
-                                            uint32_t aHeight,
-                                            JSObject** aRetval)
-{
-  MOZ_ASSERT(aWidth && aHeight);
-
-  CheckedInt<uint32_t> len = CheckedInt<uint32_t>(aWidth) * aHeight * 4;
-  if (!len.isValid()) {
-    return NS_ERROR_DOM_INDEX_SIZE_ERR;
-  }
-
-  CheckedInt<int32_t> rightMost = CheckedInt<int32_t>(aX) + aWidth;
-  CheckedInt<int32_t> bottomMost = CheckedInt<int32_t>(aY) + aHeight;
-
-  if (!rightMost.isValid() || !bottomMost.isValid()) {
-    return NS_ERROR_DOM_SYNTAX_ERR;
-  }
-
-  JSObject* darray = JS_NewUint8ClampedArray(aCx, len.value());
-  if (!darray) {
-    return NS_ERROR_OUT_OF_MEMORY;
-  }
-
-  if (mZero) {
-    *aRetval = darray;
-    return NS_OK;
-  }
-
-  uint8_t* data = JS_GetUint8ClampedArrayData(darray);
-
-  IntRect srcRect(0, 0, mWidth, mHeight);
-  IntRect destRect(aX, aY, aWidth, aHeight);
-
-  IntRect srcReadRect = srcRect.Intersect(destRect);
-  IntRect dstWriteRect = srcReadRect;
-  dstWriteRect.MoveBy(-aX, -aY);
-
-  uint8_t* src = data;
-  uint32_t srcStride = aWidth * 4;
-
-  RefPtr<DataSourceSurface> readback;
-  if (!srcReadRect.IsEmpty()) {
-    RefPtr<SourceSurface> snapshot = mTarget->Snapshot();
-    if (snapshot) {
-      readback = snapshot->GetDataSurface();
-
-      srcStride = readback->Stride();
-      src = readback->GetData() + srcReadRect.y * srcStride + srcReadRect.x * 4;
-    }
-  }
-
-  // make sure sUnpremultiplyTable has been created
-  EnsureUnpremultiplyTable();
-
-  // NOTE! dst is the same as src, and this relies on reading
-  // from src and advancing that ptr before writing to dst.
-  // NOTE! I'm not sure that it is, I think this comment might have been
-  // inherited from Thebes canvas and is no longer true
-  uint8_t* dst = data + dstWriteRect.y * (aWidth * 4) + dstWriteRect.x * 4;
-
-  for (int32_t j = 0; j < dstWriteRect.height; ++j) {
-    for (int32_t i = 0; i < dstWriteRect.width; ++i) {
-      // XXX Is there some useful swizzle MMX we can use here?
-#ifdef IS_LITTLE_ENDIAN
-      uint8_t b = *src++;
-      uint8_t g = *src++;
-      uint8_t r = *src++;
-      uint8_t a = *src++;
-#else
-      uint8_t a = *src++;
-      uint8_t r = *src++;
-      uint8_t g = *src++;
-      uint8_t b = *src++;
-#endif
-      // Convert to non-premultiplied color
-      *dst++ = sUnpremultiplyTable[a][r];
-      *dst++ = sUnpremultiplyTable[a][g];
-      *dst++ = sUnpremultiplyTable[a][b];
-      *dst++ = a;
-    }
-    src += srcStride - (dstWriteRect.width * 4);
-    dst += (aWidth * 4) - (dstWriteRect.width * 4);
-  }
-
-  *aRetval = darray;
-  return NS_OK;
-}
-
-void
-CanvasRenderingContext2D::EnsurePremultiplyTable() {
-  if (sPremultiplyTable)
-    return;
-
-  // Infallably alloc the premultiply table.
-  sPremultiplyTable = new uint8_t[256][256];
-
-  // Like the unpremultiply table, it's important that we index the premultiply
-  // table with the alpha value as the first index to ensure good cache
-  // performance.
-
-  for (int a = 0; a <= 255; a++) {
-    for (int c = 0; c <= 255; c++) {
-      sPremultiplyTable[a][c] = (a * c + 254) / 255;
-    }
-  }
-}
-
-void
-CanvasRenderingContext2D::EnsureErrorTarget()
-{
-  if (sErrorTarget) {
-    return;
-  }
-
-  RefPtr<DrawTarget> errorTarget = gfxPlatform::GetPlatform()->CreateOffscreenDrawTarget(IntSize(1, 1), FORMAT_B8G8R8A8);
-  NS_ABORT_IF_FALSE(errorTarget, "Failed to allocate the error target!");
-
-  sErrorTarget = errorTarget;
-  NS_ADDREF(sErrorTarget);
-}
-
-void
-CanvasRenderingContext2D::FillRuleChanged()
-{
-  if (mPath) {
-    mPathBuilder = mPath->CopyToBuilder(CurrentState().fillRule);
-    mPath = nullptr;
-  }
-}
-
-void
-CanvasRenderingContext2D::PutImageData(JSContext* cx,
-                                       ImageData& imageData, double dx,
-                                       double dy, ErrorResult& error)
-{
-  if (!FloatValidate(dx, dy)) {
-    error.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
-    return;
-  }
-
-  dom::Uint8ClampedArray arr(imageData.GetDataObject());
-
-  error = PutImageData_explicit(JS_DoubleToInt32(dx), JS_DoubleToInt32(dy),
-                                imageData.Width(), imageData.Height(),
-                                arr.Data(), arr.Length(), false, 0, 0, 0, 0);
-}
-
-void
-CanvasRenderingContext2D::PutImageData(JSContext* cx,
-                                       ImageData& imageData, double dx,
-                                       double dy, double dirtyX,
-                                       double dirtyY, double dirtyWidth,
-                                       double dirtyHeight,
-                                       ErrorResult& error)
-{
-  if (!FloatValidate(dx, dy, dirtyX, dirtyY, dirtyWidth, dirtyHeight)) {
-    error.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
-    return;
-  }
-
-  dom::Uint8ClampedArray arr(imageData.GetDataObject());
-
-  error = PutImageData_explicit(JS_DoubleToInt32(dx), JS_DoubleToInt32(dy),
-                                imageData.Width(), imageData.Height(),
-                                arr.Data(), arr.Length(), true,
-                                JS_DoubleToInt32(dirtyX),
-                                JS_DoubleToInt32(dirtyY),
-                                JS_DoubleToInt32(dirtyWidth),
-                                JS_DoubleToInt32(dirtyHeight));
-}
-
-// void putImageData (in ImageData d, in float x, in float y);
-// void putImageData (in ImageData d, in double x, in double y, in double dirtyX, in double dirtyY, in double dirtyWidth, in double dirtyHeight);
-
-nsresult
-CanvasRenderingContext2D::PutImageData_explicit(int32_t x, int32_t y, uint32_t w, uint32_t h,
-                                                unsigned char *aData, uint32_t aDataLen,
-                                                bool hasDirtyRect, int32_t dirtyX, int32_t dirtyY,
-                                                int32_t dirtyWidth, int32_t dirtyHeight)
-{
-  if (w == 0 || h == 0) {
-    return NS_ERROR_DOM_SYNTAX_ERR;
-  }
-
-  IntRect dirtyRect;
-  IntRect imageDataRect(0, 0, w, h);
-
-  if (hasDirtyRect) {
-    // fix up negative dimensions
-    if (dirtyWidth < 0) {
-      NS_ENSURE_TRUE(dirtyWidth != INT_MIN, NS_ERROR_DOM_INDEX_SIZE_ERR);
-
-      CheckedInt32 checkedDirtyX = CheckedInt32(dirtyX) + dirtyWidth;
-
-      if (!checkedDirtyX.isValid())
-        return NS_ERROR_DOM_INDEX_SIZE_ERR;
-
-      dirtyX = checkedDirtyX.value();
-      dirtyWidth = -dirtyWidth;
-    }
-
-    if (dirtyHeight < 0) {
-      NS_ENSURE_TRUE(dirtyHeight != INT_MIN, NS_ERROR_DOM_INDEX_SIZE_ERR);
-
-      CheckedInt32 checkedDirtyY = CheckedInt32(dirtyY) + dirtyHeight;
-
-      if (!checkedDirtyY.isValid())
-        return NS_ERROR_DOM_INDEX_SIZE_ERR;
-
-      dirtyY = checkedDirtyY.value();
-      dirtyHeight = -dirtyHeight;
-    }
-
-    // bound the dirty rect within the imageData rectangle
-    dirtyRect = imageDataRect.Intersect(IntRect(dirtyX, dirtyY, dirtyWidth, dirtyHeight));
-
-    if (dirtyRect.Width() <= 0 || dirtyRect.Height() <= 0)
-      return NS_OK;
-  } else {
-    dirtyRect = imageDataRect;
-  }
-
-  dirtyRect.MoveBy(IntPoint(x, y));
-  dirtyRect = IntRect(0, 0, mWidth, mHeight).Intersect(dirtyRect);
-
-  if (dirtyRect.Width() <= 0 || dirtyRect.Height() <= 0) {
-    return NS_OK;
-  }
-
-  uint32_t len = w * h * 4;
-  if (aDataLen != len) {
-    return NS_ERROR_DOM_SYNTAX_ERR;
-  }
-
-  nsRefPtr<gfxImageSurface> imgsurf = new gfxImageSurface(gfxIntSize(w, h),
-                                                          gfxASurface::ImageFormatARGB32,
-                                                          false);
-  if (!imgsurf || imgsurf->CairoStatus()) {
-    return NS_ERROR_FAILURE;
-  }
-
-  // ensure premultiply table has been created
-  EnsurePremultiplyTable();
-
-  uint8_t *src = aData;
-  uint8_t *dst = imgsurf->Data();
-
-  for (uint32_t j = 0; j < h; j++) {
-    for (uint32_t i = 0; i < w; i++) {
-      uint8_t r = *src++;
-      uint8_t g = *src++;
-      uint8_t b = *src++;
-      uint8_t a = *src++;
-
-      // Convert to premultiplied color (losslessly if the input came from getImageData)
-#ifdef IS_LITTLE_ENDIAN
-      *dst++ = sPremultiplyTable[a][b];
-      *dst++ = sPremultiplyTable[a][g];
-      *dst++ = sPremultiplyTable[a][r];
-      *dst++ = a;
-#else
-      *dst++ = a;
-      *dst++ = sPremultiplyTable[a][r];
-      *dst++ = sPremultiplyTable[a][g];
-      *dst++ = sPremultiplyTable[a][b];
-#endif
-    }
-  }
-
-  EnsureTarget();
-  if (!IsTargetValid()) {
-    return NS_ERROR_FAILURE;
-  }
-
-  RefPtr<SourceSurface> sourceSurface =
-    mTarget->CreateSourceSurfaceFromData(imgsurf->Data(), IntSize(w, h), imgsurf->Stride(), FORMAT_B8G8R8A8);
-
-
-  mTarget->CopySurface(sourceSurface,
-                       IntRect(dirtyRect.x - x, dirtyRect.y - y,
-                               dirtyRect.width, dirtyRect.height),
-                       IntPoint(dirtyRect.x, dirtyRect.y));
-
-  Redraw(mgfx::Rect(dirtyRect.x, dirtyRect.y, dirtyRect.width, dirtyRect.height));
-
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-CanvasRenderingContext2D::GetThebesSurface(gfxASurface **surface)
-{
-  EnsureTarget();
-  if (!mThebesSurface) {
-    mThebesSurface =
-      gfxPlatform::GetPlatform()->GetThebesSurfaceForDrawTarget(mTarget);
-
-    if (!mThebesSurface) {
-      return NS_ERROR_FAILURE;
-    }
-  } else {
-    // Normally GetThebesSurfaceForDrawTarget will handle the flush, when
-    // we're returning a cached ThebesSurface we need to flush here.
-    mTarget->Flush();
-  }
-
-  *surface = mThebesSurface;
-  NS_ADDREF(*surface);
-
-  return NS_OK;
-}
-
-static already_AddRefed<ImageData>
-CreateImageData(JSContext* cx, CanvasRenderingContext2D* context,
-                uint32_t w, uint32_t h, ErrorResult& error)
-{
-  if (w == 0)
-      w = 1;
-  if (h == 0)
-      h = 1;
-
-  CheckedInt<uint32_t> len = CheckedInt<uint32_t>(w) * h * 4;
-  if (!len.isValid()) {
-    error.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
-    return NULL;
-  }
-
-  // Create the fast typed array; it's initialized to 0 by default.
-  JSObject* darray = Uint8ClampedArray::Create(cx, context, len.value());
-  if (!darray) {
-    error.Throw(NS_ERROR_OUT_OF_MEMORY);
-    return NULL;
-  }
-
-  nsRefPtr<mozilla::dom::ImageData> imageData =
-    new mozilla::dom::ImageData(w, h, *darray);
-  return imageData.forget();
-}
-
-already_AddRefed<ImageData>
-CanvasRenderingContext2D::CreateImageData(JSContext* cx, double sw,
-                                          double sh, ErrorResult& error)
-{
-  if (!FloatValidate(sw, sh)) {
-    error.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
-    return NULL;
-  }
-
-  if (!sw || !sh) {
-    error.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
-    return NULL;
-  }
-
-  int32_t wi = JS_DoubleToInt32(sw);
-  int32_t hi = JS_DoubleToInt32(sh);
-
-  uint32_t w = NS_ABS(wi);
-  uint32_t h = NS_ABS(hi);
-  return mozilla::dom::CreateImageData(cx, this, w, h, error);
-}
-
-already_AddRefed<ImageData>
-CanvasRenderingContext2D::CreateImageData(JSContext* cx,
-                                          ImageData& imagedata,
-                                          ErrorResult& error)
-{
-  return mozilla::dom::CreateImageData(cx, this, imagedata.Width(),
-                                       imagedata.Height(), error);
-}
-
-static uint8_t g2DContextLayerUserData;
-
-already_AddRefed<CanvasLayer>
-CanvasRenderingContext2D::GetCanvasLayer(nsDisplayListBuilder* aBuilder,
-                                         CanvasLayer *aOldLayer,
-                                         LayerManager *aManager)
-{
-  // Don't call EnsureTarget() ... if there isn't already a surface, then
-  // we have nothing to paint and there is no need to create a surface just
-  // to paint nothing. Also, EnsureTarget() can cause creation of a persistent
-  // layer manager which must NOT happen during a paint.
-  if (!mTarget || !IsTargetValid()) {
-    // No DidTransactionCallback will be received, so mark the context clean
-    // now so future invalidations will be dispatched.
-    MarkContextClean();
-    return nullptr;
-  }
-
-  mTarget->Flush();
-
-  if (!mResetLayer && aOldLayer) {
-    CanvasRenderingContext2DUserData* userData =
-      static_cast<CanvasRenderingContext2DUserData*>(
-        aOldLayer->GetUserData(&g2DContextLayerUserData));
-    if (userData && userData->IsForContext(this)) {
-      NS_ADDREF(aOldLayer);
-      return aOldLayer;
-    }
-  }
-
-  nsRefPtr<CanvasLayer> canvasLayer = aManager->CreateCanvasLayer();
-  if (!canvasLayer) {
-    NS_WARNING("CreateCanvasLayer returned null!");
-    // No DidTransactionCallback will be received, so mark the context clean
-    // now so future invalidations will be dispatched.
-    MarkContextClean();
-    return nullptr;
-  }
-  CanvasRenderingContext2DUserData *userData = nullptr;
-  // Make the layer tell us whenever a transaction finishes (including
-  // the current transaction), so we can clear our invalidation state and
-  // start invalidating again. We need to do this for all layers since
-  // callers of DrawWindow may be expecting to receive normal invalidation
-  // notifications after this paint.
-
-  // The layer will be destroyed when we tear down the presentation
-  // (at the latest), at which time this userData will be destroyed,
-  // releasing the reference to the element.
-  // The userData will receive DidTransactionCallbacks, which flush the
-  // the invalidation state to indicate that the canvas is up to date.
-  userData = new CanvasRenderingContext2DUserData(this);
-  canvasLayer->SetDidTransactionCallback(
-          CanvasRenderingContext2DUserData::DidTransactionCallback, userData);
-  canvasLayer->SetUserData(&g2DContextLayerUserData, userData);
-
-  CanvasLayer::Data data;
-
-  data.mDrawTarget = mTarget;
-  data.mSize = nsIntSize(mWidth, mHeight);
-
-  canvasLayer->Initialize(data);
-  uint32_t flags = mOpaque ? Layer::CONTENT_OPAQUE : 0;
-  canvasLayer->SetContentFlags(flags);
-  canvasLayer->Updated();
-
-  mResetLayer = false;
-
-  return canvasLayer.forget();
-}
-
-void
-CanvasRenderingContext2D::MarkContextClean()
-{
-  if (mInvalidateCount > 0) {
-    mPredictManyRedrawCalls = mInvalidateCount > kCanvasMaxInvalidateCount;
-  }
-  mIsEntireFrameInvalid = false;
-  mInvalidateCount = 0;
-}
-
-
-bool
-CanvasRenderingContext2D::ShouldForceInactiveLayer(LayerManager *aManager)
-{
-  return !aManager->CanUseCanvasLayerForSize(gfxIntSize(mWidth, mHeight));
-}
-
-}
-}
-
-DOMCI_DATA(TextMetrics, mozilla::dom::TextMetrics)
-DOMCI_DATA(CanvasGradient, mozilla::dom::CanvasGradient)
-DOMCI_DATA(CanvasPattern, mozilla::dom::CanvasPattern)
-DOMCI_DATA(CanvasRenderingContext2D, mozilla::dom::CanvasRenderingContext2D)
-
+  }
+}
+
+bool
+CanvasRenderingContext2D::IsPointInPath(double x, double y)
+{
+  if (!FloatValidate(x,y)) {
+    return false;
+  }
+
+  EnsureUserSpacePath(false);
+  if (!mPath) {
+    return false;
+  }
+  if (mPathTransformWillUpdate) {
+    return mPath->ContainsPoint(Point(x, y), mPathToDS);
+  }
+  return mPath->ContainsPoint(Point(x, y), mTarget->GetTransform());
+}
+
+bool
+CanvasRenderingContext2D::MozIsPointInStroke(double x, double y)
+{
+  if (!FloatValidate(x,y)) {
+    return false;
+  }
+
+  EnsureUserSpacePath(false);
+  if (!mPath) {
+    return false;
+  }
+
+  const ContextState &state = CurrentState();
+
+  StrokeOptions strokeOptions(state.lineWidth,
+                              state.lineJoin,
+                              state.lineCap,
+                              state.miterLimit,
+                              state.dash.Length(),
+                              state.dash.Elements(),
+                              state.dashOffset);
+
+  if (mPathTransformWillUpdate) {
+    return mPath->StrokeContainsPoint(strokeOptions, Point(x, y), mPathToDS);
+  }
+  return mPath->StrokeContainsPoint(strokeOptions, Point(x, y), mTarget->GetTransform());
+}
+
+//
+// image
+//
+
+// drawImage(in HTMLImageElement image, in float dx, in float dy);
+//   -- render image from 0,0 at dx,dy top-left coords
+// drawImage(in HTMLImageElement image, in float dx, in float dy, in float sw, in float sh);
+//   -- render image from 0,0 at dx,dy top-left coords clipping it to sw,sh
+// drawImage(in HTMLImageElement image, in float sx, in float sy, in float sw, in float sh, in float dx, in float dy, in float dw, in float dh);
+//   -- render the region defined by (sx,sy,sw,wh) in image-local space into the region (dx,dy,dw,dh) on the canvas
+
+// If only dx and dy are passed in then optional_argc should be 0. If only
+// dx, dy, dw and dh are passed in then optional_argc should be 2. The only
+// other valid value for optional_argc is 6 if sx, sy, sw, sh, dx, dy, dw and dh
+// are all passed in.
+
+void
+CanvasRenderingContext2D::DrawImage(const HTMLImageOrCanvasOrVideoElement& image,
+                                    double sx, double sy, double sw,
+                                    double sh, double dx, double dy,
+                                    double dw, double dh,
+                                    uint8_t optional_argc,
+                                    ErrorResult& error)
+{
+  MOZ_ASSERT(optional_argc == 0 || optional_argc == 2 || optional_argc == 6);
+
+  RefPtr<SourceSurface> srcSurf;
+  gfxIntSize imgSize;
+
+  Element* element;
+
+  EnsureTarget();
+  if (image.IsHTMLCanvasElement()) {
+    nsHTMLCanvasElement* canvas = image.GetAsHTMLCanvasElement();
+    element = canvas;
+    nsIntSize size = canvas->GetSize();
+    if (size.width == 0 || size.height == 0) {
+      error.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
+      return;
+    }
+
+    // Special case for Canvas, which could be an Azure canvas!
+    nsICanvasRenderingContextInternal *srcCanvas = canvas->GetContextAtIndex(0);
+    if (srcCanvas == this) {
+      // Self-copy.
+      srcSurf = mTarget->Snapshot();
+      imgSize = gfxIntSize(mWidth, mHeight);
+    } else if (srcCanvas) {
+      // This might not be an Azure canvas!
+      srcSurf = srcCanvas->GetSurfaceSnapshot();
+
+      if (srcSurf) {
+        if (mCanvasElement) {
+          // Do security check here.
+          CanvasUtils::DoDrawImageSecurityCheck(mCanvasElement,
+                                                element->NodePrincipal(),
+                                                canvas->IsWriteOnly(),
+                                                false);
+        }
+        imgSize = gfxIntSize(srcSurf->GetSize().width, srcSurf->GetSize().height);
+      }
+    }
+  } else {
+    if (image.IsHTMLImageElement()) {
+      nsHTMLImageElement* img = image.GetAsHTMLImageElement();
+      element = img;
+    } else {
+      nsHTMLVideoElement* video = image.GetAsHTMLVideoElement();
+      element = video;
+    }
+
+    gfxASurface* imgsurf =
+      CanvasImageCache::Lookup(element, mCanvasElement, &imgSize);
+    if (imgsurf) {
+      srcSurf = gfxPlatform::GetPlatform()->GetSourceSurfaceForSurface(mTarget, imgsurf);
+    }
+  }
+
+  if (!srcSurf) {
+    // The canvas spec says that drawImage should draw the first frame
+    // of animated images
+    uint32_t sfeFlags = nsLayoutUtils::SFE_WANT_FIRST_FRAME;
+    nsLayoutUtils::SurfaceFromElementResult res =
+      nsLayoutUtils::SurfaceFromElement(element, sfeFlags);
+
+    if (!res.mSurface) {
+      // Spec says to silently do nothing if the element is still loading.
+      if (!res.mIsStillLoading) {
+        error.Throw(NS_ERROR_NOT_AVAILABLE);
+      }
+      return;
+    }
+
+    // Ignore cairo surfaces that are bad! See bug 666312.
+    if (res.mSurface->CairoStatus()) {
+      return;
+    }
+
+    imgSize = res.mSize;
+
+    if (mCanvasElement) {
+      CanvasUtils::DoDrawImageSecurityCheck(mCanvasElement,
+                                            res.mPrincipal, res.mIsWriteOnly,
+                                            res.mCORSUsed);
+    }
+
+    if (res.mImageRequest) {
+      CanvasImageCache::NotifyDrawImage(element, mCanvasElement,
+                                        res.mImageRequest, res.mSurface, imgSize);
+    }
+
+    srcSurf = gfxPlatform::GetPlatform()->GetSourceSurfaceForSurface(mTarget, res.mSurface);
+  }
+
+  if (optional_argc == 0) {
+    sx = sy = 0.0;
+    dw = sw = (double) imgSize.width;
+    dh = sh = (double) imgSize.height;
+  } else if (optional_argc == 2) {
+    sx = sy = 0.0;
+    sw = (double) imgSize.width;
+    sh = (double) imgSize.height;
+  }
+
+  if (sw == 0.0 || sh == 0.0) {
+    error.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
+    return;
+  }
+
+  if (dw == 0.0 || dh == 0.0) {
+    // not really failure, but nothing to do --
+    // and noone likes a divide-by-zero
+    return;
+  }
+
+  if (sx < 0.0 || sy < 0.0 ||
+      sw < 0.0 || sw > (double) imgSize.width ||
+      sh < 0.0 || sh > (double) imgSize.height ||
+      dw < 0.0 || dh < 0.0) {
+    // XXX - Unresolved spec issues here, for now return error.
+    error.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
+    return;
+  }
+
+  Filter filter;
+
+  if (CurrentState().imageSmoothingEnabled)
+    filter = mgfx::FILTER_LINEAR;
+  else
+    filter = mgfx::FILTER_POINT;
+
+  mgfx::Rect bounds;
+
+  if (NeedToDrawShadow()) {
+    bounds = mgfx::Rect(dx, dy, dw, dh);
+    bounds = mTarget->GetTransform().TransformBounds(bounds);
+  }
+
+  AdjustedTarget(this, bounds.IsEmpty() ? nullptr : &bounds)->
+    DrawSurface(srcSurf,
+                mgfx::Rect(dx, dy, dw, dh),
+                mgfx::Rect(sx, sy, sw, sh),
+                DrawSurfaceOptions(filter),
+                DrawOptions(CurrentState().globalAlpha, UsedOperation()));
+
+  RedrawUser(gfxRect(dx, dy, dw, dh));
+}
+
+void
+CanvasRenderingContext2D::SetGlobalCompositeOperation(const nsAString& op,
+                                                      ErrorResult& error)
+{
+  CompositionOp comp_op;
+
+#define CANVAS_OP_TO_GFX_OP(cvsop, op2d) \
+  if (op.EqualsLiteral(cvsop))   \
+    comp_op = OP_##op2d;
+
+  CANVAS_OP_TO_GFX_OP("copy", SOURCE)
+  else CANVAS_OP_TO_GFX_OP("source-atop", ATOP)
+  else CANVAS_OP_TO_GFX_OP("source-in", IN)
+  else CANVAS_OP_TO_GFX_OP("source-out", OUT)
+  else CANVAS_OP_TO_GFX_OP("source-over", OVER)
+  else CANVAS_OP_TO_GFX_OP("destination-in", DEST_IN)
+  else CANVAS_OP_TO_GFX_OP("destination-out", DEST_OUT)
+  else CANVAS_OP_TO_GFX_OP("destination-over", DEST_OVER)
+  else CANVAS_OP_TO_GFX_OP("destination-atop", DEST_ATOP)
+  else CANVAS_OP_TO_GFX_OP("lighter", ADD)
+  else CANVAS_OP_TO_GFX_OP("xor", XOR)
+  // XXX ERRMSG we need to report an error to developers here! (bug 329026)
+  else return;
+
+#undef CANVAS_OP_TO_GFX_OP
+  CurrentState().op = comp_op;
+}
+
+void
+CanvasRenderingContext2D::GetGlobalCompositeOperation(nsAString& op,
+                                                      ErrorResult& error)
+{
+  CompositionOp comp_op = CurrentState().op;
+
+#define CANVAS_OP_TO_GFX_OP(cvsop, op2d) \
+  if (comp_op == OP_##op2d) \
+    op.AssignLiteral(cvsop);
+
+  CANVAS_OP_TO_GFX_OP("copy", SOURCE)
+  else CANVAS_OP_TO_GFX_OP("destination-atop", DEST_ATOP)
+  else CANVAS_OP_TO_GFX_OP("destination-in", DEST_IN)
+  else CANVAS_OP_TO_GFX_OP("destination-out", DEST_OUT)
+  else CANVAS_OP_TO_GFX_OP("destination-over", DEST_OVER)
+  else CANVAS_OP_TO_GFX_OP("lighter", ADD)
+  else CANVAS_OP_TO_GFX_OP("source-atop", ATOP)
+  else CANVAS_OP_TO_GFX_OP("source-in", IN)
+  else CANVAS_OP_TO_GFX_OP("source-out", OUT)
+  else CANVAS_OP_TO_GFX_OP("source-over", OVER)
+  else CANVAS_OP_TO_GFX_OP("xor", XOR)
+  else {
+    error.Throw(NS_ERROR_FAILURE);
+  }
+
+#undef CANVAS_OP_TO_GFX_OP
+}
+
+void
+CanvasRenderingContext2D::DrawWindow(nsIDOMWindow* window, double x,
+                                     double y, double w, double h,
+                                     const nsAString& bgColor,
+                                     uint32_t flags, ErrorResult& error)
+{
+  // protect against too-large surfaces that will cause allocation
+  // or overflow issues
+  if (!gfxASurface::CheckSurfaceSize(gfxIntSize(int32_t(w), int32_t(h)),
+                                     0xffff)) {
+    error.Throw(NS_ERROR_FAILURE);
+    return;
+  }
+
+  EnsureTarget();
+  // We can't allow web apps to call this until we fix at least the
+  // following potential security issues:
+  // -- rendering cross-domain IFRAMEs and then extracting the results
+  // -- rendering the user's theme and then extracting the results
+  // -- rendering native anonymous content (e.g., file input paths;
+  // scrollbars should be allowed)
+  if (!nsContentUtils::IsCallerChrome()) {
+    // not permitted to use DrawWindow
+    // XXX ERRMSG we need to report an error to developers here! (bug 329026)
+    error.Throw(NS_ERROR_DOM_SECURITY_ERR);
+    return;
+  }
+
+  // Flush layout updates
+  if (!(flags & nsIDOMCanvasRenderingContext2D::DRAWWINDOW_DO_NOT_FLUSH)) {
+    nsContentUtils::FlushLayoutForTree(window);
+  }
+
+  nsRefPtr<nsPresContext> presContext;
+  nsCOMPtr<nsPIDOMWindow> win = do_QueryInterface(window);
+  if (win) {
+    nsIDocShell* docshell = win->GetDocShell();
+    if (docshell) {
+      docshell->GetPresContext(getter_AddRefs(presContext));
+    }
+  }
+  if (!presContext) {
+    error.Throw(NS_ERROR_FAILURE);
+    return;
+  }
+
+  nscolor backgroundColor;
+  if (!ParseColor(bgColor, &backgroundColor)) {
+    error.Throw(NS_ERROR_FAILURE);
+    return;
+  }
+
+  nsRect r(nsPresContext::CSSPixelsToAppUnits((float)x),
+           nsPresContext::CSSPixelsToAppUnits((float)y),
+           nsPresContext::CSSPixelsToAppUnits((float)w),
+           nsPresContext::CSSPixelsToAppUnits((float)h));
+  uint32_t renderDocFlags = (nsIPresShell::RENDER_IGNORE_VIEWPORT_SCROLLING |
+                             nsIPresShell::RENDER_DOCUMENT_RELATIVE);
+  if (flags & nsIDOMCanvasRenderingContext2D::DRAWWINDOW_DRAW_CARET) {
+    renderDocFlags |= nsIPresShell::RENDER_CARET;
+  }
+  if (flags & nsIDOMCanvasRenderingContext2D::DRAWWINDOW_DRAW_VIEW) {
+    renderDocFlags &= ~(nsIPresShell::RENDER_IGNORE_VIEWPORT_SCROLLING |
+                        nsIPresShell::RENDER_DOCUMENT_RELATIVE);
+  }
+  if (flags & nsIDOMCanvasRenderingContext2D::DRAWWINDOW_USE_WIDGET_LAYERS) {
+    renderDocFlags |= nsIPresShell::RENDER_USE_WIDGET_LAYERS;
+  }
+  if (flags & nsIDOMCanvasRenderingContext2D::DRAWWINDOW_ASYNC_DECODE_IMAGES) {
+    renderDocFlags |= nsIPresShell::RENDER_ASYNC_DECODE_IMAGES;
+  }
+
+  // gfxContext-over-Azure may modify the DrawTarget's transform, so
+  // save and restore it
+  Matrix matrix = mTarget->GetTransform();
+  nsRefPtr<gfxContext> thebes;
+  if (gfxPlatform::GetPlatform()->SupportsAzureContent()) {
+    thebes = new gfxContext(mTarget);
+  } else {
+    nsRefPtr<gfxASurface> drawSurf;
+    GetThebesSurface(getter_AddRefs(drawSurf));
+    thebes = new gfxContext(drawSurf);
+  }
+  thebes->SetMatrix(gfxMatrix(matrix._11, matrix._12, matrix._21,
+                              matrix._22, matrix._31, matrix._32));
+  nsCOMPtr<nsIPresShell> shell = presContext->PresShell();
+  unused << shell->RenderDocument(r, renderDocFlags, backgroundColor, thebes);
+  mTarget->SetTransform(matrix);
+
+  // note that x and y are coordinates in the document that
+  // we're drawing; x and y are drawn to 0,0 in current user
+  // space.
+  RedrawUser(gfxRect(0, 0, w, h));
+}
+
+void
+CanvasRenderingContext2D::AsyncDrawXULElement(nsIDOMXULElement* elem,
+                                              double x, double y,
+                                              double w, double h,
+                                              const nsAString& bgColor,
+                                              uint32_t flags,
+                                              ErrorResult& error)
+{
+  // We can't allow web apps to call this until we fix at least the
+  // following potential security issues:
+  // -- rendering cross-domain IFRAMEs and then extracting the results
+  // -- rendering the user's theme and then extracting the results
+  // -- rendering native anonymous content (e.g., file input paths;
+  // scrollbars should be allowed)
+  if (!nsContentUtils::IsCallerChrome()) {
+    // not permitted to use DrawWindow
+    // XXX ERRMSG we need to report an error to developers here! (bug 329026)
+    error.Throw(NS_ERROR_DOM_SECURITY_ERR);
+    return;
+  }
+
+#if 0
+  nsCOMPtr<nsIFrameLoaderOwner> loaderOwner = do_QueryInterface(elem);
+  if (!loaderOwner) {
+    error.Throw(NS_ERROR_FAILURE);
+    return;
+  }
+
+  nsRefPtr<nsFrameLoader> frameloader = loaderOwner->GetFrameLoader();
+  if (!frameloader) {
+    error.Throw(NS_ERROR_FAILURE);
+    return;
+  }
+
+  PBrowserParent *child = frameloader->GetRemoteBrowser();
+  if (!child) {
+    nsCOMPtr<nsIDOMWindow> window =
+      do_GetInterface(frameloader->GetExistingDocShell());
+    if (!window) {
+      error.Throw(NS_ERROR_FAILURE);
+      return;
+    }
+
+    return DrawWindow(window, x, y, w, h, bgColor, flags);
+  }
+
+  // protect against too-large surfaces that will cause allocation
+  // or overflow issues
+  if (!gfxASurface::CheckSurfaceSize(gfxIntSize(w, h), 0xffff)) {
+    error.Throw(NS_ERROR_FAILURE);
+    return;
+  }
+
+  bool flush =
+    (flags & nsIDOMCanvasRenderingContext2D::DRAWWINDOW_DO_NOT_FLUSH) == 0;
+
+  uint32_t renderDocFlags = nsIPresShell::RENDER_IGNORE_VIEWPORT_SCROLLING;
+  if (flags & nsIDOMCanvasRenderingContext2D::DRAWWINDOW_DRAW_CARET) {
+    renderDocFlags |= nsIPresShell::RENDER_CARET;
+  }
+  if (flags & nsIDOMCanvasRenderingContext2D::DRAWWINDOW_DRAW_VIEW) {
+    renderDocFlags &= ~nsIPresShell::RENDER_IGNORE_VIEWPORT_SCROLLING;
+  }
+
+  nsRect rect(nsPresContext::CSSPixelsToAppUnits(x),
+              nsPresContext::CSSPixelsToAppUnits(y),
+              nsPresContext::CSSPixelsToAppUnits(w),
+              nsPresContext::CSSPixelsToAppUnits(h));
+  if (mIPC) {
+    PDocumentRendererParent *pdocrender =
+      child->SendPDocumentRendererConstructor(rect,
+                                              mThebes->CurrentMatrix(),
+                                              nsString(aBGColor),
+                                              renderDocFlags, flush,
+                                              nsIntSize(mWidth, mHeight));
+    if (!pdocrender)
+      return NS_ERROR_FAILURE;
+
+    DocumentRendererParent *docrender =
+      static_cast<DocumentRendererParent *>(pdocrender);
+
+    docrender->SetCanvasContext(this, mThebes);
+  }
+#endif
+}
+
+//
+// device pixel getting/setting
+//
+
+void
+CanvasRenderingContext2D::EnsureUnpremultiplyTable() {
+  if (sUnpremultiplyTable)
+    return;
+
+  // Infallably alloc the unpremultiply table.
+  sUnpremultiplyTable = new uint8_t[256][256];
+
+  // It's important that the array be indexed first by alpha and then by rgb
+  // value.  When we unpremultiply a pixel, we're guaranteed to do three
+  // lookups with the same alpha; indexing by alpha first makes it likely that
+  // those three lookups will be close to one another in memory, thus
+  // increasing the chance of a cache hit.
+
+  // a == 0 case
+  for (uint32_t c = 0; c <= 255; c++) {
+    sUnpremultiplyTable[0][c] = c;
+  }
+
+  for (int a = 1; a <= 255; a++) {
+    for (int c = 0; c <= 255; c++) {
+      sUnpremultiplyTable[a][c] = (uint8_t)((c * 255) / a);
+    }
+  }
+}
+
+
+already_AddRefed<ImageData>
+CanvasRenderingContext2D::GetImageData(JSContext* aCx, double aSx,
+                                       double aSy, double aSw,
+                                       double aSh, ErrorResult& error)
+{
+  EnsureTarget();
+  if (!IsTargetValid()) {
+    error.Throw(NS_ERROR_FAILURE);
+    return NULL;
+  }
+
+  if (!mCanvasElement && !mDocShell) {
+    NS_ERROR("No canvas element and no docshell in GetImageData!!!");
+    error.Throw(NS_ERROR_DOM_SECURITY_ERR);
+    return NULL;
+  }
+
+  // Check only if we have a canvas element; if we were created with a docshell,
+  // then it's special internal use.
+  if (mCanvasElement && mCanvasElement->IsWriteOnly() &&
+      !nsContentUtils::IsCallerChrome())
+  {
+    // XXX ERRMSG we need to report an error to developers here! (bug 329026)
+    error.Throw(NS_ERROR_DOM_SECURITY_ERR);
+    return NULL;
+  }
+
+  if (!NS_finite(aSx) || !NS_finite(aSy) ||
+      !NS_finite(aSw) || !NS_finite(aSh)) {
+    error.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
+    return NULL;
+  }
+
+  if (!aSw || !aSh) {
+    error.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
+    return NULL;
+  }
+
+  int32_t x = JS_DoubleToInt32(aSx);
+  int32_t y = JS_DoubleToInt32(aSy);
+  int32_t wi = JS_DoubleToInt32(aSw);
+  int32_t hi = JS_DoubleToInt32(aSh);
+
+  // Handle negative width and height by flipping the rectangle over in the
+  // relevant direction.
+  uint32_t w, h;
+  if (aSw < 0) {
+    w = -wi;
+    x -= w;
+  } else {
+    w = wi;
+  }
+  if (aSh < 0) {
+    h = -hi;
+    y -= h;
+  } else {
+    h = hi;
+  }
+
+  if (w == 0) {
+    w = 1;
+  }
+  if (h == 0) {
+    h = 1;
+  }
+
+  JSObject* array;
+  error = GetImageDataArray(aCx, x, y, w, h, &array);
+  if (error.Failed()) {
+    return NULL;
+  }
+  MOZ_ASSERT(array);
+
+  nsRefPtr<ImageData> imageData = new ImageData(w, h, *array);
+  return imageData.forget();
+}
+
+nsresult
+CanvasRenderingContext2D::GetImageDataArray(JSContext* aCx,
+                                            int32_t aX,
+                                            int32_t aY,
+                                            uint32_t aWidth,
+                                            uint32_t aHeight,
+                                            JSObject** aRetval)
+{
+  MOZ_ASSERT(aWidth && aHeight);
+
+  CheckedInt<uint32_t> len = CheckedInt<uint32_t>(aWidth) * aHeight * 4;
+  if (!len.isValid()) {
+    return NS_ERROR_DOM_INDEX_SIZE_ERR;
+  }
+
+  CheckedInt<int32_t> rightMost = CheckedInt<int32_t>(aX) + aWidth;
+  CheckedInt<int32_t> bottomMost = CheckedInt<int32_t>(aY) + aHeight;
+
+  if (!rightMost.isValid() || !bottomMost.isValid()) {
+    return NS_ERROR_DOM_SYNTAX_ERR;
+  }
+
+  JSObject* darray = JS_NewUint8ClampedArray(aCx, len.value());
+  if (!darray) {
+    return NS_ERROR_OUT_OF_MEMORY;
+  }
+
+  if (mZero) {
+    *aRetval = darray;
+    return NS_OK;
+  }
+
+  uint8_t* data = JS_GetUint8ClampedArrayData(darray);
+
+  IntRect srcRect(0, 0, mWidth, mHeight);
+  IntRect destRect(aX, aY, aWidth, aHeight);
+
+  IntRect srcReadRect = srcRect.Intersect(destRect);
+  IntRect dstWriteRect = srcReadRect;
+  dstWriteRect.MoveBy(-aX, -aY);
+
+  uint8_t* src = data;
+  uint32_t srcStride = aWidth * 4;
+
+  RefPtr<DataSourceSurface> readback;
+  if (!srcReadRect.IsEmpty()) {
+    RefPtr<SourceSurface> snapshot = mTarget->Snapshot();
+    if (snapshot) {
+      readback = snapshot->GetDataSurface();
+
+      srcStride = readback->Stride();
+      src = readback->GetData() + srcReadRect.y * srcStride + srcReadRect.x * 4;
+    }
+  }
+
+  // make sure sUnpremultiplyTable has been created
+  EnsureUnpremultiplyTable();
+
+  // NOTE! dst is the same as src, and this relies on reading
+  // from src and advancing that ptr before writing to dst.
+  // NOTE! I'm not sure that it is, I think this comment might have been
+  // inherited from Thebes canvas and is no longer true
+  uint8_t* dst = data + dstWriteRect.y * (aWidth * 4) + dstWriteRect.x * 4;
+
+  for (int32_t j = 0; j < dstWriteRect.height; ++j) {
+    for (int32_t i = 0; i < dstWriteRect.width; ++i) {
+      // XXX Is there some useful swizzle MMX we can use here?
+#ifdef IS_LITTLE_ENDIAN
+      uint8_t b = *src++;
+      uint8_t g = *src++;
+      uint8_t r = *src++;
+      uint8_t a = *src++;
+#else
+      uint8_t a = *src++;
+      uint8_t r = *src++;
+      uint8_t g = *src++;
+      uint8_t b = *src++;
+#endif
+      // Convert to non-premultiplied color
+      *dst++ = sUnpremultiplyTable[a][r];
+      *dst++ = sUnpremultiplyTable[a][g];
+      *dst++ = sUnpremultiplyTable[a][b];
+      *dst++ = a;
+    }
+    src += srcStride - (dstWriteRect.width * 4);
+    dst += (aWidth * 4) - (dstWriteRect.width * 4);
+  }
+
+  *aRetval = darray;
+  return NS_OK;
+}
+
+void
+CanvasRenderingContext2D::EnsurePremultiplyTable() {
+  if (sPremultiplyTable)
+    return;
+
+  // Infallably alloc the premultiply table.
+  sPremultiplyTable = new uint8_t[256][256];
+
+  // Like the unpremultiply table, it's important that we index the premultiply
+  // table with the alpha value as the first index to ensure good cache
+  // performance.
+
+  for (int a = 0; a <= 255; a++) {
+    for (int c = 0; c <= 255; c++) {
+      sPremultiplyTable[a][c] = (a * c + 254) / 255;
+    }
+  }
+}
+
+void
+CanvasRenderingContext2D::EnsureErrorTarget()
+{
+  if (sErrorTarget) {
+    return;
+  }
+
+  RefPtr<DrawTarget> errorTarget = gfxPlatform::GetPlatform()->CreateOffscreenDrawTarget(IntSize(1, 1), FORMAT_B8G8R8A8);
+  NS_ABORT_IF_FALSE(errorTarget, "Failed to allocate the error target!");
+
+  sErrorTarget = errorTarget;
+  NS_ADDREF(sErrorTarget);
+}
+
+void
+CanvasRenderingContext2D::FillRuleChanged()
+{
+  if (mPath) {
+    mPathBuilder = mPath->CopyToBuilder(CurrentState().fillRule);
+    mPath = nullptr;
+  }
+}
+
+void
+CanvasRenderingContext2D::PutImageData(JSContext* cx,
+                                       ImageData& imageData, double dx,
+                                       double dy, ErrorResult& error)
+{
+  if (!FloatValidate(dx, dy)) {
+    error.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
+    return;
+  }
+
+  dom::Uint8ClampedArray arr(imageData.GetDataObject());
+
+  error = PutImageData_explicit(JS_DoubleToInt32(dx), JS_DoubleToInt32(dy),
+                                imageData.Width(), imageData.Height(),
+                                arr.Data(), arr.Length(), false, 0, 0, 0, 0);
+}
+
+void
+CanvasRenderingContext2D::PutImageData(JSContext* cx,
+                                       ImageData& imageData, double dx,
+                                       double dy, double dirtyX,
+                                       double dirtyY, double dirtyWidth,
+                                       double dirtyHeight,
+                                       ErrorResult& error)
+{
+  if (!FloatValidate(dx, dy, dirtyX, dirtyY, dirtyWidth, dirtyHeight)) {
+    error.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
+    return;
+  }
+
+  dom::Uint8ClampedArray arr(imageData.GetDataObject());
+
+  error = PutImageData_explicit(JS_DoubleToInt32(dx), JS_DoubleToInt32(dy),
+                                imageData.Width(), imageData.Height(),
+                                arr.Data(), arr.Length(), true,
+                                JS_DoubleToInt32(dirtyX),
+                                JS_DoubleToInt32(dirtyY),
+                                JS_DoubleToInt32(dirtyWidth),
+                                JS_DoubleToInt32(dirtyHeight));
+}
+
+// void putImageData (in ImageData d, in float x, in float y);
+// void putImageData (in ImageData d, in double x, in double y, in double dirtyX, in double dirtyY, in double dirtyWidth, in double dirtyHeight);
+
+nsresult
+CanvasRenderingContext2D::PutImageData_explicit(int32_t x, int32_t y, uint32_t w, uint32_t h,
+                                                unsigned char *aData, uint32_t aDataLen,
+                                                bool hasDirtyRect, int32_t dirtyX, int32_t dirtyY,
+                                                int32_t dirtyWidth, int32_t dirtyHeight)
+{
+  if (w == 0 || h == 0) {
+    return NS_ERROR_DOM_SYNTAX_ERR;
+  }
+
+  IntRect dirtyRect;
+  IntRect imageDataRect(0, 0, w, h);
+
+  if (hasDirtyRect) {
+    // fix up negative dimensions
+    if (dirtyWidth < 0) {
+      NS_ENSURE_TRUE(dirtyWidth != INT_MIN, NS_ERROR_DOM_INDEX_SIZE_ERR);
+
+      CheckedInt32 checkedDirtyX = CheckedInt32(dirtyX) + dirtyWidth;
+
+      if (!checkedDirtyX.isValid())
+        return NS_ERROR_DOM_INDEX_SIZE_ERR;
+
+      dirtyX = checkedDirtyX.value();
+      dirtyWidth = -dirtyWidth;
+    }
+
+    if (dirtyHeight < 0) {
+      NS_ENSURE_TRUE(dirtyHeight != INT_MIN, NS_ERROR_DOM_INDEX_SIZE_ERR);
+
+      CheckedInt32 checkedDirtyY = CheckedInt32(dirtyY) + dirtyHeight;
+
+      if (!checkedDirtyY.isValid())
+        return NS_ERROR_DOM_INDEX_SIZE_ERR;
+
+      dirtyY = checkedDirtyY.value();
+      dirtyHeight = -dirtyHeight;
+    }
+
+    // bound the dirty rect within the imageData rectangle
+    dirtyRect = imageDataRect.Intersect(IntRect(dirtyX, dirtyY, dirtyWidth, dirtyHeight));
+
+    if (dirtyRect.Width() <= 0 || dirtyRect.Height() <= 0)
+      return NS_OK;
+  } else {
+    dirtyRect = imageDataRect;
+  }
+
+  dirtyRect.MoveBy(IntPoint(x, y));
+  dirtyRect = IntRect(0, 0, mWidth, mHeight).Intersect(dirtyRect);
+
+  if (dirtyRect.Width() <= 0 || dirtyRect.Height() <= 0) {
+    return NS_OK;
+  }
+
+  uint32_t len = w * h * 4;
+  if (aDataLen != len) {
+    return NS_ERROR_DOM_SYNTAX_ERR;
+  }
+
+  nsRefPtr<gfxImageSurface> imgsurf = new gfxImageSurface(gfxIntSize(w, h),
+                                                          gfxASurface::ImageFormatARGB32,
+                                                          false);
+  if (!imgsurf || imgsurf->CairoStatus()) {
+    return NS_ERROR_FAILURE;
+  }
+
+  // ensure premultiply table has been created
+  EnsurePremultiplyTable();
+
+  uint8_t *src = aData;
+  uint8_t *dst = imgsurf->Data();
+
+  for (uint32_t j = 0; j < h; j++) {
+    for (uint32_t i = 0; i < w; i++) {
+      uint8_t r = *src++;
+      uint8_t g = *src++;
+      uint8_t b = *src++;
+      uint8_t a = *src++;
+
+      // Convert to premultiplied color (losslessly if the input came from getImageData)
+#ifdef IS_LITTLE_ENDIAN
+      *dst++ = sPremultiplyTable[a][b];
+      *dst++ = sPremultiplyTable[a][g];
+      *dst++ = sPremultiplyTable[a][r];
+      *dst++ = a;
+#else
+      *dst++ = a;
+      *dst++ = sPremultiplyTable[a][r];
+      *dst++ = sPremultiplyTable[a][g];
+      *dst++ = sPremultiplyTable[a][b];
+#endif
+    }
+  }
+
+  EnsureTarget();
+  if (!IsTargetValid()) {
+    return NS_ERROR_FAILURE;
+  }
+
+  RefPtr<SourceSurface> sourceSurface =
+    mTarget->CreateSourceSurfaceFromData(imgsurf->Data(), IntSize(w, h), imgsurf->Stride(), FORMAT_B8G8R8A8);
+
+
+  mTarget->CopySurface(sourceSurface,
+                       IntRect(dirtyRect.x - x, dirtyRect.y - y,
+                               dirtyRect.width, dirtyRect.height),
+                       IntPoint(dirtyRect.x, dirtyRect.y));
+
+  Redraw(mgfx::Rect(dirtyRect.x, dirtyRect.y, dirtyRect.width, dirtyRect.height));
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+CanvasRenderingContext2D::GetThebesSurface(gfxASurface **surface)
+{
+  EnsureTarget();
+  if (!mThebesSurface) {
+    mThebesSurface =
+      gfxPlatform::GetPlatform()->GetThebesSurfaceForDrawTarget(mTarget);
+
+    if (!mThebesSurface) {
+      return NS_ERROR_FAILURE;
+    }
+  } else {
+    // Normally GetThebesSurfaceForDrawTarget will handle the flush, when
+    // we're returning a cached ThebesSurface we need to flush here.
+    mTarget->Flush();
+  }
+
+  *surface = mThebesSurface;
+  NS_ADDREF(*surface);
+
+  return NS_OK;
+}
+
+static already_AddRefed<ImageData>
+CreateImageData(JSContext* cx, CanvasRenderingContext2D* context,
+                uint32_t w, uint32_t h, ErrorResult& error)
+{
+  if (w == 0)
+      w = 1;
+  if (h == 0)
+      h = 1;
+
+  CheckedInt<uint32_t> len = CheckedInt<uint32_t>(w) * h * 4;
+  if (!len.isValid()) {
+    error.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
+    return NULL;
+  }
+
+  // Create the fast typed array; it's initialized to 0 by default.
+  JSObject* darray = Uint8ClampedArray::Create(cx, context, len.value());
+  if (!darray) {
+    error.Throw(NS_ERROR_OUT_OF_MEMORY);
+    return NULL;
+  }
+
+  nsRefPtr<mozilla::dom::ImageData> imageData =
+    new mozilla::dom::ImageData(w, h, *darray);
+  return imageData.forget();
+}
+
+already_AddRefed<ImageData>
+CanvasRenderingContext2D::CreateImageData(JSContext* cx, double sw,
+                                          double sh, ErrorResult& error)
+{
+  if (!FloatValidate(sw, sh)) {
+    error.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
+    return NULL;
+  }
+
+  if (!sw || !sh) {
+    error.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
+    return NULL;
+  }
+
+  int32_t wi = JS_DoubleToInt32(sw);
+  int32_t hi = JS_DoubleToInt32(sh);
+
+  uint32_t w = NS_ABS(wi);
+  uint32_t h = NS_ABS(hi);
+  return mozilla::dom::CreateImageData(cx, this, w, h, error);
+}
+
+already_AddRefed<ImageData>
+CanvasRenderingContext2D::CreateImageData(JSContext* cx,
+                                          ImageData& imagedata,
+                                          ErrorResult& error)
+{
+  return mozilla::dom::CreateImageData(cx, this, imagedata.Width(),
+                                       imagedata.Height(), error);
+}
+
+static uint8_t g2DContextLayerUserData;
+
+already_AddRefed<CanvasLayer>
+CanvasRenderingContext2D::GetCanvasLayer(nsDisplayListBuilder* aBuilder,
+                                         CanvasLayer *aOldLayer,
+                                         LayerManager *aManager)
+{
+  // Don't call EnsureTarget() ... if there isn't already a surface, then
+  // we have nothing to paint and there is no need to create a surface just
+  // to paint nothing. Also, EnsureTarget() can cause creation of a persistent
+  // layer manager which must NOT happen during a paint.