Merge m-i to m-c, a=merge
authorPhil Ringnalda <philringnalda@gmail.com>
Wed, 19 Oct 2016 18:29:50 -0700
changeset 318654 998ad5a74da80fe63664a8dcf30d8f269ffe4e65
parent 318624 dcfc4c3e4b552a47d3bd5748f3292c11acb12efc (current diff)
parent 318653 e6f320965d24073a97365a948a87240b640e9762 (diff)
child 318655 99a239e1866a57f987b08dad796528e4ea30e622
child 318665 f18405638007fc25bcaa88f6b0372a1e5266a86f
child 318710 1daa20fc836ed5ffdd90c25b72e05f16708bec2a
push id20725
push userphilringnalda@gmail.com
push dateThu, 20 Oct 2016 01:36:01 +0000
treeherderfx-team@998ad5a74da8 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone52.0a1
Merge m-i to m-c, a=merge MozReview-Commit-ID: 6ims5jd1FU5
js/src/jit/IonAnalysis.cpp
testing/mozharness/scripts/androidx86_emulator_unittest.py
--- a/browser/components/places/tests/browser/browser_410196_paste_into_tags.js
+++ b/browser/components/places/tests/browser/browser_410196_paste_into_tags.js
@@ -88,19 +88,17 @@ add_task(function* () {
 
 function focusTag(PlacesOrganizer) {
   PlacesOrganizer.selectLeftPaneQuery("Tags");
   let tags = PlacesOrganizer._places.selectedNode;
   tags.containerOpen = true;
   let fooTag = tags.getChild(0);
   let tagNode = fooTag;
   PlacesOrganizer._places.selectNode(fooTag);
-  // Bug 1283076: Nightly has a default 'bug' tag already set
-  let tagValue = AppConstants.NIGHTLY_BUILD ? 'bug' : 'foo';
-  is(tagNode.title, tagValue, "tagNode title is " + tagValue);
+  is(tagNode.title, 'foo', "tagNode title is foo");
   let ip = PlacesOrganizer._places.insertionPoint;
   ok(ip.isTag, "IP is a tag");
 }
 
 function copyHistNode(PlacesOrganizer, ContentTree) {
   // focus the history object
   PlacesOrganizer.selectLeftPaneQuery("History");
   let histContainer = PlacesOrganizer._places.selectedNode;
--- a/browser/components/places/tests/browser/browser_423515.js
+++ b/browser/components/places/tests/browser/browser_423515.js
@@ -147,19 +147,17 @@ function test() {
     validate: function() {
       // get tag root
       var query = PlacesUtils.history.getNewQuery();
       var options = PlacesUtils.history.getNewQueryOptions();
       options.resultType = Ci.nsINavHistoryQueryOptions.RESULTS_AS_TAG_QUERY;
       var tagsNode = PlacesUtils.history.executeQuery(query, options).root;
 
       tagsNode.containerOpen = true;
-      // Bug 1283076: Nightly already has 7 tags set
-      let tagsCount = AppConstants.NIGHTLY_BUILD ? 8 : 1;
-      is(tagsNode.childCount, tagsCount, "has new tag");
+      is(tagsNode.childCount, 1, "has new tag");
 
       var tagNode = tagsNode.getChild(0);
 
       is(PlacesControllerDragHelper.canMoveNode(tagNode),
          false, "should not be able to move tag container node");
       tagsNode.containerOpen = false;
     }
   });
--- a/browser/components/places/tests/browser/browser_bookmarkProperties_editTagContainer.js
+++ b/browser/components/places/tests/browser/browser_bookmarkProperties_editTagContainer.js
@@ -18,20 +18,19 @@ add_task(function* () {
   registerCleanupFunction(function* () {
     yield promiseLibraryClosed(library);
   });
 
   PlacesOrganizer.selectLeftPaneQuery("Tags");
   let tree = PlacesOrganizer._places;
   let tagsContainer = tree.selectedNode;
   tagsContainer.containerOpen = true;
-  // Bug 1283076: Nightly already has several tags set, position changes
-  let tagPosition = AppConstants.NIGHTLY_BUILD ? 7 : 0;
-  let tagNode = tagsContainer.getChild(tagPosition);
-  tree.selectNode(tagNode);
+  let fooTag = tagsContainer.getChild(0);
+  let tagNode = fooTag;
+  tree.selectNode(fooTag);
   is(tagNode.title, 'tag1', "tagNode title is correct");
 
   ok(tree.controller.isCommandEnabled("placesCmd_show:info"),
      "'placesCmd_show:info' on current selected node is enabled");
 
   yield withBookmarksDialog(
     true,
     function openDialog() {
--- a/browser/components/places/tests/browser/browser_library_commands.js
+++ b/browser/components/places/tests/browser/browser_library_commands.js
@@ -200,19 +200,18 @@ add_task(function* test_tags() {
      "Copy command is enabled");
   ok(!PO._places.controller.isCommandEnabled("cmd_cut"),
      "Cut command is disabled");
   ok(!PO._places.controller.isCommandEnabled("cmd_delete"),
      "Delete command is disabled");
 
   // Now select the tag.
   PlacesUtils.asContainer(tagsNode).containerOpen = true;
-  // Bug 1283076: Nightly already has several tags set, position changes
-  let tagPosition = AppConstants.NIGHTLY_BUILD ? 7 : 0;
-  PO._places.selectNode(tagsNode.getChild(tagPosition));
+  let tag = tagsNode.getChild(0);
+  PO._places.selectNode(tag);
   is(PO._places.selectedNode.title, "test",
      "The created tag has been properly selected");
 
   // Check that cut is disabled but delete is enabled.
   ok(PO._places.controller.isCommandEnabled("cmd_copy"),
      "Copy command is enabled");
   ok(!PO._places.controller.isCommandEnabled("cmd_cut"),
      "Cut command is disabled");
--- a/browser/locales/generic/profile/bookmarks.html.in
+++ b/browser/locales/generic/profile/bookmarks.html.in
@@ -40,17 +40,17 @@
         </dl>
 #else
         <dl>
             <p><dt><a href="https://www.mozilla.org/@AB_CD@/contribute/" icon="@mozilla_icon@">@firefox_community@</a>
         </dl>
     <p><dt><h3>@nightly_heading@</h3></dt>
         <dl><p>
             <dt><a href="https://blog.nightly.mozilla.org/" icon="@nightly_icon@">@nightly_blog@</a>
-            <dt><a href="https://bugzilla.mozilla.org/" icon="@bugzilla_icon@" shortcuturl="bz" tags="bug,issue">@bugzilla@</a>
+            <dt><a href="https://bugzilla.mozilla.org/" icon="@bugzilla_icon@" shortcuturl="bz">@bugzilla@</a>
             <dt><a href="https://developer.mozilla.org/" icon="@mdn_icon@" shortcuturl="mdn">@mdn@</a>
             <dt><a href="https://addons.mozilla.org/@AB_CD@/firefox/addon/nightly-tester-tools/" icon="@addon_icon@">@nightly_tester_tools@</a>
-            <dt><a href="about:crashes" icon="@mozilla_icon@" tags="crash">@crashes@</a>
-            <dt><a href="https://mibbit.com/?server=irc.mozilla.org&channel=%23nightly" icon="@mozilla_icon@" tags="chat,irc">@irc@</a>
-            <dt><a href="https://planet.mozilla.org/" icon="@mozilla_icon@" tags="planet,news">@planet@</a>
+            <dt><a href="about:crashes" icon="@mozilla_icon@">@crashes@</a>
+            <dt><a href="https://mibbit.com/?server=irc.mozilla.org&channel=%23nightly" icon="@mozilla_icon@">@irc@</a>
+            <dt><a href="https://planet.mozilla.org/" icon="@mozilla_icon@">@planet@</a>
         </dl>
 #endif
 </dl>
--- a/dom/ipc/PBrowser.ipdl
+++ b/dom/ipc/PBrowser.ipdl
@@ -580,19 +580,18 @@ parent:
     async SetDimensions(uint32_t aFlags, int32_t aX, int32_t aY, int32_t aCx, int32_t aCy);
 
     nested(inside_sync) sync DispatchWheelEvent(WidgetWheelEvent event);
     nested(inside_sync) sync DispatchMouseEvent(WidgetMouseEvent event);
     nested(inside_sync) sync DispatchKeyboardEvent(WidgetKeyboardEvent event);
 
     async InvokeDragSession(IPCDataTransfer[] transfers, uint32_t action,
                             OptionalShmem visualData,
-                            uint32_t width, uint32_t height,
                             uint32_t stride, uint8_t format,
-                            int32_t dragAreaX, int32_t dragAreaY);
+                            LayoutDeviceIntRect dragRect);
 
     async AudioChannelActivityNotification(uint32_t aAudioChannel,
                                            bool aActive);
 
     // After a compositor reset, it is necessary to reconnect each layers ID to
     // the compositor of the widget that will render those layers. Note that
     // this is sync so we can ensure that messages to the window compositor
     // arrive before the TabChild attempts to use its cross-process compositor
--- a/dom/ipc/TabParent.cpp
+++ b/dom/ipc/TabParent.cpp
@@ -283,18 +283,17 @@ TabParent::TabParent(nsIContentParent* a
   , mSizeMode(nsSizeMode_Normal)
   , mManager(aManager)
   , mDocShellIsActive(false)
   , mMarkedDestroying(false)
   , mIsDestroyed(false)
   , mIsDetached(true)
   , mAppPackageFileDescriptorSent(false)
   , mChromeFlags(aChromeFlags)
-  , mDragAreaX(0)
-  , mDragAreaY(0)
+  , mDragValid(false)
   , mInitedByParent(false)
   , mTabId(aTabId)
   , mCreatingWindow(false)
   , mCursor(nsCursor(-1))
   , mTabSetsCursor(false)
   , mHasContentOpener(false)
 #ifdef DEBUG
   , mActiveSupressDisplayportCount(0)
@@ -3228,19 +3227,18 @@ TabParent::RecvAsyncAuthPrompt(const nsC
 
   return rv == NS_OK;
 }
 
 bool
 TabParent::RecvInvokeDragSession(nsTArray<IPCDataTransfer>&& aTransfers,
                                  const uint32_t& aAction,
                                  const OptionalShmem& aVisualDnDData,
-                                 const uint32_t& aWidth, const uint32_t& aHeight,
                                  const uint32_t& aStride, const uint8_t& aFormat,
-                                 const int32_t& aDragAreaX, const int32_t& aDragAreaY)
+                                 const LayoutDeviceIntRect& aDragRect)
 {
   mInitialDataTransferItems.Clear();
   nsIPresShell* shell = mFrameElement->OwnerDoc()->GetShell();
   if (!shell) {
     if (Manager()->IsContentParent()) {
       Unused << Manager()->AsContentParent()->SendEndDragSession(true, true,
                                                                  LayoutDeviceIntPoint());
     }
@@ -3256,27 +3254,28 @@ TabParent::RecvInvokeDragSession(nsTArra
       do_GetService("@mozilla.org/widget/dragservice;1");
     if (dragService) {
       dragService->MaybeAddChildProcess(Manager()->AsContentParent());
     }
   }
 
   if (aVisualDnDData.type() == OptionalShmem::Tvoid_t ||
       !aVisualDnDData.get_Shmem().IsReadable() ||
-      aVisualDnDData.get_Shmem().Size<char>() < aHeight * aStride) {
+      aVisualDnDData.get_Shmem().Size<char>() < aDragRect.height * aStride) {
     mDnDVisualization = nullptr;
   } else {
     mDnDVisualization =
-        gfx::CreateDataSourceSurfaceFromData(gfx::IntSize(aWidth, aHeight),
+        gfx::CreateDataSourceSurfaceFromData(gfx::IntSize(aDragRect.width, aDragRect.height),
                                              static_cast<gfx::SurfaceFormat>(aFormat),
                                              aVisualDnDData.get_Shmem().get<uint8_t>(),
                                              aStride);
   }
-  mDragAreaX = aDragAreaX;
-  mDragAreaY = aDragAreaY;
+
+  mDragValid = true;
+  mDragRect = aDragRect;
 
   esm->BeginTrackingRemoteDragGesture(mFrameElement);
 
   if (aVisualDnDData.type() == OptionalShmem::TShmem) {
     Unused << DeallocShmem(aVisualDnDData);
   }
 
   return true;
@@ -3330,23 +3329,27 @@ TabParent::AddInitialDnDDataTo(DataTrans
                                                           variant, i,
                                                           nsContentUtils::GetSystemPrincipal(),
                                                           /* aHidden = */ false);
     }
   }
   mInitialDataTransferItems.Clear();
 }
 
-void
+bool
 TabParent::TakeDragVisualization(RefPtr<mozilla::gfx::SourceSurface>& aSurface,
-                                 int32_t& aDragAreaX, int32_t& aDragAreaY)
+                                 LayoutDeviceIntRect* aDragRect)
 {
+  if (!mDragValid)
+    return false;
+
   aSurface = mDnDVisualization.forget();
-  aDragAreaX = mDragAreaX;
-  aDragAreaY = mDragAreaY;
+  *aDragRect = mDragRect;
+  mDragValid = false;
+  return true;
 }
 
 bool
 TabParent::AsyncPanZoomEnabled() const
 {
   nsCOMPtr<nsIWidget> widget = GetWidget();
   return widget && widget->AsyncPanZoomEnabled();
 }
--- a/dom/ipc/TabParent.h
+++ b/dom/ipc/TabParent.h
@@ -569,24 +569,23 @@ public:
                             const bool& aRunInGlobalScope);
 
   void LayerTreeUpdate(uint64_t aEpoch, bool aActive);
 
   virtual bool
   RecvInvokeDragSession(nsTArray<IPCDataTransfer>&& aTransfers,
                         const uint32_t& aAction,
                         const OptionalShmem& aVisualDnDData,
-                        const uint32_t& aWidth, const uint32_t& aHeight,
                         const uint32_t& aStride, const uint8_t& aFormat,
-                        const int32_t& aDragAreaX, const int32_t& aDragAreaY) override;
+                        const LayoutDeviceIntRect& aDragRect) override;
 
   void AddInitialDnDDataTo(DataTransfer* aDataTransfer);
 
-  void TakeDragVisualization(RefPtr<mozilla::gfx::SourceSurface>& aSurface,
-                             int32_t& aDragAreaX, int32_t& aDragAreaY);
+  bool TakeDragVisualization(RefPtr<mozilla::gfx::SourceSurface>& aSurface,
+                             LayoutDeviceIntRect* aDragRect);
 
   layout::RenderFrameParent* GetRenderFrame();
 
   void AudioChannelChangeNotification(nsPIDOMWindowOuter* aWindow,
                                       AudioChannel aAudioChannel,
                                       float aVolume,
                                       bool aMuted);
   bool SetRenderFrame(PRenderFrameParent* aRFParent);
@@ -680,18 +679,18 @@ private:
   // Whether we have already sent a FileDescriptor for the app package.
   bool mAppPackageFileDescriptorSent;
 
   uint32_t mChromeFlags;
 
   nsTArray<nsTArray<IPCDataTransferItem>> mInitialDataTransferItems;
 
   RefPtr<gfx::DataSourceSurface> mDnDVisualization;
-  int32_t mDragAreaX;
-  int32_t mDragAreaY;
+  bool mDragValid;
+  LayoutDeviceIntRect mDragRect;
 
   // When true, the TabParent is initialized without child side's request.
   // When false, the TabParent is initialized by window.open() from child side.
   bool mInitedByParent;
 
   nsCOMPtr<nsILoadContext> mLoadContext;
 
   // We keep a strong reference to the frameloader after we've sent the
--- a/dom/media/TextTrackCue.h
+++ b/dom/media/TextTrackCue.h
@@ -377,26 +377,26 @@ private:
   RefPtr<nsIDocument> mDocument;
   nsString mText;
   double mStartTime;
   double mEndTime;
 
   RefPtr<TextTrack> mTrack;
   RefPtr<HTMLTrackElement> mTrackElement;
   nsString mId;
-  double mPosition;
+  MOZ_INIT_OUTSIDE_CTOR double mPosition;
   bool mPositionIsAutoKeyword;
   PositionAlignSetting mPositionAlign;
   double mSize;
   bool mPauseOnExit;
   bool mSnapToLines;
   RefPtr<TextTrackRegion> mRegion;
   DirectionSetting mVertical;
   bool mLineIsAutoKeyword;
-  double mLine;
+  MOZ_INIT_OUTSIDE_CTOR double mLine;
   AlignSetting mAlign;
   LineAlignSetting mLineAlign;
 
   // Holds the computed DOM elements that represent the parsed cue text.
   // http://www.whatwg.org/specs/web-apps/current-work/#text-track-cue-display-state
   RefPtr<nsGenericHTMLElement> mDisplayState;
   // Tells whether or not we need to recompute mDisplayState. This is set
   // anytime a property that relates to the display of the TextTrackCue is
--- a/image/Decoder.cpp
+++ b/image/Decoder.cpp
@@ -58,17 +58,16 @@ Decoder::Decoder(RasterImage* aImage)
   , mInitialized(false)
   , mMetadataDecode(false)
   , mHaveExplicitOutputSize(false)
   , mInFrame(false)
   , mFinishedNewFrame(false)
   , mReachedTerminalState(false)
   , mDecodeDone(false)
   , mError(false)
-  , mDecodeAborted(false)
   , mShouldReportError(false)
 { }
 
 Decoder::~Decoder()
 {
   MOZ_ASSERT(mProgress == NoProgress || !mImage,
              "Destroying Decoder without taking all its progress changes");
   MOZ_ASSERT(mInvalidRect.IsEmpty() || !mImage,
@@ -186,56 +185,49 @@ Decoder::CompleteDecode()
   }
 
   rv = HasError() ? FinishWithErrorInternal()
                   : FinishInternal();
   if (NS_FAILED(rv)) {
     PostError();
   }
 
-  // If this was a metadata decode and we never got a size, the decode failed.
-  if (IsMetadataDecode() && !HasSize()) {
-    PostError();
+  if (IsMetadataDecode()) {
+    // If this was a metadata decode and we never got a size, the decode failed.
+    if (!HasSize()) {
+      PostError();
+    }
+    return;
   }
 
-  // If the implementation left us mid-frame, finish that up.
-  if (mInFrame && !HasError()) {
+  // If the implementation left us mid-frame, finish that up. Note that it may
+  // have left us transparent.
+  if (mInFrame) {
+    PostHasTransparency();
     PostFrameStop();
   }
 
-  // If PostDecodeDone() has not been called, and this decoder wasn't aborted
-  // early because of low-memory conditions or losing a race with another
-  // decoder, we need to send teardown notifications (and report an error to the
-  // console later).
-  if (!IsMetadataDecode() && !mDecodeDone && !WasAborted()) {
+  // If PostDecodeDone() has not been called, we may need to send teardown
+  // notifications if it is unrecoverable.
+  if (!mDecodeDone) {
+    // We should always report an error to the console in this case.
     mShouldReportError = true;
 
-    // Even if we encountered an error, we're still usable if we have at least
-    // one complete frame.
     if (GetCompleteFrameCount() > 0) {
-      // We're usable, so do exactly what we should have when the decoder
-      // completed.
-
-      // Not writing to the entire frame may have left us transparent.
+      // We're usable if we have at least one complete frame, so do exactly
+      // what we should have when the decoder completed.
       PostHasTransparency();
-
-      if (mInFrame) {
-        PostFrameStop();
-      }
       PostDecodeDone();
     } else {
       // We're not usable. Record some final progress indicating the error.
-      if (!IsMetadataDecode()) {
-        mProgress |= FLAG_DECODE_COMPLETE;
-      }
-      mProgress |= FLAG_HAS_ERROR;
+      mProgress |= FLAG_DECODE_COMPLETE | FLAG_HAS_ERROR;
     }
   }
 
-  if (mDecodeDone && !IsMetadataDecode()) {
+  if (mDecodeDone) {
     MOZ_ASSERT(HasError() || mCurrentFrame, "Should have an error or a frame");
 
     // If this image wasn't animated and isn't a transient image, mark its frame
     // as optimizable. We don't support optimizing animated images and
     // optimizing transient images isn't worth it.
     if (!HasAnimation() &&
         !(mDecoderFlags & DecoderFlags::IMAGE_IS_TRANSIENT) &&
         mCurrentFrame) {
@@ -266,17 +258,16 @@ Decoder::TakeCompleteFrameCount()
   return finishedNewFrame ? Some(GetCompleteFrameCount()) : Nothing();
 }
 
 DecoderFinalStatus
 Decoder::FinalStatus() const
 {
   return DecoderFinalStatus(IsMetadataDecode(),
                             GetDecodeDone(),
-                            WasAborted(),
                             HasError(),
                             ShouldReportError());
 }
 
 DecoderTelemetry
 Decoder::Telemetry() const
 {
   MOZ_ASSERT(mIterator);
@@ -512,15 +503,19 @@ Decoder::PostDecodeDone(int32_t aLoopCou
   mProgress |= FLAG_DECODE_COMPLETE;
 }
 
 void
 Decoder::PostError()
 {
   mError = true;
 
-  if (mInFrame && mCurrentFrame) {
+  if (mInFrame) {
+    MOZ_ASSERT(mCurrentFrame);
+    MOZ_ASSERT(mFrameCount > 0);
     mCurrentFrame->Abort();
+    mInFrame = false;
+    --mFrameCount;
   }
 }
 
 } // namespace image
 } // namespace mozilla
--- a/image/Decoder.h
+++ b/image/Decoder.h
@@ -27,37 +27,30 @@ namespace Telemetry {
 } // namespace Telemetry
 
 namespace image {
 
 struct DecoderFinalStatus final
 {
   DecoderFinalStatus(bool aWasMetadataDecode,
                      bool aFinished,
-                     bool aWasAborted,
                      bool aHadError,
                      bool aShouldReportError)
     : mWasMetadataDecode(aWasMetadataDecode)
     , mFinished(aFinished)
-    , mWasAborted(aWasAborted)
     , mHadError(aHadError)
     , mShouldReportError(aShouldReportError)
   { }
 
   /// True if this was a metadata decode.
   const bool mWasMetadataDecode : 1;
 
   /// True if this decoder finished, whether successfully or due to failure.
   const bool mFinished : 1;
 
-  /// True if this decoder was asynchronously aborted. This normally happens
-  /// when a decoder fails to insert a surface into the surface cache, indicating
-  /// that another decoding beat it to the punch.
-  const bool mWasAborted : 1;
-
   /// True if this decoder encountered an error.
   const bool mHadError : 1;
 
   /// True if this decoder encountered the kind of error that should be reported
   /// to the console.
   const bool mShouldReportError : 1;
 };
 
@@ -282,27 +275,16 @@ public:
   {
     return mReachedTerminalState || mDecodeDone ||
            (mMetadataDecode && HasSize()) || HasError();
   }
 
   /// Are we in the middle of a frame right now? Used for assertions only.
   bool InFrame() const { return mInFrame; }
 
-  /**
-   * Returns true if this decoder was aborted.
-   *
-   * This may happen due to a low-memory condition, or because another decoder
-   * was racing with this one to decode the same frames with the same flags and
-   * this decoder lost the race. Either way, this is not a permanent situation
-   * and does not constitute an error, so we don't report any errors when this
-   * happens.
-   */
-  bool WasAborted() const { return mDecodeAborted; }
-
   enum DecodeStyle {
       PROGRESSIVE, // produce intermediate frames representing the partial
                    // state of the image
       SEQUENTIAL   // decode to final image immediately
   };
 
   /**
    * Get or set the DecoderFlags that influence the behavior of this decoder.
@@ -566,16 +548,15 @@ private:
   bool mMetadataDecode : 1;
   bool mHaveExplicitOutputSize : 1;
   bool mInFrame : 1;
   bool mFinishedNewFrame : 1;  // True if PostFrameStop() has been called since
                                // the last call to TakeCompleteFrameCount().
   bool mReachedTerminalState : 1;
   bool mDecodeDone : 1;
   bool mError : 1;
-  bool mDecodeAborted : 1;
   bool mShouldReportError : 1;
 };
 
 } // namespace image
 } // namespace mozilla
 
 #endif // mozilla_image_Decoder_h
--- a/image/RasterImage.cpp
+++ b/image/RasterImage.cpp
@@ -1578,17 +1578,17 @@ RasterImage::NotifyDecodeComplete(const 
                                   const IntRect& aInvalidRect,
                                   const Maybe<uint32_t>& aFrameCount,
                                   DecoderFlags aDecoderFlags,
                                   SurfaceFlags aSurfaceFlags)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   // If the decoder detected an error, log it to the error console.
-  if (aStatus.mShouldReportError && !aStatus.mWasAborted) {
+  if (aStatus.mShouldReportError) {
     ReportDecoderError();
   }
 
   // Record all the metadata the decoder gathered about this image.
   bool metadataOK = SetMetadata(aMetadata, aStatus.mWasMetadataDecode);
   if (!metadataOK) {
     // This indicates a serious error that requires us to discard all existing
     // surfaces and redecode to recover. We'll drop the results from this
@@ -1596,66 +1596,70 @@ RasterImage::NotifyDecodeComplete(const 
     RecoverFromInvalidFrames(mSize,
                              FromSurfaceFlags(aSurfaceFlags));
     return;
   }
 
   MOZ_ASSERT(mError || mHasSize || !aMetadata.HasSize(),
              "SetMetadata should've gotten a size");
 
-  if (!aStatus.mWasMetadataDecode && aStatus.mFinished && !aStatus.mWasAborted) {
+  if (!aStatus.mWasMetadataDecode && aStatus.mFinished) {
     // Flag that we've been decoded before.
     mHasBeenDecoded = true;
   }
 
   // Send out any final notifications.
   NotifyProgress(aProgress, aInvalidRect, aFrameCount,
                  aDecoderFlags, aSurfaceFlags);
 
   if (!(aDecoderFlags & DecoderFlags::FIRST_FRAME_ONLY) &&
       mHasBeenDecoded && mAnimationState) {
     // We've finished a full decode of all animation frames and our AnimationState
     // has been notified about them all, so let it know not to expect anymore.
     mAnimationState->SetDoneDecoding(true);
   }
 
-  if (!aStatus.mWasMetadataDecode && aTelemetry.mChunkCount) {
-    Telemetry::Accumulate(Telemetry::IMAGE_DECODE_CHUNKS, aTelemetry.mChunkCount);
-  }
+  // Do some telemetry if this isn't a metadata decode.
+  if (!aStatus.mWasMetadataDecode) {
+    if (aTelemetry.mChunkCount) {
+      Telemetry::Accumulate(Telemetry::IMAGE_DECODE_CHUNKS, aTelemetry.mChunkCount);
+    }
 
-  if (aStatus.mFinished) {
-    // Do some telemetry if this isn't a metadata decode.
-    if (!aStatus.mWasMetadataDecode) {
+    if (aStatus.mFinished) {
       Telemetry::Accumulate(Telemetry::IMAGE_DECODE_TIME,
                             int32_t(aTelemetry.mDecodeTime.ToMicroseconds()));
 
       if (aTelemetry.mSpeedHistogram) {
         Telemetry::Accumulate(*aTelemetry.mSpeedHistogram, aTelemetry.Speed());
       }
     }
+  }
 
-    // Detect errors.
-    if (aStatus.mHadError && !aStatus.mWasAborted) {
-      DoError();
-    } else if (aStatus.mWasMetadataDecode && !mHasSize) {
-      DoError();
-    }
+  // Only act on errors if we have no usable frames from the decoder.
+  if (aStatus.mHadError &&
+      (!mAnimationState || mAnimationState->KnownFrameCount() == 0)) {
+    DoError();
+  } else if (aStatus.mWasMetadataDecode && !mHasSize) {
+    DoError();
+  }
 
+  // XXX(aosmond): Can we get this far without mFinished == true?
+  if (aStatus.mFinished && aStatus.mWasMetadataDecode) {
     // If we were waiting to fire the load event, go ahead and fire it now.
-    if (mLoadProgress && aStatus.mWasMetadataDecode) {
+    if (mLoadProgress) {
       NotifyForLoadEvent(*mLoadProgress);
       mLoadProgress = Nothing();
       NotifyProgress(FLAG_ONLOAD_UNBLOCKED);
     }
-  }
 
-  // If we were a metadata decode and a full decode was requested, do it.
-  if (aStatus.mFinished && aStatus.mWasMetadataDecode && mWantFullDecode) {
-    mWantFullDecode = false;
-    RequestDecodeForSize(mSize, DECODE_FLAGS_DEFAULT);
+    // If we were a metadata decode and a full decode was requested, do it.
+    if (mWantFullDecode) {
+      mWantFullDecode = false;
+      RequestDecodeForSize(mSize, DECODE_FLAGS_DEFAULT);
+    }
   }
 }
 
 void
 RasterImage::ReportDecoderError()
 {
   nsCOMPtr<nsIConsoleService> consoleService =
     do_GetService(NS_CONSOLESERVICE_CONTRACTID);
--- a/image/decoders/nsICODecoder.cpp
+++ b/image/decoders/nsICODecoder.cpp
@@ -95,17 +95,16 @@ nsICODecoder::GetFinalStateFromContained
   // Let the contained decoder finish up if necessary.
   if (!mContainedSourceBuffer->IsComplete()) {
     mContainedSourceBuffer->Complete(NS_OK);
     mContainedDecoder->Decode();
   }
 
   // Make our state the same as the state of the contained decoder.
   mDecodeDone = mContainedDecoder->GetDecodeDone();
-  mDecodeAborted = mContainedDecoder->WasAborted();
   mProgress |= mContainedDecoder->TakeProgress();
   mInvalidRect.UnionRect(mInvalidRect, mContainedDecoder->TakeInvalidRect());
   mCurrentFrame = mContainedDecoder->GetCurrentFrameRef();
 
   // Propagate errors.
   nsresult rv = HasError() || mContainedDecoder->HasError()
               ? NS_ERROR_FAILURE
               : NS_OK;
--- a/image/test/gtest/TestDecoders.cpp
+++ b/image/test/gtest/TestDecoders.cpp
@@ -30,17 +30,16 @@ using namespace mozilla::gfx;
 using namespace mozilla::image;
 
 static already_AddRefed<SourceSurface>
 CheckDecoderState(const ImageTestCase& aTestCase, Decoder* aDecoder)
 {
   EXPECT_TRUE(aDecoder->GetDecodeDone());
   EXPECT_EQ(bool(aTestCase.mFlags & TEST_CASE_HAS_ERROR),
             aDecoder->HasError());
-  EXPECT_TRUE(!aDecoder->WasAborted());
 
   // Verify that the decoder made the expected progress.
   Progress progress = aDecoder->TakeProgress();
   EXPECT_EQ(bool(aTestCase.mFlags & TEST_CASE_HAS_ERROR),
             bool(progress & FLAG_HAS_ERROR));
 
   if (aTestCase.mFlags & TEST_CASE_HAS_ERROR) {
     return nullptr;  // That's all we can check for bad images.
--- a/js/src/asmjs/WasmBaselineCompile.cpp
+++ b/js/src/asmjs/WasmBaselineCompile.cpp
@@ -698,17 +698,19 @@ class BaseCompiler
 #endif
     }
 
     Register64 allocInt64() {
         MOZ_ASSERT(hasInt64());
 #ifdef JS_PUNBOX64
         return Register64(availGPR_.takeAny());
 #else
-        return Register64(availGPR_.takeAny(), availGPR_.takeAny());
+        Register high = availGPR_.takeAny();
+        Register low = availGPR_.takeAny();
+        return Register64(high, low);
 #endif
     }
 
     void freeInt64(Register64 r) {
 #ifdef JS_PUNBOX64
         availGPR_.add(r.reg);
 #else
         availGPR_.add(r.low);
@@ -1488,16 +1490,21 @@ class BaseCompiler
         if (v.kind() == Stk::RegisterI64)
             r = v.i64reg();
         else
             popI64(v, (r = needI64()));
         stk_.popBack();
         return r;
     }
 
+    // Note, the stack top can be in one half of "specific" on 32-bit
+    // systems.  We can optimize, but for simplicity, if the register
+    // does not match exactly, then just force the stack top to memory
+    // and then read it back in.
+
     RegI64 popI64(RegI64 specific) {
         Stk& v = stk_.back();
 
         if (!(v.kind() == Stk::RegisterI64 && v.i64reg() == specific)) {
             needI64(specific);
             popI64(v, specific);
             if (v.kind() == Stk::RegisterI64)
                 freeI64(v.i64reg());
@@ -2332,77 +2339,63 @@ class BaseCompiler
         masm.addCodeLabel(tableCl);
 
         masm.jmp(Operand(scratch, switchValue.reg, ScalePointer));
 #else
         MOZ_CRASH("BaseCompiler platform hook: tableSwitch");
 #endif
     }
 
-    void captureReturnedI32(RegI32 dest) {
-        moveI32(RegI32(ReturnReg), dest);
-    }
-
-    void captureReturnedI64(RegI64 dest) {
-        moveI64(RegI64(ReturnReg64), dest);
-    }
-
-    void captureReturnedF32(const FunctionCall& call, RegF32 dest) {
+    RegI32 captureReturnedI32() {
+        RegI32 rv = RegI32(ReturnReg);
+        MOZ_ASSERT(isAvailable(rv.reg));
+        needI32(rv);
+        return rv;
+    }
+
+    RegI64 captureReturnedI64() {
+        RegI64 rv = RegI64(ReturnReg64);
+        MOZ_ASSERT(isAvailable(rv.reg));
+        needI64(rv);
+        return rv;
+    }
+
+    RegF32 captureReturnedF32(const FunctionCall& call) {
+        RegF32 rv = RegF32(ReturnFloat32Reg);
+        MOZ_ASSERT(isAvailable(rv.reg));
+        needF32(rv);
 #ifdef JS_CODEGEN_X86
         if (call.builtinCall_) {
             masm.reserveStack(sizeof(float));
             Operand op(esp, 0);
             masm.fstp32(op);
-            masm.loadFloat32(op, dest.reg);
+            masm.loadFloat32(op, rv.reg);
             masm.freeStack(sizeof(float));
-            return;
         }
 #endif
-        moveF32(RegF32(ReturnFloat32Reg), dest);
-    }
-
-    void captureReturnedF64(const FunctionCall& call, RegF64 dest) {
+        return rv;
+    }
+
+    RegF64 captureReturnedF64(const FunctionCall& call) {
+        RegF64 rv = RegF64(ReturnDoubleReg);
+        MOZ_ASSERT(isAvailable(rv.reg));
+        needF64(rv);
 #ifdef JS_CODEGEN_X86
         if (call.builtinCall_) {
             masm.reserveStack(sizeof(double));
             Operand op(esp, 0);
             masm.fstp(op);
-            masm.loadDouble(op, dest.reg);
+            masm.loadDouble(op, rv.reg);
             masm.freeStack(sizeof(double));
-            return;
         }
 #endif
-        moveF64(RegF64(ReturnDoubleReg), dest);
-    }
-
-    void returnVoid() {
-        popStackBeforeBranch(ctl_[0].framePushed);
-        masm.jump(&returnLabel_);
-    }
-
-    void returnI32(RegI32 r) {
-        moveI32(r, RegI32(ReturnReg));
-        popStackBeforeBranch(ctl_[0].framePushed);
-        masm.jump(&returnLabel_);
-    }
-
-    void returnI64(RegI64 r) {
-        moveI64(r, RegI64(Register64(ReturnReg64)));
-        popStackBeforeBranch(ctl_[0].framePushed);
-        masm.jump(&returnLabel_);
-    }
-
-    void returnF64(RegF64 r) {
-        moveF64(r, RegF64(ReturnDoubleReg));
-        popStackBeforeBranch(ctl_[0].framePushed);
-        masm.jump(&returnLabel_);
-    }
-
-    void returnF32(RegF32 r) {
-        moveF32(r, RegF32(ReturnFloat32Reg));
+        return rv;
+    }
+
+    void returnCleanup() {
         popStackBeforeBranch(ctl_[0].framePushed);
         masm.jump(&returnLabel_);
     }
 
     void subtractI64(RegI64 rhs, RegI64 srcDest) {
 #if defined(JS_CODEGEN_X64) || defined(JS_CODEGEN_X86)
         masm.sub64(rhs.reg, srcDest.reg);
 #else
@@ -5054,40 +5047,40 @@ BaseCompiler::emitBrTable()
     return true;
 }
 
 void
 BaseCompiler::doReturn(ExprType type)
 {
     switch (type) {
       case ExprType::Void: {
-        returnVoid();
+        returnCleanup();
         break;
       }
       case ExprType::I32: {
-        RegI32 rv = popI32();
-        returnI32(rv);
+        RegI32 rv = popI32(RegI32(ReturnReg));
+        returnCleanup();
         freeI32(rv);
         break;
       }
       case ExprType::I64: {
-        RegI64 rv = popI64();
-        returnI64(rv);
+        RegI64 rv = popI64(RegI64(ReturnReg64));
+        returnCleanup();
         freeI64(rv);
         break;
       }
       case ExprType::F64: {
-        RegF64 rv = popF64();
-        returnF64(rv);
+        RegF64 rv = popF64(RegF64(ReturnDoubleReg));
+        returnCleanup();
         freeF64(rv);
         break;
       }
       case ExprType::F32: {
-        RegF32 rv = popF32();
-        returnF32(rv);
+        RegF32 rv = popF32(RegF32(ReturnFloat32Reg));
+        returnCleanup();
         freeF32(rv);
         break;
       }
       default: {
         MOZ_CRASH("Function return type");
       }
     }
 }
@@ -5141,36 +5134,32 @@ BaseCompiler::emitCallArgs(const ValType
 void
 BaseCompiler::pushReturned(const FunctionCall& call, ExprType type)
 {
     switch (type) {
       case ExprType::Void:
         MOZ_CRASH("Compiler bug: attempt to push void return");
         break;
       case ExprType::I32: {
-        RegI32 rv = needI32();
-        captureReturnedI32(rv);
+        RegI32 rv = captureReturnedI32();
         pushI32(rv);
         break;
       }
       case ExprType::I64: {
-        RegI64 rv = needI64();
-        captureReturnedI64(rv);
+        RegI64 rv = captureReturnedI64();
         pushI64(rv);
         break;
       }
       case ExprType::F32: {
-        RegF32 rv = needF32();
-        captureReturnedF32(call, rv);
+        RegF32 rv = captureReturnedF32(call);
         pushF32(rv);
         break;
       }
       case ExprType::F64: {
-        RegF64 rv = needF64();
-        captureReturnedF64(call, rv);
+        RegF64 rv = captureReturnedF64(call);
         pushF64(rv);
         break;
       }
       default:
         MOZ_CRASH("Function return type");
     }
 }
 
@@ -5706,31 +5695,35 @@ BaseCompiler::emitSetGlobal()
         return true;
 
     const GlobalDesc& global = mg_.globals[id];
 
     switch (global.type()) {
       case ValType::I32: {
         RegI32 rv = popI32();
         storeGlobalVarI32(global.offset(), rv);
+        freeI32(rv);
         break;
       }
       case ValType::I64: {
         RegI64 rv = popI64();
         storeGlobalVarI64(global.offset(), rv);
+        freeI64(rv);
         break;
       }
       case ValType::F32: {
         RegF32 rv = popF32();
         storeGlobalVarF32(global.offset(), rv);
+        freeF32(rv);
         break;
       }
       case ValType::F64: {
         RegF64 rv = popF64();
         storeGlobalVarF64(global.offset(), rv);
+        freeF64(rv);
         break;
       }
       default:
         MOZ_CRASH("Global variable type");
         break;
     }
     return true;
 }
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/ion/bug1304640.js
@@ -0,0 +1,10 @@
+
+function f() {
+        return /x/;
+}
+function g() {
+        return (f() == f());
+}
+for (var i = 0; i < 2; ++i) {
+    assertEq(g(), false);
+}
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/ion/bug1304643.js
@@ -0,0 +1,7 @@
+
+var x = Object.create(this);
+var y = '1';
+for (var i = 0; i < 3; ++i) {
+        y += x.y;
+}
+assertEq(y, "11111111");
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/ion/bug1308802.js
@@ -0,0 +1,8 @@
+
+function test() {
+    for (var i = 0; i < 2; ++i) {
+        assertEq(Math.max(-0), -0);
+    }
+}
+test();
+
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/tracelogger/bug1304641.js
@@ -0,0 +1,13 @@
+
+var du = new Debugger();
+if (typeof du.startTraceLogger === "function") {
+    var failed = false;
+    try {
+        newGlobal().startTraceLogger();
+        print("z");
+    } catch (e) {
+        failed = true;
+    }
+
+    assertEq(failed, true);
+}
--- a/js/src/jit/AliasAnalysisShared.cpp
+++ b/js/src/jit/AliasAnalysisShared.cpp
@@ -65,23 +65,20 @@ MaybeUnwrap(const MDefinition* object)
 // Get the object of any load/store. Returns nullptr if not tied to
 // an object.
 static inline const MDefinition*
 GetObject(const MDefinition* ins)
 {
     if (!ins->getAliasSet().isStore() && !ins->getAliasSet().isLoad())
         return nullptr;
 
+    // Note: only return the object if that objects owns that property.
+    // I.e. the poperty isn't on the prototype chain.
     const MDefinition* object = nullptr;
     switch (ins->op()) {
-      case MDefinition::Op_GetPropertyCache:
-        if (!ins->toGetPropertyCache()->idempotent())
-            return nullptr;
-        object = ins->getOperand(0);
-        break;
       case MDefinition::Op_InitializedLength:
       case MDefinition::Op_LoadElement:
       case MDefinition::Op_LoadUnboxedScalar:
       case MDefinition::Op_LoadUnboxedObjectOrNull:
       case MDefinition::Op_LoadUnboxedString:
       case MDefinition::Op_StoreElement:
       case MDefinition::Op_StoreUnboxedObjectOrNull:
       case MDefinition::Op_StoreUnboxedString:
@@ -123,16 +120,17 @@ GetObject(const MDefinition* ins)
       case MDefinition::Op_LoadSlot:
       case MDefinition::Op_StoreSlot:
       case MDefinition::Op_InArray:
       case MDefinition::Op_LoadElementHole:
       case MDefinition::Op_TypedArrayElements:
       case MDefinition::Op_TypedObjectElements:
         object = ins->getOperand(0);
         break;
+      case MDefinition::Op_GetPropertyCache:
       case MDefinition::Op_LoadTypedArrayElementStatic:
       case MDefinition::Op_StoreTypedArrayElementStatic:
       case MDefinition::Op_GetDOMProperty:
       case MDefinition::Op_GetDOMMember:
       case MDefinition::Op_Call:
       case MDefinition::Op_Compare:
       case MDefinition::Op_GetArgumentsObjectArg:
       case MDefinition::Op_SetArgumentsObjectArg:
--- a/js/src/jit/IonAnalysis.cpp
+++ b/js/src/jit/IonAnalysis.cpp
@@ -2024,38 +2024,46 @@ IsRegExpHoistableCall(MCall* call, MDefi
 
     if (name == runtime->names().RegExp_prototype_Exec)
         return IsExclusiveThisArg(call, def);
 
     return false;
 }
 
 static bool
-CanCompareWithoutToPrimitive(MCompare* compare, MDefinition* def)
+CanCompareRegExp(MCompare* compare, MDefinition* def)
 {
+    MDefinition* value;
+    if (compare->lhs() == def) {
+        value = compare->rhs();
+    } else {
+        MOZ_ASSERT(compare->rhs() == def);
+        value = compare->lhs();
+    }
+
+    // Comparing two regexp that weren't cloned will give different result
+    // than if they were cloned.
+    if (value->mightBeType(MIRType::Object))
+        return false;
+
+    // Make sure @@toPrimitive is not called which could notice
+    // the difference between a not cloned/cloned regexp.
+
     JSOp op = compare->jsop();
     // Strict equality comparison won't invoke @@toPrimitive.
     if (op == JSOP_STRICTEQ || op == JSOP_STRICTNE)
         return true;
 
     if (op != JSOP_EQ && op != JSOP_NE) {
         // Relational comparison always invoke @@toPrimitive.
         MOZ_ASSERT(op == JSOP_GT || op == JSOP_GE || op == JSOP_LT || op == JSOP_LE);
         return false;
     }
 
     // Loose equality comparison can invoke @@toPrimitive.
-    MDefinition* value;
-    if (compare->lhs() == def) {
-        value = compare->rhs();
-    } else {
-        MOZ_ASSERT(compare->rhs() == def);
-        value = compare->lhs();
-    }
-
     if (value->mightBeType(MIRType::Boolean) || value->mightBeType(MIRType::String) ||
         value->mightBeType(MIRType::Int32) ||
         value->mightBeType(MIRType::Double) || value->mightBeType(MIRType::Float32) ||
         value->mightBeType(MIRType::Symbol))
     {
         return false;
     }
 
@@ -2111,17 +2119,17 @@ IsRegExpHoistable(MIRGenerator* mir, MDe
             if (useDef->isRegExpMatcher() || useDef->isRegExpTester() ||
                 useDef->isRegExpSearcher())
             {
                 if (IsExclusiveNthOperand(useDef, 0, def))
                     continue;
             } else if (useDef->isLoadFixedSlot() || useDef->isTypeOf()) {
                 continue;
             } else if (useDef->isCompare()) {
-                if (CanCompareWithoutToPrimitive(useDef->toCompare(), def))
+                if (CanCompareRegExp(useDef->toCompare(), def))
                     continue;
             }
             // Instructions that modifies `lastIndex` property.
             else if (useDef->isStoreFixedSlot()) {
                 if (IsExclusiveNthOperand(useDef, 0, def)) {
                     MStoreFixedSlot* store = useDef->toStoreFixedSlot();
                     if (store->slot() == RegExpObject::lastIndexSlot())
                         continue;
--- a/js/src/jit/MIR.cpp
+++ b/js/src/jit/MIR.cpp
@@ -4063,17 +4063,17 @@ MToInt32::foldsTo(TempAllocator& alloc)
                        convert == MacroAssembler::IntConversion_NumbersOrBoolsOnly);
             return MConstant::New(alloc, Int32Value(input->toConstant()->toBoolean()));
           case MIRType::Int32:
             return MConstant::New(alloc, Int32Value(input->toConstant()->toInt32()));
           case MIRType::Float32:
           case MIRType::Double:
             int32_t ival;
             // Only the value within the range of Int32 can be substituted as constant.
-            if (mozilla::NumberEqualsInt32(input->toConstant()->numberToDouble(), &ival))
+            if (mozilla::NumberIsInt32(input->toConstant()->numberToDouble(), &ival))
                 return MConstant::New(alloc, Int32Value(ival));
             break;
           default:
             break;
         }
     }
 
     if (input->type() == MIRType::Int32)
@@ -5073,16 +5073,33 @@ MDefinition*
 MLoadSlot::foldsTo(TempAllocator& alloc)
 {
     if (MDefinition* def = foldsToStore(alloc))
         return def;
 
     return this;
 }
 
+void
+MLoadSlot::printOpcode(GenericPrinter& out) const
+{
+    MDefinition::printOpcode(out);
+    out.printf(" %d", slot());
+}
+
+void
+MStoreSlot::printOpcode(GenericPrinter& out) const
+{
+    PrintOpcodeName(out, op());
+    out.printf(" ", slot());
+    getOperand(0)->printName(out);
+    out.printf(" %d ", slot());
+    getOperand(1)->printName(out);
+}
+
 MDefinition*
 MFunctionEnvironment::foldsTo(TempAllocator& alloc)
 {
     if (!input()->isLambda())
         return this;
 
     return input()->toLambda()->environmentChain();
 }
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -11457,16 +11457,18 @@ class MLoadSlot
     MDefinition* foldsTo(TempAllocator& alloc) override;
 
     AliasSet getAliasSet() const override {
         MOZ_ASSERT(slots()->type() == MIRType::Slots);
         return AliasSet::Load(AliasSet::DynamicSlot);
     }
     AliasType mightAlias(const MDefinition* store) const override;
 
+    void printOpcode(GenericPrinter& out) const override;
+
     ALLOW_CLONE(MLoadSlot)
 };
 
 // Inline call to access a function's environment (scope chain).
 class MFunctionEnvironment
   : public MUnaryInstruction,
     public SingleObjectPolicy::Data
 {
@@ -11537,16 +11539,17 @@ class MStoreSlot
         return needsBarrier_;
     }
     void setNeedsBarrier() {
         needsBarrier_ = true;
     }
     AliasSet getAliasSet() const override {
         return AliasSet::Store(AliasSet::DynamicSlot);
     }
+    void printOpcode(GenericPrinter& out) const override;
 
     ALLOW_CLONE(MStoreSlot)
 };
 
 class MGetNameCache
   : public MUnaryInstruction,
     public SingleObjectPolicy::Data
 {
--- a/js/src/vm/TraceLogging.cpp
+++ b/js/src/vm/TraceLogging.cpp
@@ -234,19 +234,19 @@ TraceLoggerThread::enable(JSContext* cx)
             return false;
         } else {
             MOZ_ASSERT(act->isInterpreter());
             InterpreterFrame* fp = act->asInterpreter()->current();
             MOZ_ASSERT(!fp->runningInJit());
 
             script = fp->script();
             engine = TraceLogger_Interpreter;
-            if (script->compartment() != cx->compartment())
-                return fail(cx, "compartment mismatch");
         }
+        if (script->compartment() != cx->compartment())
+            return fail(cx, "compartment mismatch");
 
         TraceLoggerEvent event(this, TraceLogger_Scripts, script);
         startEvent(event);
         startEvent(engine);
     }
 
     return true;
 }
--- a/layout/base/nsIPresShell.h
+++ b/layout/base/nsIPresShell.h
@@ -1122,18 +1122,18 @@ public:
    * Renders a node aNode to a surface and returns it. The aRegion may be used
    * to clip the rendering. This region is measured in CSS pixels from the
    * edge of the presshell area. The aPoint, aScreenRect and aFlags arguments
    * function in a similar manner as RenderSelection.
    */
   virtual already_AddRefed<mozilla::gfx::SourceSurface>
   RenderNode(nsIDOMNode* aNode,
              nsIntRegion* aRegion,
-             nsIntPoint& aPoint,
-             nsIntRect* aScreenRect,
+             const mozilla::LayoutDeviceIntPoint aPoint,
+             mozilla::LayoutDeviceIntRect* aScreenRect,
              uint32_t aFlags) = 0;
 
   /**
    * Renders a selection to a surface and returns it. This method is primarily
    * intended to create the drag feedback when dragging a selection.
    *
    * aScreenRect will be filled in with the bounding rectangle of the
    * selection area on screen.
@@ -1144,18 +1144,18 @@ public:
    * scaling. Typically, this will be the mouse position, so that the screen
    * rectangle is positioned such that the mouse is over the same point in the
    * scaled image as in the original. When scaling does not occur, the mouse
    * point isn't used because the position can be determined from the displayed
    * frames.
    */
   virtual already_AddRefed<mozilla::gfx::SourceSurface>
   RenderSelection(nsISelection* aSelection,
-                  nsIntPoint& aPoint,
-                  nsIntRect* aScreenRect,
+                  const mozilla::LayoutDeviceIntPoint aPoint,
+                  mozilla::LayoutDeviceIntRect* aScreenRect,
                   uint32_t aFlags) = 0;
 
   void AddWeakFrameInternal(nsWeakFrame* aWeakFrame);
   virtual void AddWeakFrameExternal(nsWeakFrame* aWeakFrame);
 
   void AddWeakFrame(nsWeakFrame* aWeakFrame)
   {
 #ifdef MOZILLA_INTERNAL_API
--- a/layout/base/nsPresShell.cpp
+++ b/layout/base/nsPresShell.cpp
@@ -4962,18 +4962,18 @@ PresShell::CreateRangePaintInfo(nsIDOMRa
   return info;
 }
 
 already_AddRefed<SourceSurface>
 PresShell::PaintRangePaintInfo(const nsTArray<UniquePtr<RangePaintInfo>>& aItems,
                                nsISelection* aSelection,
                                nsIntRegion* aRegion,
                                nsRect aArea,
-                               nsIntPoint& aPoint,
-                               nsIntRect* aScreenRect,
+                               const LayoutDeviceIntPoint aPoint,
+                               LayoutDeviceIntRect* aScreenRect,
                                uint32_t aFlags)
 {
   nsPresContext* pc = GetPresContext();
   if (!pc || aArea.width == 0 || aArea.height == 0)
     return nullptr;
 
   // use the rectangle to create the surface
   nsIntRect pixelArea = aArea.ToOutsidePixels(pc->AppUnitsPerDevPixel());
@@ -5051,24 +5051,34 @@ PresShell::PaintRangePaintInfo(const nsT
   if (!dt || !dt->IsValid()) {
     return nullptr;
   }
 
   RefPtr<gfxContext> ctx = gfxContext::CreateOrNull(dt);
   MOZ_ASSERT(ctx); // already checked the draw target above
 
   if (aRegion) {
+    RefPtr<PathBuilder> builder = dt->CreatePathBuilder(FillRule::FILL_WINDING);
+    
     // Convert aRegion from CSS pixels to dev pixels
     nsIntRegion region =
       aRegion->ToAppUnits(nsPresContext::AppUnitsPerCSSPixel())
         .ToOutsidePixels(pc->AppUnitsPerDevPixel());
     for (auto iter = region.RectIter(); !iter.Done(); iter.Next()) {
       const nsIntRect& rect = iter.Get();
-      ctx->Clip(gfxRect(rect.x, rect.y, rect.width, rect.height));
-    }
+
+      builder->MoveTo(rect.TopLeft());
+      builder->LineTo(rect.TopRight());
+      builder->LineTo(rect.BottomRight());
+      builder->LineTo(rect.BottomLeft());
+      builder->LineTo(rect.TopLeft());
+    }
+
+    RefPtr<Path> path = builder->Finish();
+    ctx->Clip(path);
   }
 
   nsRenderingContext rc(ctx);
 
   gfxMatrix initialTM = ctx->CurrentMatrix();
 
   if (resize)
     initialTM.Scale(scale, scale);
@@ -5111,18 +5121,18 @@ PresShell::PaintRangePaintInfo(const nsT
   frameSelection->SetDisplaySelection(oldDisplaySelection);
 
   return dt->Snapshot();
 }
 
 already_AddRefed<SourceSurface>
 PresShell::RenderNode(nsIDOMNode* aNode,
                       nsIntRegion* aRegion,
-                      nsIntPoint& aPoint,
-                      nsIntRect* aScreenRect,
+                      const LayoutDeviceIntPoint aPoint,
+                      LayoutDeviceIntRect* aScreenRect,
                       uint32_t aFlags)
 {
   // area will hold the size of the surface needed to draw the node, measured
   // from the root frame.
   nsRect area;
   nsTArray<UniquePtr<RangePaintInfo>> rangeItems;
 
   // nothing to draw if the node isn't in a document
@@ -5146,28 +5156,28 @@ PresShell::RenderNode(nsIDOMNode* aNode,
     nsRect rrect = ToAppUnits(rrectPixels, nsPresContext::AppUnitsPerCSSPixel());
     area.IntersectRect(area, rrect);
 
     nsPresContext* pc = GetPresContext();
     if (!pc)
       return nullptr;
 
     // move the region so that it is offset from the topleft corner of the surface
-    aRegion->MoveBy(-pc->AppUnitsToDevPixels(area.x),
-                    -pc->AppUnitsToDevPixels(area.y));
+    aRegion->MoveBy(-nsPresContext::AppUnitsToIntCSSPixels(area.x),
+                    -nsPresContext::AppUnitsToIntCSSPixels(area.y));
   }
 
   return PaintRangePaintInfo(rangeItems, nullptr, aRegion, area, aPoint,
                              aScreenRect, aFlags);
 }
 
 already_AddRefed<SourceSurface>
 PresShell::RenderSelection(nsISelection* aSelection,
-                           nsIntPoint& aPoint,
-                           nsIntRect* aScreenRect,
+                           const LayoutDeviceIntPoint aPoint,
+                           LayoutDeviceIntRect* aScreenRect,
                            uint32_t aFlags)
 {
   // area will hold the size of the surface needed to draw the selection,
   // measured from the root frame.
   nsRect area;
   nsTArray<UniquePtr<RangePaintInfo>> rangeItems;
 
   // iterate over each range and collect them into the rangeItems array.
--- a/layout/base/nsPresShell.h
+++ b/layout/base/nsPresShell.h
@@ -207,24 +207,24 @@ public:
 
   virtual nsresult RenderDocument(const nsRect& aRect, uint32_t aFlags,
                                               nscolor aBackgroundColor,
                                               gfxContext* aThebesContext) override;
 
   virtual already_AddRefed<SourceSurface>
   RenderNode(nsIDOMNode* aNode,
              nsIntRegion* aRegion,
-             nsIntPoint& aPoint,
-             nsIntRect* aScreenRect,
+             const mozilla::LayoutDeviceIntPoint aPoint,
+             mozilla::LayoutDeviceIntRect* aScreenRect,
              uint32_t aFlags) override;
 
   virtual already_AddRefed<SourceSurface>
   RenderSelection(nsISelection* aSelection,
-                  nsIntPoint& aPoint,
-                  nsIntRect* aScreenRect,
+                  const mozilla::LayoutDeviceIntPoint aPoint,
+                  mozilla::LayoutDeviceIntRect* aScreenRect,
                   uint32_t aFlags) override;
 
   virtual already_AddRefed<nsPIDOMWindowOuter> GetRootWindow() override;
 
   virtual LayerManager* GetLayerManager() override;
 
   virtual bool AsyncPanZoomEnabled() override;
 
@@ -572,18 +572,18 @@ protected:
    * aFlags - set RENDER_AUTO_SCALE to scale down large images, but it must not
    *          be set if a custom image was specified
    */
   already_AddRefed<SourceSurface>
   PaintRangePaintInfo(const nsTArray<mozilla::UniquePtr<RangePaintInfo>>& aItems,
                       nsISelection* aSelection,
                       nsIntRegion* aRegion,
                       nsRect aArea,
-                      nsIntPoint& aPoint,
-                      nsIntRect* aScreenRect,
+                      const mozilla::LayoutDeviceIntPoint aPoint,
+                      mozilla::LayoutDeviceIntRect* aScreenRect,
                       uint32_t aFlags);
 
   /**
    * Methods to handle changes to user and UA sheet lists that we get
    * notified about.
    */
   void AddUserSheet(nsISupports* aSheet);
   void AddAgentSheet(nsISupports* aSheet);
--- a/netwerk/protocol/http/HttpChannelParent.cpp
+++ b/netwerk/protocol/http/HttpChannelParent.cpp
@@ -1262,21 +1262,24 @@ HttpChannelParent::StartRedirect(uint32_
   nsAutoCString channelId;
   nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(newChannel);
   if (httpChannel) {
     nsresult rv = httpChannel->GetChannelId(channelId);
     NS_ENSURE_SUCCESS(rv, NS_BINDING_ABORTED);
   }
 
   nsHttpResponseHead *responseHead = mChannel->GetResponseHead();
-  bool result = SendRedirect1Begin(registrarId, uriParams, redirectFlags,
-                                   responseHead ? *responseHead
-                                                : nsHttpResponseHead(),
-                                   secInfoSerialization,
-                                   channelId);
+  bool result = false;
+  if (!mIPCClosed) {
+    result = SendRedirect1Begin(registrarId, uriParams, redirectFlags,
+                                responseHead ? *responseHead
+                                             : nsHttpResponseHead(),
+                                secInfoSerialization,
+                                channelId);
+  }
   if (!result) {
     // Bug 621446 investigation
     mSentRedirect1BeginFailed = true;
     return NS_BINDING_ABORTED;
   }
 
   // Bug 621446 investigation
   mSentRedirect1Begin = true;
--- a/parser/html/javasrc/Tokenizer.java
+++ b/parser/html/javasrc/Tokenizer.java
@@ -1,73 +1,73 @@
 /*
  * Copyright (c) 2005-2007 Henri Sivonen
  * Copyright (c) 2007-2015 Mozilla Foundation
- * Portions of comments Copyright 2004-2010 Apple Computer, Inc., Mozilla 
+ * Portions of comments Copyright 2004-2010 Apple Computer, Inc., Mozilla
  * Foundation, and Opera Software ASA.
  *
- * Permission is hereby granted, free of charge, to any person obtaining a 
- * copy of this software and associated documentation files (the "Software"), 
- * to deal in the Software without restriction, including without limitation 
- * the rights to use, copy, modify, merge, publish, distribute, sublicense, 
- * and/or sell copies of the Software, and to permit persons to whom the 
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
  * Software is furnished to do so, subject to the following conditions:
  *
- * The above copyright notice and this permission notice shall be included in 
+ * The above copyright notice and this permission notice shall be included in
  * all copies or substantial portions of the Software.
  *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  * DEALINGS IN THE SOFTWARE.
  */
 
 /*
- * The comments following this one that use the same comment syntax as this 
- * comment are quotes from the WHATWG HTML 5 spec as of 2 June 2007 
+ * The comments following this one that use the same comment syntax as this
+ * comment are quotes from the WHATWG HTML 5 spec as of 2 June 2007
  * amended as of June 18 2008 and May 31 2010.
  * That document came with this statement:
- * "© Copyright 2004-2010 Apple Computer, Inc., Mozilla Foundation, and 
- * Opera Software ASA. You are granted a license to use, reproduce and 
+ * "© Copyright 2004-2010 Apple Computer, Inc., Mozilla Foundation, and
+ * Opera Software ASA. You are granted a license to use, reproduce and
  * create derivative works of this document."
  */
 
 package nu.validator.htmlparser.impl;
 
+import org.xml.sax.ErrorHandler;
+import org.xml.sax.Locator;
+import org.xml.sax.SAXException;
+import org.xml.sax.SAXParseException;
+
 import nu.validator.htmlparser.annotation.Auto;
 import nu.validator.htmlparser.annotation.CharacterName;
 import nu.validator.htmlparser.annotation.Const;
 import nu.validator.htmlparser.annotation.Inline;
 import nu.validator.htmlparser.annotation.Local;
 import nu.validator.htmlparser.annotation.NoLength;
 import nu.validator.htmlparser.common.EncodingDeclarationHandler;
 import nu.validator.htmlparser.common.Interner;
 import nu.validator.htmlparser.common.TokenHandler;
 import nu.validator.htmlparser.common.XmlViolationPolicy;
 
-import org.xml.sax.ErrorHandler;
-import org.xml.sax.Locator;
-import org.xml.sax.SAXException;
-import org.xml.sax.SAXParseException;
-
 /**
  * An implementation of
  * https://html.spec.whatwg.org/multipage/syntax.html#tokenization
- * 
+ *
  * This class implements the <code>Locator</code> interface. This is not an
  * incidental implementation detail: Users of this class are encouraged to make
  * use of the <code>Locator</code> nature.
- * 
+ *
  * By default, the tokenizer may report data that XML 1.0 bans. The tokenizer
  * can be configured to treat these conditions as fatal or to coerce the infoset
  * to something that XML 1.0 allows.
- * 
+ *
  * @version $Id$
  * @author hsivonen
  */
 public class Tokenizer implements Locator {
 
     private static final int DATA_AND_RCDATA_MASK = ~1;
 
     public static final int DATA = 0;
@@ -524,22 +524,22 @@ public class Tokenizer implements Locato
         this.systemIdentifier = null;
         this.attributes = null;
     }
 
     // ]NOCPP]
 
     /**
      * The constructor.
-     * 
+     *
      * @param tokenHandler
      *            the handler for receiving tokens
      */
     public Tokenizer(TokenHandler tokenHandler
-    // CPPONLY: , boolean viewingXmlSource        
+    // CPPONLY: , boolean viewingXmlSource
     ) {
         this.tokenHandler = tokenHandler;
         this.encodingDeclarationHandler = null;
         // [NOCPP[
         this.newAttributesEachTime = false;
         // ]NOCPP]
         // &CounterClockwiseContourIntegral; is the longest valid char ref and
         // the semicolon never gets appended to the buffer.
@@ -572,117 +572,117 @@ public class Tokenizer implements Locato
     // CPPONLY: boolean isViewingXmlSource() {
     // CPPONLY: return viewingXmlSource;
     // CPPONLY: }
 
     // [NOCPP[
 
     /**
      * Returns the mappingLangToXmlLang.
-     * 
+     *
      * @return the mappingLangToXmlLang
      */
     public boolean isMappingLangToXmlLang() {
         return mappingLangToXmlLang == AttributeName.HTML_LANG;
     }
 
     /**
      * Sets the mappingLangToXmlLang.
-     * 
+     *
      * @param mappingLangToXmlLang
      *            the mappingLangToXmlLang to set
      */
     public void setMappingLangToXmlLang(boolean mappingLangToXmlLang) {
         this.mappingLangToXmlLang = mappingLangToXmlLang ? AttributeName.HTML_LANG
                 : AttributeName.HTML;
     }
 
     /**
      * Sets the error handler.
-     * 
+     *
      * @see org.xml.sax.XMLReader#setErrorHandler(org.xml.sax.ErrorHandler)
      */
     public void setErrorHandler(ErrorHandler eh) {
         this.errorHandler = eh;
     }
 
     public ErrorHandler getErrorHandler() {
         return this.errorHandler;
     }
 
     /**
      * Sets the commentPolicy.
-     * 
+     *
      * @param commentPolicy
      *            the commentPolicy to set
      */
     public void setCommentPolicy(XmlViolationPolicy commentPolicy) {
         this.commentPolicy = commentPolicy;
     }
 
     /**
      * Sets the contentNonXmlCharPolicy.
-     * 
+     *
      * @param contentNonXmlCharPolicy
      *            the contentNonXmlCharPolicy to set
      */
     public void setContentNonXmlCharPolicy(
             XmlViolationPolicy contentNonXmlCharPolicy) {
         if (contentNonXmlCharPolicy != XmlViolationPolicy.ALLOW) {
             throw new IllegalArgumentException(
                     "Must use ErrorReportingTokenizer to set contentNonXmlCharPolicy to non-ALLOW.");
         }
     }
 
     /**
      * Sets the contentSpacePolicy.
-     * 
+     *
      * @param contentSpacePolicy
      *            the contentSpacePolicy to set
      */
     public void setContentSpacePolicy(XmlViolationPolicy contentSpacePolicy) {
         this.contentSpacePolicy = contentSpacePolicy;
     }
 
     /**
      * Sets the xmlnsPolicy.
-     * 
+     *
      * @param xmlnsPolicy
      *            the xmlnsPolicy to set
      */
     public void setXmlnsPolicy(XmlViolationPolicy xmlnsPolicy) {
         if (xmlnsPolicy == XmlViolationPolicy.FATAL) {
             throw new IllegalArgumentException("Can't use FATAL here.");
         }
         this.xmlnsPolicy = xmlnsPolicy;
     }
 
     public void setNamePolicy(XmlViolationPolicy namePolicy) {
         this.namePolicy = namePolicy;
     }
 
     /**
      * Sets the html4ModeCompatibleWithXhtml1Schemata.
-     * 
+     *
      * @param html4ModeCompatibleWithXhtml1Schemata
      *            the html4ModeCompatibleWithXhtml1Schemata to set
      */
     public void setHtml4ModeCompatibleWithXhtml1Schemata(
             boolean html4ModeCompatibleWithXhtml1Schemata) {
         this.html4ModeCompatibleWithXhtml1Schemata = html4ModeCompatibleWithXhtml1Schemata;
     }
 
     // ]NOCPP]
 
     // For the token handler to call
     /**
-     * Sets the tokenizer state and the associated element name. This should 
+     * Sets the tokenizer state and the associated element name. This should
      * only ever used to put the tokenizer into one of the states that have
      * a special end tag expectation.
-     * 
+     *
      * @param specialTokenizerState
      *            the tokenizer state to set
      * @param endTagExpectation
      *            the expected end tag for transitioning back to normal
      */
     public void setStateAndEndTagExpectation(int specialTokenizerState,
             @Local String endTagExpectation) {
         this.stateSave = specialTokenizerState;
@@ -691,20 +691,20 @@ public class Tokenizer implements Locato
         }
         @Auto char[] asArray = Portability.newCharArrayFromLocal(endTagExpectation);
         this.endTagExpectation = ElementName.elementNameByBuffer(asArray, 0,
                 asArray.length, interner);
         endTagExpectationToArray();
     }
 
     /**
-     * Sets the tokenizer state and the associated element name. This should 
+     * Sets the tokenizer state and the associated element name. This should
      * only ever used to put the tokenizer into one of the states that have
      * a special end tag expectation.
-     * 
+     *
      * @param specialTokenizerState
      *            the tokenizer state to set
      * @param endTagExpectation
      *            the expected end tag for transitioning back to normal
      */
     public void setStateAndEndTagExpectation(int specialTokenizerState,
             ElementName endTagExpectation) {
         this.stateSave = specialTokenizerState;
@@ -817,90 +817,96 @@ public class Tokenizer implements Locato
     }
 
     @Inline private void appendCharRefBuf(char c) {
         // CPPONLY: assert charRefBufLen < charRefBuf.length:
         // CPPONLY:     "RELEASE: Attempted to overrun charRefBuf!";
         charRefBuf[charRefBufLen++] = c;
     }
 
-    @Inline private void clearCharRefBufAndAppend(char c) {
-        charRefBuf[0] = c;
-        charRefBufLen = 1;
-    }
-
     private void emitOrAppendCharRefBuf(int returnState) throws SAXException {
         if ((returnState & DATA_AND_RCDATA_MASK) != 0) {
             appendCharRefBufToStrBuf();
         } else {
             if (charRefBufLen > 0) {
                 tokenHandler.characters(charRefBuf, 0, charRefBufLen);
+                charRefBufLen = 0;
             }
         }
     }
 
-    @Inline private void clearStrBufAndAppend(char c) {
-        strBuf[0] = c;
-        strBufLen = 1;
-    }
-
-    @Inline private void clearStrBuf() {
+    @Inline private void clearStrBufAfterUse() {
+        strBufLen = 0;
+    }
+
+    @Inline private void clearStrBufBeforeUse() {
+        assert strBufLen == 0: "strBufLen not reset after previous use!";
+        strBufLen = 0; // no-op in the absence of bugs
+    }
+
+    @Inline private void clearStrBufAfterOneHyphen() {
+        assert strBufLen == 1: "strBufLen length not one!";
+        assert strBuf[0] == '-': "strBuf does not start with a hyphen!";
         strBufLen = 0;
     }
 
     /**
      * Appends to the buffer.
-     * 
+     *
      * @param c
      *            the UTF-16 code unit to append
      */
     @Inline private void appendStrBuf(char c) {
         // CPPONLY: assert strBufLen < strBuf.length: "Previous buffer length insufficient.";
         // CPPONLY: if (strBufLen == strBuf.length) {
         // CPPONLY:     if (!EnsureBufferSpace(1)) {
         // CPPONLY:         assert false: "RELEASE: Unable to recover from buffer reallocation failure";
         // CPPONLY:     } // TODO: Add telemetry when outer if fires but inner does not
         // CPPONLY: }
         strBuf[strBufLen++] = c;
     }
 
     /**
      * The buffer as a String. Currently only used for error reporting.
-     * 
+     *
      * <p>
      * C++ memory note: The return value must be released.
-     * 
+     *
      * @return the buffer as a string
      */
     protected String strBufToString() {
-        return Portability.newStringFromBuffer(strBuf, 0, strBufLen
+        String str = Portability.newStringFromBuffer(strBuf, 0, strBufLen
             // CPPONLY: , tokenHandler
         );
+        clearStrBufAfterUse();
+        return str;
     }
 
     /**
      * Returns the buffer as a local name. The return value is released in
      * emitDoctypeToken().
-     * 
+     *
      * @return the buffer as local name
      */
     private void strBufToDoctypeName() {
         doctypeName = Portability.newLocalNameFromBuffer(strBuf, 0, strBufLen,
                 interner);
+        clearStrBufAfterUse();
     }
 
     /**
      * Emits the buffer as character tokens.
-     * 
+     *
      * @throws SAXException
      *             if the token handler threw
      */
     private void emitStrBuf() throws SAXException {
         if (strBufLen > 0) {
             tokenHandler.characters(strBuf, 0, strBufLen);
+            clearStrBufAfterUse();
         }
     }
 
     @Inline private void appendSecondHyphenToBogusComment() throws SAXException {
         // [NOCPP[
         switch (commentPolicy) {
             case ALTER_INFOSET:
                 appendStrBuf(' ');
@@ -937,16 +943,18 @@ public class Tokenizer implements Locato
 
     @Inline private void adjustDoubleHyphenAndAppendToStrBufAndErr(char c)
             throws SAXException {
         errConsecutiveHyphens();
         // [NOCPP[
         switch (commentPolicy) {
             case ALTER_INFOSET:
                 strBufLen--;
+                // WARNING!!! This expands the worst case of the buffer length
+                // given the length of input!
                 appendStrBuf(' ');
                 appendStrBuf('-');
                 // FALLTHROUGH
             case ALLOW:
                 warn("The document is not mappable to XML 1.0 due to two consecutive hyphens in a comment.");
                 // ]NOCPP]
                 appendStrBuf(c);
                 // [NOCPP[
@@ -970,77 +978,79 @@ public class Tokenizer implements Locato
         strBufLen = newLen;
     }
 
     /**
      * Append the contents of the char reference buffer to the main one.
      */
     @Inline private void appendCharRefBufToStrBuf() {
         appendStrBuf(charRefBuf, 0, charRefBufLen);
+        charRefBufLen = 0;
     }
 
     /**
      * Emits the current comment token.
-     * 
+     *
      * @param pos
      *            TODO
-     * 
+     *
      * @throws SAXException
      */
     private void emitComment(int provisionalHyphens, int pos)
             throws SAXException {
         // [NOCPP[
         if (wantsComments) {
             // ]NOCPP]
             tokenHandler.comment(strBuf, 0, strBufLen
                     - provisionalHyphens);
             // [NOCPP[
         }
         // ]NOCPP]
+        clearStrBufAfterUse();
         cstart = pos + 1;
     }
 
     /**
      * Flushes coalesced character tokens.
-     * 
+     *
      * @param buf
      *            TODO
      * @param pos
      *            TODO
-     * 
+     *
      * @throws SAXException
      */
     protected void flushChars(@NoLength char[] buf, int pos)
             throws SAXException {
         if (pos > cstart) {
             tokenHandler.characters(buf, cstart, pos - cstart);
         }
         cstart = Integer.MAX_VALUE;
     }
 
     /**
      * Reports an condition that would make the infoset incompatible with XML
      * 1.0 as fatal.
-     * 
+     *
      * @param message
      *            the message
      * @throws SAXException
      * @throws SAXParseException
      */
     public void fatal(String message) throws SAXException {
         SAXParseException spe = new SAXParseException(message, this);
         if (errorHandler != null) {
             errorHandler.fatalError(spe);
         }
         throw spe;
     }
 
     /**
      * Reports a Parse Error.
-     * 
+     *
      * @param message
      *            the message
      * @throws SAXException
      */
     public void err(String message) throws SAXException {
         if (errorHandler == null) {
             return;
         }
@@ -1061,32 +1071,33 @@ public class Tokenizer implements Locato
             return;
         }
         SAXParseException spe = new SAXParseException(message, this);
         eh.error(spe);
     }
 
     /**
      * Reports a warning
-     * 
+     *
      * @param message
      *            the message
      * @throws SAXException
      */
     public void warn(String message) throws SAXException {
         if (errorHandler == null) {
             return;
         }
         SAXParseException spe = new SAXParseException(message, this);
         errorHandler.warning(spe);
     }
 
     private void strBufToElementNameString() {
         tagName = ElementName.elementNameByBuffer(strBuf, 0, strBufLen,
                 interner);
+        clearStrBufAfterUse();
     }
 
     private int emitCurrentTagToken(boolean selfClosing, int pos)
             throws SAXException {
         cstart = pos + 1;
         maybeErrSlashInEndTag(selfClosing);
         stateSave = Tokenizer.DATA;
         HtmlAttributes attrs = (attributes == null ? HtmlAttributes.EMPTY_ATTRIBUTES
@@ -1128,16 +1139,17 @@ public class Tokenizer implements Locato
     }
 
     private void attributeNameComplete() throws SAXException {
         attributeName = AttributeName.nameByBuffer(strBuf, 0, strBufLen
         // [NOCPP[
                 , namePolicy != XmlViolationPolicy.ALLOW
                 // ]NOCPP]
                 , interner);
+        clearStrBufAfterUse();
 
         if (attributes == null) {
             attributes = new HtmlAttributes(mappingLangToXmlLang);
         }
 
         /*
          * When the user agent leaves the attribute name state (and before
          * emitting the tag token, if appropriate), the complete attribute's
@@ -1194,16 +1206,18 @@ public class Tokenizer implements Locato
                 // ]NOCPP]
                 // CPPONLY: , attributeLine
                 );
                 // [NOCPP[
             }
             // ]NOCPP]
             attributeName = null; // attributeName has been adopted by the
             // |attributes| object
+        } else {
+            clearStrBufAfterUse();
         }
     }
 
     private void addAttributeWithValue() throws SAXException {
         // [NOCPP[
         if (metaBoundaryPassed && ElementName.META == tagName
                 && AttributeName.CHARSET == attributeName) {
             err("A \u201Ccharset\u201D attribute on a \u201Cmeta\u201D element found after the first 512 bytes.");
@@ -1224,16 +1238,19 @@ public class Tokenizer implements Locato
             attributes.addAttribute(attributeName, val
             // [NOCPP[
                     , xmlnsPolicy
             // ]NOCPP]
             // CPPONLY: , attributeLine
             );
             attributeName = null; // attributeName has been adopted by the
             // |attributes| object
+        } else {
+            // We have a duplicate attribute. Explicitly discard its value.
+            clearStrBufAfterUse();
         }
     }
 
     // [NOCPP[
 
     private static String newAsciiLowerCaseStringFromString(String str) {
         if (str == null) {
             return null;
@@ -1249,17 +1266,17 @@ public class Tokenizer implements Locato
         return new String(buf);
     }
 
     protected void startErrorReporting() throws SAXException {
 
     }
 
     // ]NOCPP]
-    
+
     public void start() throws SAXException {
         initializeWithoutStarting();
         tokenHandler.startTokenization(this);
         // [NOCPP[
         startErrorReporting();
         // ]NOCPP]
     }
 
@@ -1342,16 +1359,24 @@ public class Tokenizer implements Locato
     // [NOCPP[
     private void ensureBufferSpace(int inputLength) throws SAXException {
         // Add 2 to account for emissions of LT_GT, LT_SOLIDUS and RSQB_RSQB.
         // Adding to the general worst case instead of only the
         // TreeBuilder-exposed worst case to avoid re-introducing a bug when
         // unifying the tokenizer and tree builder buffers in the future.
         int worstCase = strBufLen + inputLength + charRefBufLen + 2;
         tokenHandler.ensureBufferSpace(worstCase);
+        if (commentPolicy == XmlViolationPolicy.ALTER_INFOSET) {
+            // When altering infoset, if the comment contents are consecutive
+            // hyphens, each hyphen generates a space, too. These buffer
+            // contents never get emitted as characters() to the tokenHandler,
+            // which is why this calculation happens after the call to
+            // ensureBufferSpace on tokenHandler.
+            worstCase *= 2;
+        }
         if (strBuf == null) {
             // Add an arbitrary small value to avoid immediate reallocation
             // once there are a few characters in the buffer.
             strBuf = new char[worstCase + 128];
         } else if (worstCase > strBuf.length) {
             // HotSpot reportedly allocates memory with 8-byte accuracy, so
             // there's no point in trying to do math here to avoid slop.
             // Maybe we should add some small constant to worstCase here
@@ -1365,76 +1390,76 @@ public class Tokenizer implements Locato
     }
     // ]NOCPP]
 
     @SuppressWarnings("unused") private int stateLoop(int state, char c,
             int pos, @NoLength char[] buf, boolean reconsume, int returnState,
             int endPos) throws SAXException {
         /*
          * Idioms used in this code:
-         * 
-         * 
+         *
+         *
          * Consuming the next input character
-         * 
+         *
          * To consume the next input character, the code does this: if (++pos ==
          * endPos) { break stateloop; } c = checkChar(buf, pos);
-         * 
-         * 
+         *
+         *
          * Staying in a state
-         * 
+         *
          * When there's a state that the tokenizer may stay in over multiple
          * input characters, the state has a wrapper |for(;;)| loop and staying
          * in the state continues the loop.
-         * 
-         * 
+         *
+         *
          * Switching to another state
-         * 
+         *
          * To switch to another state, the code sets the state variable to the
          * magic number of the new state. Then it either continues stateloop or
          * breaks out of the state's own wrapper loop if the target state is
          * right after the current state in source order. (This is a partial
          * workaround for Java's lack of goto.)
-         * 
-         * 
+         *
+         *
          * Reconsume support
-         * 
+         *
          * The spec sometimes says that an input character is reconsumed in
          * another state. If a state can ever be entered so that an input
          * character can be reconsumed in it, the state's code starts with an
          * |if (reconsume)| that sets reconsume to false and skips over the
          * normal code for consuming a new character.
-         * 
+         *
          * To reconsume the current character in another state, the code sets
          * |reconsume| to true and then switches to the other state.
-         * 
-         * 
+         *
+         *
          * Emitting character tokens
-         * 
+         *
          * This method emits character tokens lazily. Whenever a new range of
          * character tokens starts, the field cstart must be set to the start
          * index of the range. The flushChars() method must be called at the end
          * of a range to flush it.
-         * 
-         * 
+         *
+         *
          * U+0000 handling
-         * 
+         *
          * The various states have to handle the replacement of U+0000 with
          * U+FFFD. However, if U+0000 would be reconsumed in another state, the
          * replacement doesn't need to happen, because it's handled by the
          * reconsuming state.
-         * 
-         * 
+         *
+         *
          * LF handling
-         * 
+         *
          * Every state needs to increment the line number upon LF unless the LF
          * gets reconsumed by another state which increments the line number.
-         * 
-         * 
+         *
+         *
          * CR handling
-         * 
+         *
          * Every state needs to handle CR unless the CR gets reconsumed and is
          * handled by the reconsuming state. The CR needs to be handled as if it
          * were and LF, the lastCR field must be set to true and then this
          * method must return. The IO driver will then swallow the next
          * character if it is an LF to coalesce CRLF.
          */
         stateloop: for (;;) {
             switch (state) {
@@ -1450,17 +1475,18 @@ public class Tokenizer implements Locato
                         }
                         switch (c) {
                             case '&':
                                 /*
                                  * U+0026 AMPERSAND (&) Switch to the character
                                  * reference in data state.
                                  */
                                 flushChars(buf, pos);
-                                clearCharRefBufAndAppend(c);
+                                assert charRefBufLen == 0: "charRefBufLen not reset after previous use!";
+                                appendCharRefBuf(c);
                                 setAdditionalAndRememberAmpersandLocation('\u0000');
                                 returnState = state;
                                 state = transition(state, Tokenizer.CONSUME_CHARACTER_REFERENCE, reconsume, pos);
                                 continue stateloop;
                             case '<':
                                 /*
                                  * U+003C LESS-THAN SIGN (<) Switch to the tag
                                  * open state.
@@ -1477,17 +1503,17 @@ public class Tokenizer implements Locato
                                 emitCarriageReturn(buf, pos);
                                 break stateloop;
                             case '\n':
                                 silentLineFeed();
                             default:
                                 /*
                                  * Anything else Emit the input character as a
                                  * character token.
-                                 * 
+                                 *
                                  * Stay in the data state.
                                  */
                                 continue;
                         }
                     }
                     // WARNING FALLTHRU CASE TRANSITION: DON'T REORDER
                 case TAG_OPEN:
                     tagopenloop: for (;;) {
@@ -1510,17 +1536,18 @@ public class Tokenizer implements Locato
                              * token,
                              */
                             endTag = false;
                             /*
                              * set its tag name to the lowercase version of the
                              * input character (add 0x0020 to the character's
                              * code point),
                              */
-                            clearStrBufAndAppend((char) (c + 0x20));
+                            clearStrBufBeforeUse();
+                            appendStrBuf((char) (c + 0x20));
                             /* then switch to the tag name state. */
                             state = transition(state, Tokenizer.TAG_NAME, reconsume, pos);
                             /*
                              * (Don't emit the token yet; further details will
                              * be filled in before it is emitted.)
                              */
                             break tagopenloop;
                             // continue stateloop;
@@ -1529,17 +1556,18 @@ public class Tokenizer implements Locato
                              * U+0061 LATIN SMALL LETTER A through to U+007A
                              * LATIN SMALL LETTER Z Create a new start tag
                              * token,
                              */
                             endTag = false;
                             /*
                              * set its tag name to the input character,
                              */
-                            clearStrBufAndAppend(c);
+                            clearStrBufBeforeUse();
+                            appendStrBuf(c);
                             /* then switch to the tag name state. */
                             state = transition(state, Tokenizer.TAG_NAME, reconsume, pos);
                             /*
                              * (Don't emit the token yet; further details will
                              * be filled in before it is emitted.)
                              */
                             break tagopenloop;
                             // continue stateloop;
@@ -1569,17 +1597,18 @@ public class Tokenizer implements Locato
                                 // CPPONLY: }
                                 /*
                                  * U+003F QUESTION MARK (?) Parse error.
                                  */
                                 errProcessingInstruction();
                                 /*
                                  * Switch to the bogus comment state.
                                  */
-                                clearStrBufAndAppend(c);
+                                clearStrBufBeforeUse();
+                                appendStrBuf(c);
                                 state = transition(state, Tokenizer.BOGUS_COMMENT, reconsume, pos);
                                 continue stateloop;
                             case '>':
                                 /*
                                  * U+003E GREATER-THAN SIGN (>) Parse error.
                                  */
                                 errLtGt();
                                 /*
@@ -1772,17 +1801,18 @@ public class Tokenizer implements Locato
                                      */
                                     c += 0x20;
                                 }
                                 // CPPONLY: attributeLine = line;
                                 /*
                                  * Set that attribute's name to the current
                                  * input character,
                                  */
-                                clearStrBufAndAppend(c);
+                                clearStrBufBeforeUse();
+                                appendStrBuf(c);
                                 /*
                                  * and its value to the empty string.
                                  */
                                 // Will do later.
                                 /*
                                  * Switch to the attribute name state.
                                  */
                                 state = transition(state, Tokenizer.ATTRIBUTE_NAME, reconsume, pos);
@@ -1918,39 +1948,39 @@ public class Tokenizer implements Locato
                                  */
                                 continue;
                             case '"':
                                 /*
                                  * U+0022 QUOTATION MARK (") Switch to the
                                  * attribute value (double-quoted) state.
                                  */
                                 // CPPONLY: attributeLine = line;
-                                clearStrBuf();
+                                clearStrBufBeforeUse();
                                 state = transition(state, Tokenizer.ATTRIBUTE_VALUE_DOUBLE_QUOTED, reconsume, pos);
                                 break beforeattributevalueloop;
                             // continue stateloop;
                             case '&':
                                 /*
                                  * U+0026 AMPERSAND (&) Switch to the attribute
                                  * value (unquoted) state and reconsume this
                                  * input character.
                                  */
                                 // CPPONLY: attributeLine = line;
-                                clearStrBuf();
+                                clearStrBufBeforeUse();
                                 reconsume = true;
                                 state = transition(state, Tokenizer.ATTRIBUTE_VALUE_UNQUOTED, reconsume, pos);
                                 noteUnquotedAttributeValue();
                                 continue stateloop;
                             case '\'':
                                 /*
                                  * U+0027 APOSTROPHE (') Switch to the attribute
                                  * value (single-quoted) state.
                                  */
                                 // CPPONLY: attributeLine = line;
-                                clearStrBuf();
+                                clearStrBufBeforeUse();
                                 state = transition(state, Tokenizer.ATTRIBUTE_VALUE_SINGLE_QUOTED, reconsume, pos);
                                 continue stateloop;
                             case '>':
                                 /*
                                  * U+003E GREATER-THAN SIGN (>) Parse error.
                                  */
                                 errAttributeValueMissing();
                                 /*
@@ -1984,17 +2014,18 @@ public class Tokenizer implements Locato
                                 // [NOCPP[
                                 errHtml4NonNameInUnquotedAttribute(c);
                                 // ]NOCPP]
                                 /*
                                  * Anything else Append the current input
                                  * character to the current attribute's value.
                                  */
                                 // CPPONLY: attributeLine = line;
-                                clearStrBufAndAppend(c);
+                                clearStrBufBeforeUse();
+                                appendStrBuf(c);
                                 /*
                                  * Switch to the attribute value (unquoted)
                                  * state.
                                  */
 
                                 state = transition(state, Tokenizer.ATTRIBUTE_VALUE_UNQUOTED, reconsume, pos);
                                 noteUnquotedAttributeValue();
                                 continue stateloop;
@@ -2027,17 +2058,18 @@ public class Tokenizer implements Locato
                             // continue stateloop;
                             case '&':
                                 /*
                                  * U+0026 AMPERSAND (&) Switch to the character
                                  * reference in attribute value state, with the
                                  * additional allowed character being U+0022
                                  * QUOTATION MARK (").
                                  */
-                                clearCharRefBufAndAppend(c);
+                                assert charRefBufLen == 0: "charRefBufLen not reset after previous use!";
+                                appendCharRefBuf(c);
                                 setAdditionalAndRememberAmpersandLocation('\"');
                                 returnState = state;
                                 state = transition(state, Tokenizer.CONSUME_CHARACTER_REFERENCE, reconsume, pos);
                                 continue stateloop;
                             case '\r':
                                 appendStrBufCarriageReturn();
                                 break stateloop;
                             case '\n':
@@ -2196,17 +2228,18 @@ public class Tokenizer implements Locato
                                 continue stateloop;
                             case '&':
                                 /*
                                  * U+0026 AMPERSAND (&) Switch to the character
                                  * reference in attribute value state, with the
                                  * additional allowed character being U+003E
                                  * GREATER-THAN SIGN (>)
                                  */
-                                clearCharRefBufAndAppend(c);
+                                assert charRefBufLen == 0: "charRefBufLen not reset after previous use!";
+                                appendCharRefBuf(c);
                                 setAdditionalAndRememberAmpersandLocation('>');
                                 returnState = state;
                                 state = transition(state, Tokenizer.CONSUME_CHARACTER_REFERENCE, reconsume, pos);
                                 continue stateloop;
                             case '>':
                                 /*
                                  * U+003E GREATER-THAN SIGN (>) Emit the current
                                  * tag token.
@@ -2335,17 +2368,18 @@ public class Tokenizer implements Locato
                                      * 0x0020 to the character's code point)
                                      */
                                     c += 0x20;
                                 }
                                 /*
                                  * Set that attribute's name to the current
                                  * input character,
                                  */
-                                clearStrBufAndAppend(c);
+                                clearStrBufBeforeUse();
+                                appendStrBuf(c);
                                 /*
                                  * and its value to the empty string.
                                  */
                                 // Will do later.
                                 /*
                                  * Switch to the attribute name state.
                                  */
                                 state = transition(state, Tokenizer.ATTRIBUTE_NAME, reconsume, pos);
@@ -2359,76 +2393,79 @@ public class Tokenizer implements Locato
                             break stateloop;
                         }
                         c = checkChar(buf, pos);
                         /*
                          * If the next two characters are both U+002D
                          * HYPHEN-MINUS characters (-), consume those two
                          * characters, create a comment token whose data is the
                          * empty string, and switch to the comment start state.
-                         * 
+                         *
                          * Otherwise, if the next seven characters are an ASCII
                          * case-insensitive match for the word "DOCTYPE", then
                          * consume those characters and switch to the DOCTYPE
                          * state.
-                         * 
+                         *
                          * Otherwise, if the insertion mode is
                          * "in foreign content" and the current node is not an
                          * element in the HTML namespace and the next seven
                          * characters are an case-sensitive match for the string
                          * "[CDATA[" (the five uppercase letters "CDATA" with a
                          * U+005B LEFT SQUARE BRACKET character before and
                          * after), then consume those characters and switch to
                          * the CDATA section state.
-                         * 
+                         *
                          * Otherwise, is is a parse error. Switch to the bogus
                          * comment state. The next character that is consumed,
                          * if any, is the first character that will be in the
                          * comment.
                          */
                         switch (c) {
                             case '-':
-                                clearStrBufAndAppend(c);
+                                clearStrBufBeforeUse();
+                                appendStrBuf(c);
                                 state = transition(state, Tokenizer.MARKUP_DECLARATION_HYPHEN, reconsume, pos);
                                 break markupdeclarationopenloop;
                             // continue stateloop;
                             case 'd':
                             case 'D':
-                                clearStrBufAndAppend(c);
+                                clearStrBufBeforeUse();
+                                appendStrBuf(c);
                                 index = 0;
                                 state = transition(state, Tokenizer.MARKUP_DECLARATION_OCTYPE, reconsume, pos);
                                 continue stateloop;
                             case '[':
                                 if (tokenHandler.cdataSectionAllowed()) {
-                                    clearStrBufAndAppend(c);
+                                    clearStrBufBeforeUse();
+                                    appendStrBuf(c);
                                     index = 0;
                                     state = transition(state, Tokenizer.CDATA_START, reconsume, pos);
                                     continue stateloop;
                                 }
                                 // else fall through
                             default:
                                 errBogusComment();
-                                clearStrBuf();
+                                clearStrBufBeforeUse();
                                 reconsume = true;
                                 state = transition(state, Tokenizer.BOGUS_COMMENT, reconsume, pos);
                                 continue stateloop;
                         }
                     }
                     // FALLTHRU DON'T REORDER
                 case MARKUP_DECLARATION_HYPHEN:
                     markupdeclarationhyphenloop: for (;;) {
                         if (++pos == endPos) {
                             break stateloop;
                         }
                         c = checkChar(buf, pos);
                         switch (c) {
                             case '\u0000':
                                 break stateloop;
                             case '-':
-                                clearStrBuf();
+                                clearStrBufAfterOneHyphen();
                                 state = transition(state, Tokenizer.COMMENT_START, reconsume, pos);
                                 break markupdeclarationhyphenloop;
                             // continue stateloop;
                             default:
                                 errBogusComment();
                                 reconsume = true;
                                 state = transition(state, Tokenizer.BOGUS_COMMENT, reconsume, pos);
                                 continue stateloop;
@@ -2438,18 +2475,18 @@ public class Tokenizer implements Locato
                 case COMMENT_START:
                     commentstartloop: for (;;) {
                         if (++pos == endPos) {
                             break stateloop;
                         }
                         c = checkChar(buf, pos);
                         /*
                          * Comment start state
-                         * 
-                         * 
+                         *
+                         *
                          * Consume the next input character:
                          */
                         switch (c) {
                             case '-':
                                 /*
                                  * U+002D HYPHEN-MINUS (-) Switch to the comment
                                  * start dash state.
                                  */
@@ -2648,17 +2685,17 @@ public class Tokenizer implements Locato
                 case COMMENT_END_BANG:
                     for (;;) {
                         if (++pos == endPos) {
                             break stateloop;
                         }
                         c = checkChar(buf, pos);
                         /*
                          * Comment end bang state
-                         * 
+                         *
                          * Consume the next input character:
                          */
                         switch (c) {
                             case '>':
                                 /*
                                  * U+003E GREATER-THAN SIGN (>) Emit the comment
                                  * token.
                                  */
@@ -2708,17 +2745,17 @@ public class Tokenizer implements Locato
                     // XXX reorder point
                 case COMMENT_START_DASH:
                     if (++pos == endPos) {
                         break stateloop;
                     }
                     c = checkChar(buf, pos);
                     /*
                      * Comment start dash state
-                     * 
+                     *
                      * Consume the next input character:
                      */
                     switch (c) {
                         case '-':
                             /*
                              * U+002D HYPHEN-MINUS (-) Switch to the comment end
                              * state
                              */
@@ -2772,16 +2809,17 @@ public class Tokenizer implements Locato
                                 errBogusComment();
                                 reconsume = true;
                                 state = transition(state, Tokenizer.BOGUS_COMMENT, reconsume, pos);
                                 continue stateloop;
                             }
                             index++;
                             continue;
                         } else {
+                            clearStrBufAfterUse();
                             cstart = pos; // start coalescing
                             reconsume = true;
                             state = transition(state, Tokenizer.CDATA_SECTION, reconsume, pos);
                             break; // FALL THROUGH continue stateloop;
                         }
                     }
                     // WARNING FALLTHRU CASE TRANSITION: DON'T REORDER
                 case CDATA_SECTION:
@@ -2836,21 +2874,21 @@ public class Tokenizer implements Locato
                 case CDATA_RSQB_RSQB:
                     cdatarsqbrsqb: for (;;) {
                         if (++pos == endPos) {
                             break stateloop;
                         }
                         c = checkChar(buf, pos);
                         switch (c) {
                             case ']':
-                                // Saw a third ]. Emit one ] (logically the 
-                                // first one) and stay in this state to 
+                                // Saw a third ]. Emit one ] (logically the
+                                // first one) and stay in this state to
                                 // remember that the last two characters seen
                                 // have been ]].
-                                tokenHandler.characters(Tokenizer.RSQB_RSQB, 0, 1);                                
+                                tokenHandler.characters(Tokenizer.RSQB_RSQB, 0, 1);
                                 continue;
                             case '>':
                                 cstart = pos + 1;
                                 state = transition(state, Tokenizer.DATA, reconsume, pos);
                                 continue stateloop;
                             default:
                                 tokenHandler.characters(Tokenizer.RSQB_RSQB, 0, 2);
                                 cstart = pos;
@@ -2885,17 +2923,18 @@ public class Tokenizer implements Locato
                                 continue stateloop;
                             case '&':
                                 /*
                                  * U+0026 AMPERSAND (&) Switch to the character
                                  * reference in attribute value state, with the
                                  * + additional allowed character being U+0027
                                  * APOSTROPHE (').
                                  */
-                                clearCharRefBufAndAppend(c);
+                                assert charRefBufLen == 0: "charRefBufLen not reset after previous use!";
+                                appendCharRefBuf(c);
                                 setAdditionalAndRememberAmpersandLocation('\'');
                                 returnState = state;
                                 state = transition(state, Tokenizer.CONSUME_CHARACTER_REFERENCE, reconsume, pos);
                                 break attributevaluesinglequotedloop;
                             // continue stateloop;
                             case '\r':
                                 appendStrBufCarriageReturn();
                                 break stateloop;
@@ -2934,17 +2973,17 @@ public class Tokenizer implements Locato
                      * or appending to the current attribute value. It also
                      * takes care of that in the case when consuming the
                      * character reference fails.
                      */
                     /*
                      * This section defines how to consume a character
                      * reference. This definition is used when parsing character
                      * references in text and in attributes.
-                     * 
+                     *
                      * The behavior depends on the identity of the next
                      * character (the one immediately after the U+0026 AMPERSAND
                      * character):
                      */
                     switch (c) {
                         case ' ':
                         case '\t':
                         case '\n':
@@ -3005,47 +3044,47 @@ public class Tokenizer implements Locato
                             break stateloop;
                         }
                         c = checkChar(buf, pos);
                         if (c == '\u0000') {
                             break stateloop;
                         }
                         /*
                          * The data structure is as follows:
-                         * 
+                         *
                          * HILO_ACCEL is a two-dimensional int array whose major
                          * index corresponds to the second character of the
                          * character reference (code point as index) and the
                          * minor index corresponds to the first character of the
                          * character reference (packed so that A-Z runs from 0
                          * to 25 and a-z runs from 26 to 51). This layout makes
                          * it easier to use the sparseness of the data structure
                          * to omit parts of it: The second dimension of the
                          * table is null when no character reference starts with
                          * the character corresponding to that row.
-                         * 
+                         *
                          * The int value HILO_ACCEL (by these indeces) is zero
                          * if there exists no character reference starting with
                          * that two-letter prefix. Otherwise, the value is an
                          * int that packs two shorts so that the higher short is
                          * the index of the highest character reference name
                          * with that prefix in NAMES and the lower short
                          * corresponds to the index of the lowest character
                          * reference name with that prefix. (It happens that the
                          * first two character reference names share their
                          * prefix so the packed int cannot be 0 by packing the
                          * two shorts.)
-                         * 
+                         *
                          * NAMES is an array of byte arrays where each byte
                          * array encodes the name of a character references as
                          * ASCII. The names omit the first two letters of the
                          * name. (Since storing the first two letters would be
                          * redundant with the data contained in HILO_ACCEL.) The
                          * entries are lexically sorted.
-                         * 
+                         *
                          * For a given index in NAMES, the same index in VALUES
                          * contains the corresponding expansion as an array of
                          * two UTF-16 code units (either the character and
                          * U+0000 or a suggogate pair).
                          */
                         int hilo = 0;
                         if (c <= 'z') {
                             @Const @NoLength int[] row = NamedCharactersAccel.HILO_ACCEL[c];
@@ -3123,28 +3162,28 @@ public class Tokenizer implements Locato
                             } else if (c < NamedCharacters.NAMES[hi].charAt(entCol)) {
                                 hi--;
                             } else {
                                 break hiloop;
                             }
                         }
 
                         if (c == ';') {
-                            // If we see a semicolon, there cannot be a 
+                            // If we see a semicolon, there cannot be a
                             // longer match. Break the loop. However, before
-                            // breaking, take the longest match so far as the 
-                            // candidate, if we are just about to complete a 
+                            // breaking, take the longest match so far as the
+                            // candidate, if we are just about to complete a
                             // match.
                             if (entCol + 1 == NamedCharacters.NAMES[lo].length()) {
                                 candidate = lo;
                                 charRefBufMark = charRefBufLen;
-                            }                            
+                            }
                             break outer;
                         }
-                        
+
                         if (hi < lo) {
                             break outer;
                         }
                         appendCharRefBuf(c);
                         continue;
                     }
 
                     if (candidate == -1) {
@@ -3233,23 +3272,26 @@ public class Tokenizer implements Locato
                             if ((returnState & DATA_AND_RCDATA_MASK) != 0) {
                                 appendStrBuf(charRefBuf, charRefBufMark,
                                         charRefBufLen - charRefBufMark);
                             } else {
                                 tokenHandler.characters(charRefBuf, charRefBufMark,
                                         charRefBufLen - charRefBufMark);
                             }
                         }
+                        // charRefBufLen will be zeroed below!
+
                         // Check if we broke out early with c being the last
                         // character that matched as opposed to being the
-                        // first one that didn't match. In the case of an 
+                        // first one that didn't match. In the case of an
                         // early break, the next run on text should start
-                        // *after* the current character and the current 
+                        // *after* the current character and the current
                         // character shouldn't be reconsumed.
                         boolean earlyBreak = (c == ';' && charRefBufMark == charRefBufLen);
+                        charRefBufLen = 0;
                         if ((returnState & DATA_AND_RCDATA_MASK) == 0) {
                             cstart = earlyBreak ? pos + 1 : pos;
                         }
                         reconsume = !earlyBreak;
                         state = transition(state, returnState, reconsume, pos);
                         continue stateloop;
                         /*
                          * If the markup contains I'm &notit; I tell you, the
@@ -3273,36 +3315,36 @@ public class Tokenizer implements Locato
                      */
                     switch (c) {
                         case 'x':
                         case 'X':
 
                             /*
                              * U+0078 LATIN SMALL LETTER X U+0058 LATIN CAPITAL
                              * LETTER X Consume the X.
-                             * 
+                             *
                              * Follow the steps below, but using the range of
                              * characters U+0030 DIGIT ZERO through to U+0039
                              * DIGIT NINE, U+0061 LATIN SMALL LETTER A through
                              * to U+0066 LATIN SMALL LETTER F, and U+0041 LATIN
                              * CAPITAL LETTER A, through to U+0046 LATIN CAPITAL
                              * LETTER F (in other words, 0-9, A-F, a-f).
-                             * 
+                             *
                              * When it comes to interpreting the number,
                              * interpret it as a hexadecimal number.
                              */
                             appendCharRefBuf(c);
                             state = transition(state, Tokenizer.HEX_NCR_LOOP, reconsume, pos);
                             continue stateloop;
                         default:
                             /*
                              * Anything else Follow the steps below, but using
                              * the range of characters U+0030 DIGIT ZERO through
                              * to U+0039 DIGIT NINE (i.e. just 0-9).
-                             * 
+                             *
                              * When it comes to interpreting the number,
                              * interpret it as a decimal number.
                              */
                             reconsume = true;
                             state = transition(state, Tokenizer.DECIMAL_NRC_LOOP, reconsume, pos);
                             // FALL THROUGH continue stateloop;
                     }
                     // WARNING FALLTHRU CASE TRANSITION: DON'T REORDER
@@ -3349,17 +3391,17 @@ public class Tokenizer implements Locato
                             }
                         } else {
                             /*
                              * If no characters match the range, then don't
                              * consume any characters (and unconsume the U+0023
                              * NUMBER SIGN character and, if appropriate, the X
                              * character). This is a parse error; nothing is
                              * returned.
-                             * 
+                             *
                              * Otherwise, if the next character is a U+003B
                              * SEMICOLON, consume that too. If it isn't, there
                              * is a parse error.
                              */
                             if (!seenDigits) {
                                 errNoDigitsInNCR();
                                 emitOrAppendCharRefBuf(returnState);
                                 if ((returnState & DATA_AND_RCDATA_MASK) == 0) {
@@ -3378,16 +3420,18 @@ public class Tokenizer implements Locato
                                 // FALL THROUGH continue stateloop;
                                 break decimalloop;
                             }
                         }
                     }
                     // WARNING FALLTHRU CASE TRANSITION: DON'T REORDER
                 case HANDLE_NCR_VALUE:
                     // WARNING previous state sets reconsume
+                    // We are not going to emit the contents of charRefBuf.
+                    charRefBufLen = 0;
                     // XXX inline this case if the method size can take it
                     handleNcrValue(returnState);
                     state = transition(state, returnState, reconsume, pos);
                     continue stateloop;
                     // XXX reorder point
                 case HEX_NCR_LOOP:
                     for (;;) {
                         if (++pos == endPos) {
@@ -3442,17 +3486,17 @@ public class Tokenizer implements Locato
                             }
                         } else {
                             /*
                              * If no characters match the range, then don't
                              * consume any characters (and unconsume the U+0023
                              * NUMBER SIGN character and, if appropriate, the X
                              * character). This is a parse error; nothing is
                              * returned.
-                             * 
+                             *
                              * Otherwise, if the next character is a U+003B
                              * SEMICOLON, consume that too. If it isn't, there
                              * is a parse error.
                              */
                             if (!seenDigits) {
                                 errNoDigitsInNCR();
                                 emitOrAppendCharRefBuf(returnState);
                                 if ((returnState & DATA_AND_RCDATA_MASK) == 0) {
@@ -3524,27 +3568,29 @@ public class Tokenizer implements Locato
                             continue stateloop;
                         case '\r':
                             silentCarriageReturn();
                             /* Anything else Parse error. */
                             errGarbageAfterLtSlash();
                             /*
                              * Switch to the bogus comment state.
                              */
-                            clearStrBufAndAppend('\n');
+                            clearStrBufBeforeUse();
+                            appendStrBuf('\n');
                             state = transition(state, Tokenizer.BOGUS_COMMENT, reconsume, pos);
                             break stateloop;
                         case '\n':
                             silentLineFeed();
                             /* Anything else Parse error. */
                             errGarbageAfterLtSlash();
                             /*
                              * Switch to the bogus comment state.
                              */
-                            clearStrBufAndAppend('\n');
+                            clearStrBufBeforeUse();
+                            appendStrBuf(c);
                             state = transition(state, Tokenizer.BOGUS_COMMENT, reconsume, pos);
                             continue stateloop;
                         case '\u0000':
                             c = '\uFFFD';
                             // fall thru
                         default:
                             if (c >= 'A' && c <= 'Z') {
                                 c += 0x20;
@@ -3554,31 +3600,33 @@ public class Tokenizer implements Locato
                                  * U+0061 LATIN SMALL LETTER A through to U+007A
                                  * LATIN SMALL LETTER Z Create a new end tag
                                  * token,
                                  */
                                 endTag = true;
                                 /*
                                  * set its tag name to the input character,
                                  */
-                                clearStrBufAndAppend(c);
+                                clearStrBufBeforeUse();
+                                appendStrBuf(c);
                                 /*
                                  * then switch to the tag name state. (Don't
                                  * emit the token yet; further details will be
                                  * filled in before it is emitted.)
                                  */
                                 state = transition(state, Tokenizer.TAG_NAME, reconsume, pos);
                                 continue stateloop;
                             } else {
                                 /* Anything else Parse error. */
                                 errGarbageAfterLtSlash();
                                 /*
                                  * Switch to the bogus comment state.
                                  */
-                                clearStrBufAndAppend(c);
+                                clearStrBufBeforeUse();
+                                appendStrBuf(c);
                                 state = transition(state, Tokenizer.BOGUS_COMMENT, reconsume, pos);
                                 continue stateloop;
                             }
                     }
                     // XXX reorder point
                 case RCDATA:
                     rcdataloop: for (;;) {
                         if (reconsume) {
@@ -3591,17 +3639,18 @@ public class Tokenizer implements Locato
                         }
                         switch (c) {
                             case '&':
                                 /*
                                  * U+0026 AMPERSAND (&) Switch to the character
                                  * reference in RCDATA state.
                                  */
                                 flushChars(buf, pos);
-                                clearCharRefBufAndAppend(c);
+                                assert charRefBufLen == 0: "charRefBufLen not reset after previous use!";
+                                appendCharRefBuf(c);
                                 setAdditionalAndRememberAmpersandLocation('\u0000');
                                 returnState = state;
                                 state = transition(state, Tokenizer.CONSUME_CHARACTER_REFERENCE, reconsume, pos);
                                 continue stateloop;
                             case '<':
                                 /*
                                  * U+003C LESS-THAN SIGN (<) Switch to the
                                  * RCDATA less-than sign state.
@@ -3676,17 +3725,17 @@ public class Tokenizer implements Locato
                         switch (c) {
                             case '/':
                                 /*
                                  * U+002F SOLIDUS (/) Set the temporary buffer
                                  * to the empty string. Switch to the script
                                  * data end tag open state.
                                  */
                                 index = 0;
-                                clearStrBuf();
+                                clearStrBufBeforeUse();
                                 state = transition(state, Tokenizer.NON_DATA_END_TAG_NAME, reconsume, pos);
                                 break rawtextrcdatalessthansignloop;
                             // FALL THRU continue stateloop;
                             default:
                                 /*
                                  * Otherwise, emit a U+003C LESS-THAN SIGN
                                  * character token
                                  */
@@ -3705,17 +3754,17 @@ public class Tokenizer implements Locato
                 case NON_DATA_END_TAG_NAME:
                     for (;;) {
                         if (++pos == endPos) {
                             break stateloop;
                         }
                         c = checkChar(buf, pos);
                         /*
                          * ASSERT! when entering this state, set index to 0 and
-                         * call clearStrBuf() assert (contentModelElement !=
+                         * call clearStrBufBeforeUse() assert (contentModelElement !=
                          * null); Let's implement the above without lookahead.
                          * strBuf is the 'temporary buffer'.
                          */
                         if (index < endTagExpectationAsArray.length) {
                             char e = endTagExpectationAsArray[index];
                             char folded = c;
                             if (c >= 'A' && c <= 'Z') {
                                 folded += 0x20;
@@ -3738,49 +3787,53 @@ public class Tokenizer implements Locato
                         } else {
                             endTag = true;
                             // XXX replace contentModelElement with different
                             // type
                             tagName = endTagExpectation;
                             switch (c) {
                                 case '\r':
                                     silentCarriageReturn();
+                                    clearStrBufAfterUse(); // strBuf not used
                                     state = transition(state, Tokenizer.BEFORE_ATTRIBUTE_NAME, reconsume, pos);
                                     break stateloop;
                                 case '\n':
                                     silentLineFeed();
                                     // fall thru
                                 case ' ':
                                 case '\t':
                                 case '\u000C':
                                     /*
                                      * U+0009 CHARACTER TABULATION U+000A LINE
                                      * FEED (LF) U+000C FORM FEED (FF) U+0020
                                      * SPACE If the current end tag token is an
                                      * appropriate end tag token, then switch to
                                      * the before attribute name state.
                                      */
+                                    clearStrBufAfterUse(); // strBuf not used
                                     state = transition(state, Tokenizer.BEFORE_ATTRIBUTE_NAME, reconsume, pos);
                                     continue stateloop;
                                 case '/':
                                     /*
                                      * U+002F SOLIDUS (/) If the current end tag
                                      * token is an appropriate end tag token,
                                      * then switch to the self-closing start tag
                                      * state.
                                      */
+                                    clearStrBufAfterUse(); // strBuf not used
                                     state = transition(state, Tokenizer.SELF_CLOSING_START_TAG, reconsume, pos);
                                     continue stateloop;
                                 case '>':
                                     /*
                                      * U+003E GREATER-THAN SIGN (>) If the
                                      * current end tag token is an appropriate
                                      * end tag token, then emit the current tag
                                      * token and switch to the data state.
                                      */
+                                    clearStrBufAfterUse(); // strBuf not used
                                     state = transition(state, emitCurrentTagToken(false, pos), reconsume, pos);
                                     if (shouldSuspend) {
                                         break stateloop;
                                     }
                                     continue stateloop;
                                 default:
                                     /*
                                      * Emit a U+003C LESS-THAN SIGN character
@@ -3827,19 +3880,19 @@ public class Tokenizer implements Locato
                          * token whose data is the concatenation of all the
                          * characters starting from and including the character
                          * that caused the state machine to switch into the
                          * bogus comment state, up to and including the
                          * character immediately before the last consumed
                          * character (i.e. up to the character just before the
                          * U+003E or EOF character). (If the comment was started
                          * by the end of the file (EOF), the token is empty.)
-                         * 
+                         *
                          * Switch to the data state.
-                         * 
+                         *
                          * If the end of the file was reached, reconsume the EOF
                          * character.
                          */
                         switch (c) {
                             case '>':
                                 emitComment(0, pos);
                                 state = transition(state, Tokenizer.DATA, reconsume, pos);
                                 continue stateloop;
@@ -3945,17 +3998,17 @@ public class Tokenizer implements Locato
                         switch (c) {
                             case '/':
                                 /*
                                  * U+002F SOLIDUS (/) Set the temporary buffer
                                  * to the empty string. Switch to the script
                                  * data end tag open state.
                                  */
                                 index = 0;
-                                clearStrBuf();
+                                clearStrBufBeforeUse();
                                 state = transition(state, Tokenizer.NON_DATA_END_TAG_NAME, reconsume, pos);
                                 continue stateloop;
                             case '!':
                                 tokenHandler.characters(Tokenizer.LT_GT, 0, 1);
                                 cstart = pos;
                                 state = transition(state, Tokenizer.SCRIPT_DATA_ESCAPE_START, reconsume, pos);
                                 break scriptdatalessthansignloop; // FALL THRU
                             // continue
@@ -4203,17 +4256,17 @@ public class Tokenizer implements Locato
                         switch (c) {
                             case '/':
                                 /*
                                  * U+002F SOLIDUS (/) Set the temporary buffer
                                  * to the empty string. Switch to the script
                                  * data escaped end tag open state.
                                  */
                                 index = 0;
-                                clearStrBuf();
+                                clearStrBufBeforeUse();
                                 returnState = Tokenizer.SCRIPT_DATA_ESCAPED;
                                 state = transition(state, Tokenizer.NON_DATA_END_TAG_NAME, reconsume, pos);
                                 continue stateloop;
                             case 'S':
                             case 's':
                                 /*
                                  * U+0041 LATIN CAPITAL LETTER A through to
                                  * U+005A LATIN CAPITAL LETTER Z Emit a U+003C
@@ -4685,17 +4738,18 @@ public class Tokenizer implements Locato
                                      */
                                     c += 0x20;
                                 }
                                 /* Anything else Create a new DOCTYPE token. */
                                 /*
                                  * Set the token's name name to the current
                                  * input character.
                                  */
-                                clearStrBufAndAppend(c);
+                                clearStrBufBeforeUse();
+                                appendStrBuf(c);
                                 /*
                                  * Switch to the DOCTYPE name state.
                                  */
                                 state = transition(state, Tokenizer.DOCTYPE_NAME, reconsume, pos);
                                 break beforedoctypenameloop;
                             // continue stateloop;
                         }
                     }
@@ -4906,33 +4960,33 @@ public class Tokenizer implements Locato
                                 /*
                                  * U+0022 QUOTATION MARK (") Parse Error.
                                  */
                                 errNoSpaceBetweenDoctypePublicKeywordAndQuote();
                                 /*
                                  * Set the DOCTYPE token's public identifier to
                                  * the empty string (not missing),
                                  */
-                                clearStrBuf();
+                                clearStrBufBeforeUse();
                                 /*
                                  * then switch to the DOCTYPE public identifier
                                  * (double-quoted) state.
                                  */
                                 state = transition(state, Tokenizer.DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED, reconsume, pos);
                                 continue stateloop;
                             case '\'':
                                 /*
                                  * U+0027 APOSTROPHE (') Parse Error.
                                  */
                                 errNoSpaceBetweenDoctypePublicKeywordAndQuote();
                                 /*
                                  * Set the DOCTYPE token's public identifier to
                                  * the empty string (not missing),
                                  */
-                                clearStrBuf();
+                                clearStrBufBeforeUse();
                                 /*
                                  * then switch to the DOCTYPE public identifier
                                  * (single-quoted) state.
                                  */
                                 state = transition(state, Tokenizer.DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED, reconsume, pos);
                                 continue stateloop;
                             case '>':
                                 /* U+003E GREATER-THAN SIGN (>) Parse error. */
@@ -4993,31 +5047,31 @@ public class Tokenizer implements Locato
                                  */
                                 continue;
                             case '"':
                                 /*
                                  * U+0022 QUOTATION MARK (") Set the DOCTYPE
                                  * token's public identifier to the empty string
                                  * (not missing),
                                  */
-                                clearStrBuf();
+                                clearStrBufBeforeUse();
                                 /*
                                  * then switch to the DOCTYPE public identifier
                                  * (double-quoted) state.
                                  */
                                 state = transition(state, Tokenizer.DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED, reconsume, pos);
                                 break beforedoctypepublicidentifierloop;
                             // continue stateloop;
                             case '\'':
                                 /*
                                  * U+0027 APOSTROPHE (') Set the DOCTYPE token's
                                  * public identifier to the empty string (not
                                  * missing),
                                  */
-                                clearStrBuf();
+                                clearStrBufBeforeUse();
                                 /*
                                  * then switch to the DOCTYPE public identifier
                                  * (single-quoted) state.
                                  */
                                 state = transition(state, Tokenizer.DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED, reconsume, pos);
                                 continue stateloop;
                             case '>':
                                 /* U+003E GREATER-THAN SIGN (>) Parse error. */
@@ -5158,33 +5212,33 @@ public class Tokenizer implements Locato
                                 /*
                                  * U+0022 QUOTATION MARK (") Parse error.
                                  */
                                 errNoSpaceBetweenPublicAndSystemIds();
                                 /*
                                  * Set the DOCTYPE token's system identifier to
                                  * the empty string (not missing),
                                  */
-                                clearStrBuf();
+                                clearStrBufBeforeUse();
                                 /*
                                  * then switch to the DOCTYPE system identifier
                                  * (double-quoted) state.
                                  */
                                 state = transition(state, Tokenizer.DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED, reconsume, pos);
                                 continue stateloop;
                             case '\'':
                                 /*
                                  * U+0027 APOSTROPHE (') Parse error.
                                  */
                                 errNoSpaceBetweenPublicAndSystemIds();
                                 /*
                                  * Set the DOCTYPE token's system identifier to
                                  * the empty string (not missing),
                                  */
-                                clearStrBuf();
+                                clearStrBufBeforeUse();
                                 /*
                                  * then switch to the DOCTYPE system identifier
                                  * (single-quoted) state.
                                  */
                                 state = transition(state, Tokenizer.DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED, reconsume, pos);
                                 continue stateloop;
                             default:
                                 bogusDoctype();
@@ -5239,31 +5293,31 @@ public class Tokenizer implements Locato
                                 state = transition(state, Tokenizer.DATA, reconsume, pos);
                                 continue stateloop;
                             case '"':
                                 /*
                                  * U+0022 QUOTATION MARK (") Set the DOCTYPE
                                  * token's system identifier to the empty string
                                  * (not missing),
                                  */
-                                clearStrBuf();
+                                clearStrBufBeforeUse();
                                 /*
                                  * then switch to the DOCTYPE system identifier
                                  * (double-quoted) state.
                                  */
                                 state = transition(state, Tokenizer.DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED, reconsume, pos);
                                 break betweendoctypepublicandsystemidentifiersloop;
                             // continue stateloop;
                             case '\'':
                                 /*
                                  * U+0027 APOSTROPHE (') Set the DOCTYPE token's
                                  * system identifier to the empty string (not
                                  * missing),
                                  */
-                                clearStrBuf();
+                                clearStrBufBeforeUse();
                                 /*
                                  * then switch to the DOCTYPE system identifier
                                  * (single-quoted) state.
                                  */
                                 state = transition(state, Tokenizer.DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED, reconsume, pos);
                                 continue stateloop;
                             default:
                                 bogusDoctype();
@@ -5502,33 +5556,33 @@ public class Tokenizer implements Locato
                                 /*
                                  * U+0022 QUOTATION MARK (") Parse Error.
                                  */
                                 errNoSpaceBetweenDoctypeSystemKeywordAndQuote();
                                 /*
                                  * Set the DOCTYPE token's system identifier to
                                  * the empty string (not missing),
                                  */
-                                clearStrBuf();
+                                clearStrBufBeforeUse();
                                 /*
                                  * then switch to the DOCTYPE public identifier
                                  * (double-quoted) state.
                                  */
                                 state = transition(state, Tokenizer.DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED, reconsume, pos);
                                 continue stateloop;
                             case '\'':
                                 /*
                                  * U+0027 APOSTROPHE (') Parse Error.
                                  */
                                 errNoSpaceBetweenDoctypeSystemKeywordAndQuote();
                                 /*
                                  * Set the DOCTYPE token's public identifier to
                                  * the empty string (not missing),
                                  */
-                                clearStrBuf();
+                                clearStrBufBeforeUse();
                                 /*
                                  * then switch to the DOCTYPE public identifier
                                  * (single-quoted) state.
                                  */
                                 state = transition(state, Tokenizer.DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED, reconsume, pos);
                                 continue stateloop;
                             case '>':
                                 /* U+003E GREATER-THAN SIGN (>) Parse error. */
@@ -5589,30 +5643,30 @@ public class Tokenizer implements Locato
                                  */
                                 continue;
                             case '"':
                                 /*
                                  * U+0022 QUOTATION MARK (") Set the DOCTYPE
                                  * token's system identifier to the empty string
                                  * (not missing),
                                  */
-                                clearStrBuf();
+                                clearStrBufBeforeUse();
                                 /*
                                  * then switch to the DOCTYPE system identifier
                                  * (double-quoted) state.
                                  */
                                 state = transition(state, Tokenizer.DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED, reconsume, pos);
                                 continue stateloop;
                             case '\'':
                                 /*
                                  * U+0027 APOSTROPHE (') Set the DOCTYPE token's
                                  * system identifier to the empty string (not
                                  * missing),
                                  */
-                                clearStrBuf();
+                                clearStrBufBeforeUse();
                                 /*
                                  * then switch to the DOCTYPE system identifier
                                  * (single-quoted) state.
                                  */
                                 state = transition(state, Tokenizer.DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED, reconsume, pos);
                                 break beforedoctypesystemidentifierloop;
                             // continue stateloop;
                             case '>':
@@ -5806,28 +5860,31 @@ public class Tokenizer implements Locato
         /*
          * if (prevCR && pos != endPos) { // why is this needed? pos--; col--; }
          */
         // Save locals
         stateSave = state;
         returnStateSave = returnState;
         return pos;
     }
-    
+
     // HOTSPOT WORKAROUND INSERTION POINT
-    
+
     // [NOCPP[
-    
+
     protected int transition(int from, int to, boolean reconsume, int pos) throws SAXException {
         return to;
     }
 
     // ]NOCPP]
-    
+
     private void initDoctypeFields() {
+        // Discard the characters "DOCTYPE" accumulated as a potential bogus
+        // comment into strBuf.
+        clearStrBufAfterUse();
         doctypeName = "";
         if (systemIdentifier != null) {
             Portability.releaseString(systemIdentifier);
             systemIdentifier = null;
         }
         if (publicIdentifier != null) {
             Portability.releaseString(publicIdentifier);
             publicIdentifier = null;
@@ -6105,17 +6162,16 @@ public class Tokenizer implements Locato
                 case BOGUS_COMMENT_HYPHEN:
                     // [NOCPP[
                     maybeAppendSpaceToBogusComment();
                     // ]NOCPP]
                     emitComment(0, 0);
                     break eofloop;
                 case MARKUP_DECLARATION_OPEN:
                     errBogusComment();
-                    clearStrBuf();
                     emitComment(0, 0);
                     break eofloop;
                 case MARKUP_DECLARATION_HYPHEN:
                     errBogusComment();
                     emitComment(0, 0);
                     break eofloop;
                 case MARKUP_DECLARATION_OCTYPE:
                     if (index < 6) {
@@ -6316,17 +6372,17 @@ public class Tokenizer implements Locato
                      * or appending to the current attribute value. It also
                      * takes care of that in the case when consuming the entity
                      * fails.
                      */
                     /*
                      * This section defines how to consume an entity. This
                      * definition is used when parsing entities in text and in
                      * attributes.
-                     * 
+                     *
                      * The behavior depends on the identity of the next
                      * character (the one immediately after the U+0026 AMPERSAND
                      * character):
                      */
 
                     emitOrAppendCharRefBuf(returnState);
                     state = returnState;
                     continue;
@@ -6461,16 +6517,17 @@ public class Tokenizer implements Locato
                             if ((returnState & DATA_AND_RCDATA_MASK) != 0) {
                                 appendStrBuf(charRefBuf, charRefBufMark,
                                         charRefBufLen - charRefBufMark);
                             } else {
                                 tokenHandler.characters(charRefBuf, charRefBufMark,
                                         charRefBufLen - charRefBufMark);
                             }
                         }
+                        charRefBufLen = 0;
                         state = returnState;
                         continue eofloop;
                         /*
                          * If the markup contains I'm &notit; I tell you, the
                          * entity is parsed as "not", as in, I'm ¬it; I tell
                          * you. But if the markup was I'm &notin; I tell you,
                          * the entity would be parsed as "notin;", resulting in
                          * I'm ∉ I tell you.
@@ -6479,17 +6536,17 @@ public class Tokenizer implements Locato
                 case CONSUME_NCR:
                 case DECIMAL_NRC_LOOP:
                 case HEX_NCR_LOOP:
                     /*
                      * If no characters match the range, then don't consume any
                      * characters (and unconsume the U+0023 NUMBER SIGN
                      * character and, if appropriate, the X character). This is
                      * a parse error; nothing is returned.
-                     * 
+                     *
                      * Otherwise, if the next character is a U+003B SEMICOLON,
                      * consume that too. If it isn't, there is a parse error.
                      */
                     if (!seenDigits) {
                         errNoDigitsInNCR();
                         emitOrAppendCharRefBuf(returnState);
                         state = returnState;
                         continue;
@@ -6597,60 +6654,60 @@ public class Tokenizer implements Locato
         }
     }
 
     public void requestSuspension() {
         shouldSuspend = true;
     }
 
     // [NOCPP[
-    
+
     public void becomeConfident() {
         confident = true;
     }
 
     /**
      * Returns the nextCharOnNewLine.
-     * 
+     *
      * @return the nextCharOnNewLine
      */
     public boolean isNextCharOnNewLine() {
         return false;
     }
 
     public boolean isPrevCR() {
         return lastCR;
     }
 
     /**
      * Returns the line.
-     * 
+     *
      * @return the line
      */
     public int getLine() {
         return -1;
     }
 
     /**
      * Returns the col.
-     * 
+     *
      * @return the col
      */
     public int getCol() {
         return -1;
     }
 
     // ]NOCPP]
-    
+
     public boolean isInDataState() {
         return (stateSave == DATA);
     }
 
     public void resetToDataState() {
-        strBufLen = 0;
+        clearStrBufAfterUse();
         charRefBufLen = 0;
         stateSave = Tokenizer.DATA;
         // line = 1; XXX line numbers
         lastCR = false;
         index = 0;
         forceQuirks = false;
         additional = '\u0000';
         entCol = -1;
@@ -6973,38 +7030,38 @@ public class Tokenizer implements Locato
     protected void noteAttributeWithoutValue() throws SAXException {
     }
 
     protected void noteUnquotedAttributeValue() throws SAXException {
     }
 
     /**
      * Sets the encodingDeclarationHandler.
-     * 
+     *
      * @param encodingDeclarationHandler
      *            the encodingDeclarationHandler to set
      */
     public void setEncodingDeclarationHandler(
             EncodingDeclarationHandler encodingDeclarationHandler) {
         this.encodingDeclarationHandler = encodingDeclarationHandler;
     }
-    
+
     void destructor() {
         // The translator will write refcount tracing stuff here
         Portability.delete(attributes);
         attributes = null;
     }
-    
+
     // [NOCPP[
-    
+
     /**
-     * Sets an offset to be added to the position reported to 
+     * Sets an offset to be added to the position reported to
      * <code>TransitionHandler</code>.
-     * 
+     *
      * @param offset the offset
      */
     public void setTransitionBaseOffset(int offset) {
-        
-    }
-    
+
+    }
+
     // ]NOCPP]
 
 }
--- a/parser/html/nsHtml5Tokenizer.cpp
+++ b/parser/html/nsHtml5Tokenizer.cpp
@@ -1,30 +1,30 @@
 /*
  * Copyright (c) 2005-2007 Henri Sivonen
  * Copyright (c) 2007-2015 Mozilla Foundation
- * Portions of comments Copyright 2004-2010 Apple Computer, Inc., Mozilla 
+ * Portions of comments Copyright 2004-2010 Apple Computer, Inc., Mozilla
  * Foundation, and Opera Software ASA.
  *
- * Permission is hereby granted, free of charge, to any person obtaining a 
- * copy of this software and associated documentation files (the "Software"), 
- * to deal in the Software without restriction, including without limitation 
- * the rights to use, copy, modify, merge, publish, distribute, sublicense, 
- * and/or sell copies of the Software, and to permit persons to whom the 
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
  * Software is furnished to do so, subject to the following conditions:
  *
- * The above copyright notice and this permission notice shall be included in 
+ * The above copyright notice and this permission notice shall be included in
  * all copies or substantial portions of the Software.
  *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  * DEALINGS IN THE SOFTWARE.
  */
 
 /*
  * THIS IS A GENERATED FILE. PLEASE DO NOT EDIT.
  * Please edit Tokenizer.java instead and regenerate.
  */
 
@@ -212,37 +212,42 @@ nsHtml5Tokenizer::emptyAttributes()
 void 
 nsHtml5Tokenizer::emitOrAppendCharRefBuf(int32_t returnState)
 {
   if ((returnState & NS_HTML5TOKENIZER_DATA_AND_RCDATA_MASK)) {
     appendCharRefBufToStrBuf();
   } else {
     if (charRefBufLen > 0) {
       tokenHandler->characters(charRefBuf, 0, charRefBufLen);
+      charRefBufLen = 0;
     }
   }
 }
 
 nsString* 
 nsHtml5Tokenizer::strBufToString()
 {
-  return nsHtml5Portability::newStringFromBuffer(strBuf, 0, strBufLen, tokenHandler);
+  nsString* str = nsHtml5Portability::newStringFromBuffer(strBuf, 0, strBufLen, tokenHandler);
+  clearStrBufAfterUse();
+  return str;
 }
 
 void 
 nsHtml5Tokenizer::strBufToDoctypeName()
 {
   doctypeName = nsHtml5Portability::newLocalNameFromBuffer(strBuf, 0, strBufLen, interner);
+  clearStrBufAfterUse();
 }
 
 void 
 nsHtml5Tokenizer::emitStrBuf()
 {
   if (strBufLen > 0) {
     tokenHandler->characters(strBuf, 0, strBufLen);
+    clearStrBufAfterUse();
   }
 }
 
 void 
 nsHtml5Tokenizer::appendStrBuf(char16_t* buffer, int32_t offset, int32_t length)
 {
   int32_t newLen = strBufLen + length;
   MOZ_ASSERT(newLen <= strBuf.length, "Previous buffer length insufficient.");
@@ -254,32 +259,34 @@ nsHtml5Tokenizer::appendStrBuf(char16_t*
   nsHtml5ArrayCopy::arraycopy(buffer, offset, strBuf, strBufLen, length);
   strBufLen = newLen;
 }
 
 void 
 nsHtml5Tokenizer::emitComment(int32_t provisionalHyphens, int32_t pos)
 {
   tokenHandler->comment(strBuf, 0, strBufLen - provisionalHyphens);
+  clearStrBufAfterUse();
   cstart = pos + 1;
 }
 
 void 
 nsHtml5Tokenizer::flushChars(char16_t* buf, int32_t pos)
 {
   if (pos > cstart) {
     tokenHandler->characters(buf, cstart, pos - cstart);
   }
   cstart = INT32_MAX;
 }
 
 void 
 nsHtml5Tokenizer::strBufToElementNameString()
 {
   tagName = nsHtml5ElementName::elementNameByBuffer(strBuf, 0, strBufLen, interner);
+  clearStrBufAfterUse();
 }
 
 int32_t 
 nsHtml5Tokenizer::emitCurrentTagToken(bool selfClosing, int32_t pos)
 {
   cstart = pos + 1;
   maybeErrSlashInEndTag(selfClosing);
   stateSave = NS_HTML5TOKENIZER_DATA;
@@ -311,16 +318,17 @@ nsHtml5Tokenizer::emitCurrentTagToken(bo
   }
   return stateSave;
 }
 
 void 
 nsHtml5Tokenizer::attributeNameComplete()
 {
   attributeName = nsHtml5AttributeName::nameByBuffer(strBuf, 0, strBufLen, interner);
+  clearStrBufAfterUse();
   if (!attributes) {
     attributes = new nsHtml5HtmlAttributes(0);
   }
   if (attributes->contains(attributeName)) {
     errDuplicateAttribute();
     attributeName->release();
     attributeName = nullptr;
   }
@@ -328,29 +336,33 @@ nsHtml5Tokenizer::attributeNameComplete(
 
 void 
 nsHtml5Tokenizer::addAttributeWithoutValue()
 {
 
   if (attributeName) {
     attributes->addAttribute(attributeName, nsHtml5Portability::newEmptyString(), attributeLine);
     attributeName = nullptr;
+  } else {
+    clearStrBufAfterUse();
   }
 }
 
 void 
 nsHtml5Tokenizer::addAttributeWithValue()
 {
   if (attributeName) {
     nsString* val = strBufToString();
     if (mViewSource) {
       mViewSource->MaybeLinkifyAttributeValue(attributeName, val);
     }
     attributes->addAttribute(attributeName, val, attributeLine);
     attributeName = nullptr;
+  } else {
+    clearStrBufAfterUse();
   }
 }
 
 void 
 nsHtml5Tokenizer::start()
 {
   initializeWithoutStarting();
   tokenHandler->startTokenization(this);
@@ -422,17 +434,18 @@ nsHtml5Tokenizer::stateLoop(int32_t stat
             if (++pos == endPos) {
               NS_HTML5_BREAK(stateloop);
             }
             c = checkChar(buf, pos);
           }
           switch(c) {
             case '&': {
               flushChars(buf, pos);
-              clearCharRefBufAndAppend(c);
+              MOZ_ASSERT(!charRefBufLen, "charRefBufLen not reset after previous use!");
+              appendCharRefBuf(c);
               setAdditionalAndRememberAmpersandLocation('\0');
               returnState = state;
               state = P::transition(mViewSource, NS_HTML5TOKENIZER_CONSUME_CHARACTER_REFERENCE, reconsume, pos);
               NS_HTML5_CONTINUE(stateloop);
             }
             case '<': {
               flushChars(buf, pos);
               state = P::transition(mViewSource, NS_HTML5TOKENIZER_TAG_OPEN, reconsume, pos);
@@ -459,22 +472,24 @@ nsHtml5Tokenizer::stateLoop(int32_t stat
       case NS_HTML5TOKENIZER_TAG_OPEN: {
         for (; ; ) {
           if (++pos == endPos) {
             NS_HTML5_BREAK(stateloop);
           }
           c = checkChar(buf, pos);
           if (c >= 'A' && c <= 'Z') {
             endTag = false;
-            clearStrBufAndAppend((char16_t) (c + 0x20));
+            clearStrBufBeforeUse();
+            appendStrBuf((char16_t) (c + 0x20));
             state = P::transition(mViewSource, NS_HTML5TOKENIZER_TAG_NAME, reconsume, pos);
             NS_HTML5_BREAK(tagopenloop);
           } else if (c >= 'a' && c <= 'z') {
             endTag = false;
-            clearStrBufAndAppend(c);
+            clearStrBufBeforeUse();
+            appendStrBuf(c);
             state = P::transition(mViewSource, NS_HTML5TOKENIZER_TAG_NAME, reconsume, pos);
             NS_HTML5_BREAK(tagopenloop);
           }
           switch(c) {
             case '!': {
               state = P::transition(mViewSource, NS_HTML5TOKENIZER_MARKUP_DECLARATION_OPEN, reconsume, pos);
               NS_HTML5_CONTINUE(stateloop);
             }
@@ -485,17 +500,18 @@ nsHtml5Tokenizer::stateLoop(int32_t stat
             case '\?': {
               if (viewingXmlSource) {
                 state = P::transition(mViewSource, NS_HTML5TOKENIZER_PROCESSING_INSTRUCTION, reconsume, pos);
                 NS_HTML5_CONTINUE(stateloop);
               }
               if (P::reportErrors) {
                 errProcessingInstruction();
               }
-              clearStrBufAndAppend(c);
+              clearStrBufBeforeUse();
+              appendStrBuf(c);
               state = P::transition(mViewSource, NS_HTML5TOKENIZER_BOGUS_COMMENT, reconsume, pos);
               NS_HTML5_CONTINUE(stateloop);
             }
             case '>': {
               if (P::reportErrors) {
                 errLtGt();
               }
               tokenHandler->characters(nsHtml5Tokenizer::LT_GT, 0, 2);
@@ -612,17 +628,18 @@ nsHtml5Tokenizer::stateLoop(int32_t stat
                 errBadCharBeforeAttributeNameOrNull(c);
               }
             }
             default: {
               if (c >= 'A' && c <= 'Z') {
                 c += 0x20;
               }
               attributeLine = line;
-              clearStrBufAndAppend(c);
+              clearStrBufBeforeUse();
+              appendStrBuf(c);
               state = P::transition(mViewSource, NS_HTML5TOKENIZER_ATTRIBUTE_NAME, reconsume, pos);
               NS_HTML5_BREAK(beforeattributenameloop);
             }
           }
         }
         beforeattributenameloop_end: ;
       }
       case NS_HTML5TOKENIZER_ATTRIBUTE_NAME: {
@@ -705,31 +722,31 @@ nsHtml5Tokenizer::stateLoop(int32_t stat
             }
             case ' ':
             case '\t':
             case '\f': {
               continue;
             }
             case '\"': {
               attributeLine = line;
-              clearStrBuf();
+              clearStrBufBeforeUse();
               state = P::transition(mViewSource, NS_HTML5TOKENIZER_ATTRIBUTE_VALUE_DOUBLE_QUOTED, reconsume, pos);
               NS_HTML5_BREAK(beforeattributevalueloop);
             }
             case '&': {
               attributeLine = line;
-              clearStrBuf();
+              clearStrBufBeforeUse();
               reconsume = true;
               state = P::transition(mViewSource, NS_HTML5TOKENIZER_ATTRIBUTE_VALUE_UNQUOTED, reconsume, pos);
 
               NS_HTML5_CONTINUE(stateloop);
             }
             case '\'': {
               attributeLine = line;
-              clearStrBuf();
+              clearStrBufBeforeUse();
               state = P::transition(mViewSource, NS_HTML5TOKENIZER_ATTRIBUTE_VALUE_SINGLE_QUOTED, reconsume, pos);
               NS_HTML5_CONTINUE(stateloop);
             }
             case '>': {
               if (P::reportErrors) {
                 errAttributeValueMissing();
               }
               addAttributeWithoutValue();
@@ -746,17 +763,18 @@ nsHtml5Tokenizer::stateLoop(int32_t stat
             case '=':
             case '`': {
               if (P::reportErrors) {
                 errLtOrEqualsOrGraveInUnquotedAttributeOrNull(c);
               }
             }
             default: {
               attributeLine = line;
-              clearStrBufAndAppend(c);
+              clearStrBufBeforeUse();
+              appendStrBuf(c);
               state = P::transition(mViewSource, NS_HTML5TOKENIZER_ATTRIBUTE_VALUE_UNQUOTED, reconsume, pos);
 
               NS_HTML5_CONTINUE(stateloop);
             }
           }
         }
         beforeattributevalueloop_end: ;
       }
@@ -772,17 +790,18 @@ nsHtml5Tokenizer::stateLoop(int32_t stat
           }
           switch(c) {
             case '\"': {
               addAttributeWithValue();
               state = P::transition(mViewSource, NS_HTML5TOKENIZER_AFTER_ATTRIBUTE_VALUE_QUOTED, reconsume, pos);
               NS_HTML5_BREAK(attributevaluedoublequotedloop);
             }
             case '&': {
-              clearCharRefBufAndAppend(c);
+              MOZ_ASSERT(!charRefBufLen, "charRefBufLen not reset after previous use!");
+              appendCharRefBuf(c);
               setAdditionalAndRememberAmpersandLocation('\"');
               returnState = state;
               state = P::transition(mViewSource, NS_HTML5TOKENIZER_CONSUME_CHARACTER_REFERENCE, reconsume, pos);
               NS_HTML5_CONTINUE(stateloop);
             }
             case '\r': {
               appendStrBufCarriageReturn();
               NS_HTML5_BREAK(stateloop);
@@ -892,17 +911,18 @@ nsHtml5Tokenizer::stateLoop(int32_t stat
             case ' ':
             case '\t':
             case '\f': {
               addAttributeWithValue();
               state = P::transition(mViewSource, NS_HTML5TOKENIZER_BEFORE_ATTRIBUTE_NAME, reconsume, pos);
               NS_HTML5_CONTINUE(stateloop);
             }
             case '&': {
-              clearCharRefBufAndAppend(c);
+              MOZ_ASSERT(!charRefBufLen, "charRefBufLen not reset after previous use!");
+              appendCharRefBuf(c);
               setAdditionalAndRememberAmpersandLocation('>');
               returnState = state;
               state = P::transition(mViewSource, NS_HTML5TOKENIZER_CONSUME_CHARACTER_REFERENCE, reconsume, pos);
               NS_HTML5_CONTINUE(stateloop);
             }
             case '>': {
               addAttributeWithValue();
               state = P::transition(mViewSource, emitCurrentTagToken(false, pos), reconsume, pos);
@@ -977,55 +997,59 @@ nsHtml5Tokenizer::stateLoop(int32_t stat
                 errQuoteOrLtInAttributeNameOrNull(c);
               }
             }
             default: {
               addAttributeWithoutValue();
               if (c >= 'A' && c <= 'Z') {
                 c += 0x20;
               }
-              clearStrBufAndAppend(c);
+              clearStrBufBeforeUse();
+              appendStrBuf(c);
               state = P::transition(mViewSource, NS_HTML5TOKENIZER_ATTRIBUTE_NAME, reconsume, pos);
               NS_HTML5_CONTINUE(stateloop);
             }
           }
         }
       }
       case NS_HTML5TOKENIZER_MARKUP_DECLARATION_OPEN: {
         for (; ; ) {
           if (++pos == endPos) {
             NS_HTML5_BREAK(stateloop);
           }
           c = checkChar(buf, pos);
           switch(c) {
             case '-': {
-              clearStrBufAndAppend(c);
+              clearStrBufBeforeUse();
+              appendStrBuf(c);
               state = P::transition(mViewSource, NS_HTML5TOKENIZER_MARKUP_DECLARATION_HYPHEN, reconsume, pos);
               NS_HTML5_BREAK(markupdeclarationopenloop);
             }
             case 'd':
             case 'D': {
-              clearStrBufAndAppend(c);
+              clearStrBufBeforeUse();
+              appendStrBuf(c);
               index = 0;
               state = P::transition(mViewSource, NS_HTML5TOKENIZER_MARKUP_DECLARATION_OCTYPE, reconsume, pos);
               NS_HTML5_CONTINUE(stateloop);
             }
             case '[': {
               if (tokenHandler->cdataSectionAllowed()) {
-                clearStrBufAndAppend(c);
+                clearStrBufBeforeUse();
+                appendStrBuf(c);
                 index = 0;
                 state = P::transition(mViewSource, NS_HTML5TOKENIZER_CDATA_START, reconsume, pos);
                 NS_HTML5_CONTINUE(stateloop);
               }
             }
             default: {
               if (P::reportErrors) {
                 errBogusComment();
               }
-              clearStrBuf();
+              clearStrBufBeforeUse();
               reconsume = true;
               state = P::transition(mViewSource, NS_HTML5TOKENIZER_BOGUS_COMMENT, reconsume, pos);
               NS_HTML5_CONTINUE(stateloop);
             }
           }
         }
         markupdeclarationopenloop_end: ;
       }
@@ -1035,17 +1059,17 @@ nsHtml5Tokenizer::stateLoop(int32_t stat
             NS_HTML5_BREAK(stateloop);
           }
           c = checkChar(buf, pos);
           switch(c) {
             case '\0': {
               NS_HTML5_BREAK(stateloop);
             }
             case '-': {
-              clearStrBuf();
+              clearStrBufAfterOneHyphen();
               state = P::transition(mViewSource, NS_HTML5TOKENIZER_COMMENT_START, reconsume, pos);
               NS_HTML5_BREAK(markupdeclarationhyphenloop);
             }
             default: {
               if (P::reportErrors) {
                 errBogusComment();
               }
               reconsume = true;
@@ -1299,16 +1323,17 @@ nsHtml5Tokenizer::stateLoop(int32_t stat
               }
               reconsume = true;
               state = P::transition(mViewSource, NS_HTML5TOKENIZER_BOGUS_COMMENT, reconsume, pos);
               NS_HTML5_CONTINUE(stateloop);
             }
             index++;
             continue;
           } else {
+            clearStrBufAfterUse();
             cstart = pos;
             reconsume = true;
             state = P::transition(mViewSource, NS_HTML5TOKENIZER_CDATA_SECTION, reconsume, pos);
             break;
           }
         }
       }
       case NS_HTML5TOKENIZER_CDATA_SECTION: {
@@ -1406,17 +1431,18 @@ nsHtml5Tokenizer::stateLoop(int32_t stat
           }
           switch(c) {
             case '\'': {
               addAttributeWithValue();
               state = P::transition(mViewSource, NS_HTML5TOKENIZER_AFTER_ATTRIBUTE_VALUE_QUOTED, reconsume, pos);
               NS_HTML5_CONTINUE(stateloop);
             }
             case '&': {
-              clearCharRefBufAndAppend(c);
+              MOZ_ASSERT(!charRefBufLen, "charRefBufLen not reset after previous use!");
+              appendCharRefBuf(c);
               setAdditionalAndRememberAmpersandLocation('\'');
               returnState = state;
               state = P::transition(mViewSource, NS_HTML5TOKENIZER_CONSUME_CHARACTER_REFERENCE, reconsume, pos);
               NS_HTML5_BREAK(attributevaluesinglequotedloop);
             }
             case '\r': {
               appendStrBufCarriageReturn();
               NS_HTML5_BREAK(stateloop);
@@ -1638,16 +1664,17 @@ nsHtml5Tokenizer::stateLoop(int32_t stat
           if (charRefBufMark < charRefBufLen) {
             if ((returnState & NS_HTML5TOKENIZER_DATA_AND_RCDATA_MASK)) {
               appendStrBuf(charRefBuf, charRefBufMark, charRefBufLen - charRefBufMark);
             } else {
               tokenHandler->characters(charRefBuf, charRefBufMark, charRefBufLen - charRefBufMark);
             }
           }
           bool earlyBreak = (c == ';' && charRefBufMark == charRefBufLen);
+          charRefBufLen = 0;
           if (!(returnState & NS_HTML5TOKENIZER_DATA_AND_RCDATA_MASK)) {
             cstart = earlyBreak ? pos + 1 : pos;
           }
           reconsume = !earlyBreak;
           state = P::transition(mViewSource, returnState, reconsume, pos);
           NS_HTML5_CONTINUE(stateloop);
         }
       }
@@ -1731,16 +1758,17 @@ nsHtml5Tokenizer::stateLoop(int32_t stat
               state = P::transition(mViewSource, NS_HTML5TOKENIZER_HANDLE_NCR_VALUE, reconsume, pos);
               NS_HTML5_BREAK(decimalloop);
             }
           }
         }
         decimalloop_end: ;
       }
       case NS_HTML5TOKENIZER_HANDLE_NCR_VALUE: {
+        charRefBufLen = 0;
         handleNcrValue(returnState);
         state = P::transition(mViewSource, returnState, reconsume, pos);
         NS_HTML5_CONTINUE(stateloop);
       }
       case NS_HTML5TOKENIZER_HEX_NCR_LOOP: {
         for (; ; ) {
           if (++pos == endPos) {
             NS_HTML5_BREAK(stateloop);
@@ -1856,46 +1884,50 @@ nsHtml5Tokenizer::stateLoop(int32_t stat
             state = P::transition(mViewSource, NS_HTML5TOKENIZER_DATA, reconsume, pos);
             NS_HTML5_CONTINUE(stateloop);
           }
           case '\r': {
             silentCarriageReturn();
             if (P::reportErrors) {
               errGarbageAfterLtSlash();
             }
-            clearStrBufAndAppend('\n');
+            clearStrBufBeforeUse();
+            appendStrBuf('\n');
             state = P::transition(mViewSource, NS_HTML5TOKENIZER_BOGUS_COMMENT, reconsume, pos);
             NS_HTML5_BREAK(stateloop);
           }
           case '\n': {
             silentLineFeed();
             if (P::reportErrors) {
               errGarbageAfterLtSlash();
             }
-            clearStrBufAndAppend('\n');
+            clearStrBufBeforeUse();
+            appendStrBuf(c);
             state = P::transition(mViewSource, NS_HTML5TOKENIZER_BOGUS_COMMENT, reconsume, pos);
             NS_HTML5_CONTINUE(stateloop);
           }
           case '\0': {
             c = 0xfffd;
           }
           default: {
             if (c >= 'A' && c <= 'Z') {
               c += 0x20;
             }
             if (c >= 'a' && c <= 'z') {
               endTag = true;
-              clearStrBufAndAppend(c);
+              clearStrBufBeforeUse();
+              appendStrBuf(c);
               state = P::transition(mViewSource, NS_HTML5TOKENIZER_TAG_NAME, reconsume, pos);
               NS_HTML5_CONTINUE(stateloop);
             } else {
               if (P::reportErrors) {
                 errGarbageAfterLtSlash();
               }
-              clearStrBufAndAppend(c);
+              clearStrBufBeforeUse();
+              appendStrBuf(c);
               state = P::transition(mViewSource, NS_HTML5TOKENIZER_BOGUS_COMMENT, reconsume, pos);
               NS_HTML5_CONTINUE(stateloop);
             }
           }
         }
       }
       case NS_HTML5TOKENIZER_RCDATA: {
         for (; ; ) {
@@ -1905,17 +1937,18 @@ nsHtml5Tokenizer::stateLoop(int32_t stat
             if (++pos == endPos) {
               NS_HTML5_BREAK(stateloop);
             }
             c = checkChar(buf, pos);
           }
           switch(c) {
             case '&': {
               flushChars(buf, pos);
-              clearCharRefBufAndAppend(c);
+              MOZ_ASSERT(!charRefBufLen, "charRefBufLen not reset after previous use!");
+              appendCharRefBuf(c);
               setAdditionalAndRememberAmpersandLocation('\0');
               returnState = state;
               state = P::transition(mViewSource, NS_HTML5TOKENIZER_CONSUME_CHARACTER_REFERENCE, reconsume, pos);
               NS_HTML5_CONTINUE(stateloop);
             }
             case '<': {
               flushChars(buf, pos);
               returnState = state;
@@ -1979,17 +2012,17 @@ nsHtml5Tokenizer::stateLoop(int32_t stat
         for (; ; ) {
           if (++pos == endPos) {
             NS_HTML5_BREAK(stateloop);
           }
           c = checkChar(buf, pos);
           switch(c) {
             case '/': {
               index = 0;
-              clearStrBuf();
+              clearStrBufBeforeUse();
               state = P::transition(mViewSource, NS_HTML5TOKENIZER_NON_DATA_END_TAG_NAME, reconsume, pos);
               NS_HTML5_BREAK(rawtextrcdatalessthansignloop);
             }
             default: {
               tokenHandler->characters(nsHtml5Tokenizer::LT_GT, 0, 1);
               cstart = pos;
               reconsume = true;
               state = P::transition(mViewSource, returnState, reconsume, pos);
@@ -2023,33 +2056,37 @@ nsHtml5Tokenizer::stateLoop(int32_t stat
             index++;
             continue;
           } else {
             endTag = true;
             tagName = endTagExpectation;
             switch(c) {
               case '\r': {
                 silentCarriageReturn();
+                clearStrBufAfterUse();
                 state = P::transition(mViewSource, NS_HTML5TOKENIZER_BEFORE_ATTRIBUTE_NAME, reconsume, pos);
                 NS_HTML5_BREAK(stateloop);
               }
               case '\n': {
                 silentLineFeed();
               }
               case ' ':
               case '\t':
               case '\f': {
+                clearStrBufAfterUse();
                 state = P::transition(mViewSource, NS_HTML5TOKENIZER_BEFORE_ATTRIBUTE_NAME, reconsume, pos);
                 NS_HTML5_CONTINUE(stateloop);
               }
               case '/': {
+                clearStrBufAfterUse();
                 state = P::transition(mViewSource, NS_HTML5TOKENIZER_SELF_CLOSING_START_TAG, reconsume, pos);
                 NS_HTML5_CONTINUE(stateloop);
               }
               case '>': {
+                clearStrBufAfterUse();
                 state = P::transition(mViewSource, emitCurrentTagToken(false, pos), reconsume, pos);
                 if (shouldSuspend) {
                   NS_HTML5_BREAK(stateloop);
                 }
                 NS_HTML5_CONTINUE(stateloop);
               }
               default: {
                 tokenHandler->characters(nsHtml5Tokenizer::LT_SOLIDUS, 0, 2);
@@ -2183,17 +2220,17 @@ nsHtml5Tokenizer::stateLoop(int32_t stat
         for (; ; ) {
           if (++pos == endPos) {
             NS_HTML5_BREAK(stateloop);
           }
           c = checkChar(buf, pos);
           switch(c) {
             case '/': {
               index = 0;
-              clearStrBuf();
+              clearStrBufBeforeUse();
               state = P::transition(mViewSource, NS_HTML5TOKENIZER_NON_DATA_END_TAG_NAME, reconsume, pos);
               NS_HTML5_CONTINUE(stateloop);
             }
             case '!': {
               tokenHandler->characters(nsHtml5Tokenizer::LT_GT, 0, 1);
               cstart = pos;
               state = P::transition(mViewSource, NS_HTML5TOKENIZER_SCRIPT_DATA_ESCAPE_START, reconsume, pos);
               NS_HTML5_BREAK(scriptdatalessthansignloop);
@@ -2368,17 +2405,17 @@ nsHtml5Tokenizer::stateLoop(int32_t stat
         for (; ; ) {
           if (++pos == endPos) {
             NS_HTML5_BREAK(stateloop);
           }
           c = checkChar(buf, pos);
           switch(c) {
             case '/': {
               index = 0;
-              clearStrBuf();
+              clearStrBufBeforeUse();
               returnState = NS_HTML5TOKENIZER_SCRIPT_DATA_ESCAPED;
               state = P::transition(mViewSource, NS_HTML5TOKENIZER_NON_DATA_END_TAG_NAME, reconsume, pos);
               NS_HTML5_CONTINUE(stateloop);
             }
             case 'S':
             case 's': {
               tokenHandler->characters(nsHtml5Tokenizer::LT_GT, 0, 1);
               cstart = pos;
@@ -2724,17 +2761,18 @@ nsHtml5Tokenizer::stateLoop(int32_t stat
             }
             case '\0': {
               c = 0xfffd;
             }
             default: {
               if (c >= 'A' && c <= 'Z') {
                 c += 0x20;
               }
-              clearStrBufAndAppend(c);
+              clearStrBufBeforeUse();
+              appendStrBuf(c);
               state = P::transition(mViewSource, NS_HTML5TOKENIZER_DOCTYPE_NAME, reconsume, pos);
               NS_HTML5_BREAK(beforedoctypenameloop);
             }
           }
         }
         beforedoctypenameloop_end: ;
       }
       case NS_HTML5TOKENIZER_DOCTYPE_NAME: {
@@ -2876,25 +2914,25 @@ nsHtml5Tokenizer::stateLoop(int32_t stat
             case '\f': {
               state = P::transition(mViewSource, NS_HTML5TOKENIZER_BEFORE_DOCTYPE_PUBLIC_IDENTIFIER, reconsume, pos);
               NS_HTML5_BREAK(afterdoctypepublickeywordloop);
             }
             case '\"': {
               if (P::reportErrors) {
                 errNoSpaceBetweenDoctypePublicKeywordAndQuote();
               }
-              clearStrBuf();
+              clearStrBufBeforeUse();
               state = P::transition(mViewSource, NS_HTML5TOKENIZER_DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED, reconsume, pos);
               NS_HTML5_CONTINUE(stateloop);
             }
             case '\'': {
               if (P::reportErrors) {
                 errNoSpaceBetweenDoctypePublicKeywordAndQuote();
               }
-              clearStrBuf();
+              clearStrBufBeforeUse();
               state = P::transition(mViewSource, NS_HTML5TOKENIZER_DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED, reconsume, pos);
               NS_HTML5_CONTINUE(stateloop);
             }
             case '>': {
               if (P::reportErrors) {
                 errExpectedPublicId();
               }
               forceQuirks = true;
@@ -2926,22 +2964,22 @@ nsHtml5Tokenizer::stateLoop(int32_t stat
               silentLineFeed();
             }
             case ' ':
             case '\t':
             case '\f': {
               continue;
             }
             case '\"': {
-              clearStrBuf();
+              clearStrBufBeforeUse();
               state = P::transition(mViewSource, NS_HTML5TOKENIZER_DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED, reconsume, pos);
               NS_HTML5_BREAK(beforedoctypepublicidentifierloop);
             }
             case '\'': {
-              clearStrBuf();
+              clearStrBufBeforeUse();
               state = P::transition(mViewSource, NS_HTML5TOKENIZER_DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED, reconsume, pos);
               NS_HTML5_CONTINUE(stateloop);
             }
             case '>': {
               if (P::reportErrors) {
                 errExpectedPublicId();
               }
               forceQuirks = true;
@@ -3024,25 +3062,25 @@ nsHtml5Tokenizer::stateLoop(int32_t stat
               emitDoctypeToken(pos);
               state = P::transition(mViewSource, NS_HTML5TOKENIZER_DATA, reconsume, pos);
               NS_HTML5_CONTINUE(stateloop);
             }
             case '\"': {
               if (P::reportErrors) {
                 errNoSpaceBetweenPublicAndSystemIds();
               }
-              clearStrBuf();
+              clearStrBufBeforeUse();
               state = P::transition(mViewSource, NS_HTML5TOKENIZER_DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED, reconsume, pos);
               NS_HTML5_CONTINUE(stateloop);
             }
             case '\'': {
               if (P::reportErrors) {
                 errNoSpaceBetweenPublicAndSystemIds();
               }
-              clearStrBuf();
+              clearStrBufBeforeUse();
               state = P::transition(mViewSource, NS_HTML5TOKENIZER_DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED, reconsume, pos);
               NS_HTML5_CONTINUE(stateloop);
             }
             default: {
               bogusDoctype();
               state = P::transition(mViewSource, NS_HTML5TOKENIZER_BOGUS_DOCTYPE, reconsume, pos);
               NS_HTML5_CONTINUE(stateloop);
             }
@@ -3070,22 +3108,22 @@ nsHtml5Tokenizer::stateLoop(int32_t stat
               continue;
             }
             case '>': {
               emitDoctypeToken(pos);
               state = P::transition(mViewSource, NS_HTML5TOKENIZER_DATA, reconsume, pos);
               NS_HTML5_CONTINUE(stateloop);
             }
             case '\"': {
-              clearStrBuf();
+              clearStrBufBeforeUse();
               state = P::transition(mViewSource, NS_HTML5TOKENIZER_DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED, reconsume, pos);
               NS_HTML5_BREAK(betweendoctypepublicandsystemidentifiersloop);
             }
             case '\'': {
-              clearStrBuf();
+              clearStrBufBeforeUse();
               state = P::transition(mViewSource, NS_HTML5TOKENIZER_DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED, reconsume, pos);
               NS_HTML5_CONTINUE(stateloop);
             }
             default: {
               bogusDoctype();
               state = P::transition(mViewSource, NS_HTML5TOKENIZER_BOGUS_DOCTYPE, reconsume, pos);
               NS_HTML5_CONTINUE(stateloop);
             }
@@ -3247,25 +3285,25 @@ nsHtml5Tokenizer::stateLoop(int32_t stat
             case '\f': {
               state = P::transition(mViewSource, NS_HTML5TOKENIZER_BEFORE_DOCTYPE_SYSTEM_IDENTIFIER, reconsume, pos);
               NS_HTML5_BREAK(afterdoctypesystemkeywordloop);
             }
             case '\"': {
               if (P::reportErrors) {
                 errNoSpaceBetweenDoctypeSystemKeywordAndQuote();
               }
-              clearStrBuf();
+              clearStrBufBeforeUse();
               state = P::transition(mViewSource, NS_HTML5TOKENIZER_DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED, reconsume, pos);
               NS_HTML5_CONTINUE(stateloop);
             }
             case '\'': {
               if (P::reportErrors) {
                 errNoSpaceBetweenDoctypeSystemKeywordAndQuote();
               }
-              clearStrBuf();
+              clearStrBufBeforeUse();
               state = P::transition(mViewSource, NS_HTML5TOKENIZER_DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED, reconsume, pos);
               NS_HTML5_CONTINUE(stateloop);
             }
             case '>': {
               if (P::reportErrors) {
                 errExpectedPublicId();
               }
               forceQuirks = true;
@@ -3297,22 +3335,22 @@ nsHtml5Tokenizer::stateLoop(int32_t stat
               silentLineFeed();
             }
             case ' ':
             case '\t':
             case '\f': {
               continue;
             }
             case '\"': {
-              clearStrBuf();
+              clearStrBufBeforeUse();
               state = P::transition(mViewSource, NS_HTML5TOKENIZER_DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED, reconsume, pos);
               NS_HTML5_CONTINUE(stateloop);
             }
             case '\'': {
-              clearStrBuf();
+              clearStrBufBeforeUse();
               state = P::transition(mViewSource, NS_HTML5TOKENIZER_DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED, reconsume, pos);
               NS_HTML5_BREAK(beforedoctypesystemidentifierloop);
             }
             case '>': {
               if (P::reportErrors) {
                 errExpectedSystemId();
               }
               forceQuirks = true;
@@ -3450,16 +3488,17 @@ nsHtml5Tokenizer::stateLoop(int32_t stat
   stateSave = state;
   returnStateSave = returnState;
   return pos;
 }
 
 void 
 nsHtml5Tokenizer::initDoctypeFields()
 {
+  clearStrBufAfterUse();
   doctypeName = nsHtml5Atoms::emptystring;
   if (systemIdentifier) {
     nsHtml5Portability::releaseString(systemIdentifier);
     systemIdentifier = nullptr;
   }
   if (publicIdentifier) {
     nsHtml5Portability::releaseString(publicIdentifier);
     publicIdentifier = nullptr;
@@ -3602,17 +3641,16 @@ nsHtml5Tokenizer::eof()
         NS_HTML5_BREAK(eofloop);
       }
       case NS_HTML5TOKENIZER_BOGUS_COMMENT_HYPHEN: {
         emitComment(0, 0);
         NS_HTML5_BREAK(eofloop);
       }
       case NS_HTML5TOKENIZER_MARKUP_DECLARATION_OPEN: {
         errBogusComment();
-        clearStrBuf();
         emitComment(0, 0);
         NS_HTML5_BREAK(eofloop);
       }
       case NS_HTML5TOKENIZER_MARKUP_DECLARATION_HYPHEN: {
         errBogusComment();
         emitComment(0, 0);
         NS_HTML5_BREAK(eofloop);
       }
@@ -3808,16 +3846,17 @@ nsHtml5Tokenizer::eof()
           }
           if (charRefBufMark < charRefBufLen) {
             if ((returnState & NS_HTML5TOKENIZER_DATA_AND_RCDATA_MASK)) {
               appendStrBuf(charRefBuf, charRefBufMark, charRefBufLen - charRefBufMark);
             } else {
               tokenHandler->characters(charRefBuf, charRefBufMark, charRefBufLen - charRefBufMark);
             }
           }
+          charRefBufLen = 0;
           state = returnState;
           NS_HTML5_CONTINUE(eofloop);
         }
       }
       case NS_HTML5TOKENIZER_CONSUME_NCR:
       case NS_HTML5TOKENIZER_DECIMAL_NRC_LOOP:
       case NS_HTML5TOKENIZER_HEX_NCR_LOOP: {
         if (!seenDigits) {
@@ -3930,17 +3969,17 @@ bool
 nsHtml5Tokenizer::isInDataState()
 {
   return (stateSave == NS_HTML5TOKENIZER_DATA);
 }
 
 void 
 nsHtml5Tokenizer::resetToDataState()
 {
-  strBufLen = 0;
+  clearStrBufAfterUse();
   charRefBufLen = 0;
   stateSave = NS_HTML5TOKENIZER_DATA;
   lastCR = false;
   index = 0;
   forceQuirks = false;
   additional = '\0';
   entCol = -1;
   firstCharKey = -1;
--- a/parser/html/nsHtml5Tokenizer.h
+++ b/parser/html/nsHtml5Tokenizer.h
@@ -1,30 +1,30 @@
 /*
  * Copyright (c) 2005-2007 Henri Sivonen
  * Copyright (c) 2007-2015 Mozilla Foundation
- * Portions of comments Copyright 2004-2010 Apple Computer, Inc., Mozilla 
+ * Portions of comments Copyright 2004-2010 Apple Computer, Inc., Mozilla
  * Foundation, and Opera Software ASA.
  *
- * Permission is hereby granted, free of charge, to any person obtaining a 
- * copy of this software and associated documentation files (the "Software"), 
- * to deal in the Software without restriction, including without limitation 
- * the rights to use, copy, modify, merge, publish, distribute, sublicense, 
- * and/or sell copies of the Software, and to permit persons to whom the 
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
  * Software is furnished to do so, subject to the following conditions:
  *
- * The above copyright notice and this permission notice shall be included in 
+ * The above copyright notice and this permission notice shall be included in
  * all copies or substantial portions of the Software.
  *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  * DEALINGS IN THE SOFTWARE.
  */
 
 /*
  * THIS IS A GENERATED FILE. PLEASE DO NOT EDIT.
  * Please edit Tokenizer.java instead and regenerate.
  */
 
@@ -157,31 +157,32 @@ class nsHtml5Tokenizer
     nsHtml5HtmlAttributes* emptyAttributes();
   private:
     inline void appendCharRefBuf(char16_t c)
     {
       MOZ_RELEASE_ASSERT(charRefBufLen < charRefBuf.length, "Attempted to overrun charRefBuf!");
       charRefBuf[charRefBufLen++] = c;
     }
 
-    inline void clearCharRefBufAndAppend(char16_t c)
+    void emitOrAppendCharRefBuf(int32_t returnState);
+    inline void clearStrBufAfterUse()
     {
-      charRefBuf[0] = c;
-      charRefBufLen = 1;
+      strBufLen = 0;
     }
 
-    void emitOrAppendCharRefBuf(int32_t returnState);
-    inline void clearStrBufAndAppend(char16_t c)
+    inline void clearStrBufBeforeUse()
     {
-      strBuf[0] = c;
-      strBufLen = 1;
+      MOZ_ASSERT(!strBufLen, "strBufLen not reset after previous use!");
+      strBufLen = 0;
     }
 
-    inline void clearStrBuf()
+    inline void clearStrBufAfterOneHyphen()
     {
+      MOZ_ASSERT(strBufLen == 1, "strBufLen length not one!");
+      MOZ_ASSERT(strBuf[0] == '-', "strBuf does not start with a hyphen!");
       strBufLen = 0;
     }
 
     inline void appendStrBuf(char16_t c)
     {
       MOZ_ASSERT(strBufLen < strBuf.length, "Previous buffer length insufficient.");
       if (MOZ_UNLIKELY(strBufLen == strBuf.length)) {
         if (MOZ_UNLIKELY(!EnsureBufferSpace(1))) {
@@ -206,16 +207,17 @@ class nsHtml5Tokenizer
       errConsecutiveHyphens();
       appendStrBuf(c);
     }
 
     void appendStrBuf(char16_t* buffer, int32_t offset, int32_t length);
     inline void appendCharRefBufToStrBuf()
     {
       appendStrBuf(charRefBuf, 0, charRefBufLen);
+      charRefBufLen = 0;
     }
 
     void emitComment(int32_t provisionalHyphens, int32_t pos);
   protected:
     void flushChars(char16_t* buf, int32_t pos);
   private:
     void strBufToElementNameString();
     int32_t emitCurrentTagToken(bool selfClosing, int32_t pos);
--- a/taskcluster/ci/android-test/tests.yml
+++ b/taskcluster/ci/android-test/tests.yml
@@ -251,17 +251,17 @@ robocop:
             - --test-suite=robocop
 
 xpcshell:
     description: "xpcshell test run"
     suite: xpcshell
     treeherder-symbol: tc-X()
     chunks:
         by-test-platform:
-            default: 3
+            default: 6
             android-4.2-x86/opt: 4
     instance-size: xlarge
     max-run-time: 7200
     loopback-video: true
     e10s: false
     mozharness:
         script: android_emulator_unittest.py
         no-read-buildbot-config: true
--- a/testing/mozbase/mozdevice/mozdevice/adb.py
+++ b/testing/mozbase/mozdevice/mozdevice/adb.py
@@ -609,18 +609,36 @@ class ADBDevice(ADBCommand):
         else:
             raise ADBError("ADBDevice.__init__: ls not found")
         try:
             self.shell_output("%s -1A /" % self._ls, timeout=timeout)
             self._ls += " -1A"
         except ADBError:
             self._ls += " -a"
 
+        self._logger.info("%s supported" % self._ls)
+
         # Do we have cp?
         self._have_cp = self.shell_bool("type cp", timeout=timeout)
+        self._logger.info("Native cp support: %s" % self._have_cp)
+
+        # Do we have chmod -R?
+        try:
+            self._chmod_R = False
+            re_recurse = re.compile(r'[-]R')
+            chmod_output = self.shell_output("chmod --help", timeout=timeout)
+            match = re_recurse.search(chmod_output)
+            if match:
+                self._chmod_R = True
+        except (ADBError, ADBTimeoutError) as e:
+            self._logger.debug('Check chmod -R: %s' % e)
+            match = re_recurse.search(e.message)
+            if match:
+                self._chmod_R = True
+        self._logger.info("Native chmod -R support: %s" % self._chmod_R)
 
         self._logger.debug("ADBDevice: %s" % self.__dict__)
 
     def _get_device_serial(self, device):
         if device is None:
             devices = ADBHost(adb=self._adb_path, adb_host=self._adb_host,
                               adb_port=self._adb_port).devices()
             if len(devices) > 1:
@@ -660,34 +678,33 @@ class ADBDevice(ADBCommand):
             if self.shell_output("id", timeout=timeout).find(uid) != -1:
                 self._have_root_shell = True
                 self._logger.info("adbd running as root")
         except ADBError:
             self._logger.debug("Check for root shell failed")
 
         # Do we need to run adb root to get a root shell?
         try:
-            if (not self._have_root_shell and
-                self.command_output(
+            if (not self._have_root_shell and self.command_output(
                     ["root"],
                     timeout=timeout).find("cannot run as root") == -1):
                 self._have_root_shell = True
                 self._logger.info("adbd restarted as root")
         except ADBError:
             self._logger.debug("Check for root adbd failed")
 
     @staticmethod
     def _escape_command_line(cmd):
         """Utility function to return escaped and quoted version of command
         line.
         """
         quoted_cmd = []
 
         for arg in cmd:
-            arg.replace('&', '\&')
+            arg.replace('&', r'\&')
 
             needs_quoting = False
             for char in [' ', '(', ')', '"', '&']:
                 if arg.find(char) >= 0:
                     needs_quoting = True
                     break
             if needs_quoting:
                 arg = "'%s'" % arg
@@ -1273,30 +1290,114 @@ class ADBDevice(ADBCommand):
             may exceed this value. If it is not specified, the value
             set in the ADBDevice constructor is used.
         :type timeout: integer or None
         :returns: string ip address of the device or None if it could not
             be found.
         :raises: * ADBTimeoutError
                  * ADBError
         """
-        ip_regexp = re.compile(r'(\w+)\s+UP\s+([1-9]\d{0,2}\.\d{1,3}\.\d{1,3}\.\d{1,3})')
-        data = self.shell_output('netcfg')
-        for line in data.split("\n"):
-            match = ip_regexp.search(line)
-            if match:
-                interface, ip = match.groups()
+        if not interfaces:
+            interfaces = ["wlan0", "eth0"]
+            wifi_interface = self.shell_output('getprop wifi.interface', timeout=timeout)
+            self._logger.debug('get_ip_address: wifi_interface: %s' % wifi_interface)
+            if wifi_interface and wifi_interface not in interfaces:
+                interfaces = interfaces.append(wifi_interface)
+
+        # ifconfig interface
+        # can return two different formats:
+        # eth0: ip 192.168.1.139 mask 255.255.255.0 flags [up broadcast running multicast]
+        # or
+        # wlan0     Link encap:Ethernet  HWaddr 00:9A:CD:B8:39:65
+        # inet addr:192.168.1.38  Bcast:192.168.1.255  Mask:255.255.255.0
+        # inet6 addr: fe80::29a:cdff:feb8:3965/64 Scope: Link
+        # UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
+        # RX packets:180 errors:0 dropped:0 overruns:0 frame:0
+        # TX packets:218 errors:0 dropped:0 overruns:0 carrier:0
+        # collisions:0 txqueuelen:1000
+        # RX bytes:84577 TX bytes:31202
+
+        re1_ip = re.compile(r'(\w+): ip ([0-9.]+) mask.*')
+        # re1_ip will match output of the first format
+        # with group 1 returning the interface and group 2 returing the ip address.
+
+        # re2_interface will match the interface line in the second format
+        # while re2_ip will match the inet addr line of the second format.
+        re2_interface = re.compile(r'(\w+)\s+Link')
+        re2_ip = re.compile(r'\s+inet addr:([0-9.]+)')
+
+        matched_interface = None
+        matched_ip = None
+        re_bad_addr = re.compile(r'127.0.0.1|0.0.0.0')
+
+        self._logger.debug('get_ip_address: ifconfig')
+        for interface in interfaces:
+            try:
+                output = self.shell_output('ifconfig %s' % interface,
+                                           timeout=timeout)
+            except ADBError:
+                output = ''
 
-                if interface == "lo" or ip == "127.0.0.1":
-                    continue
+            for line in output.split("\n"):
+                if not matched_interface:
+                    match = re1_ip.match(line)
+                    if match:
+                        matched_interface, matched_ip = match.groups()
+                    else:
+                        match = re2_interface.match(line)
+                        if match:
+                            matched_interface = match.group(1)
+                else:
+                    match = re2_ip.match(line)
+                    if match:
+                        matched_ip = match.group(1)
+
+                if matched_ip:
+                    if not re_bad_addr.match(matched_ip):
+                        self._logger.debug('get_ip_address: found: %s %s' %
+                                           (matched_interface, matched_ip))
+                        return matched_ip
+                    matched_interface = None
+                    matched_ip = None
 
-                if interfaces is None or interface in interfaces:
-                    return ip
+        self._logger.debug('get_ip_address: netcfg')
+        # Fall back on netcfg if ifconfig does not work.
+        # $ adb shell netcfg
+        # lo       UP   127.0.0.1/8       0x00000049 00:00:00:00:00:00
+        # dummy0   DOWN   0.0.0.0/0       0x00000082 8e:cd:67:48:b7:c2
+        # rmnet0   DOWN   0.0.0.0/0       0x00000000 00:00:00:00:00:00
+        # rmnet1   DOWN   0.0.0.0/0       0x00000000 00:00:00:00:00:00
+        # rmnet2   DOWN   0.0.0.0/0       0x00000000 00:00:00:00:00:00
+        # rmnet3   DOWN   0.0.0.0/0       0x00000000 00:00:00:00:00:00
+        # rmnet4   DOWN   0.0.0.0/0       0x00000000 00:00:00:00:00:00
+        # rmnet5   DOWN   0.0.0.0/0       0x00000000 00:00:00:00:00:00
+        # rmnet6   DOWN   0.0.0.0/0       0x00000000 00:00:00:00:00:00
+        # rmnet7   DOWN   0.0.0.0/0       0x00000000 00:00:00:00:00:00
+        # sit0     DOWN   0.0.0.0/0       0x00000080 00:00:00:00:00:00
+        # vip0     DOWN   0.0.0.0/0       0x00001012 00:01:00:00:00:01
+        # wlan0    UP   192.168.1.157/24  0x00001043 38:aa:3c:1c:f6:94
 
-        return None
+        re3_netcfg = re.compile(r'(\w+)\s+UP\s+([1-9]\d{0,2}\.\d{1,3}\.\d{1,3}\.\d{1,3})')
+        try:
+            output = self.shell_output('netcfg', timeout=timeout)
+        except ADBError:
+            output = ''
+        for line in output.split("\n"):
+            match = re3_netcfg.search(line)
+            if match:
+                matched_interface, matched_ip = match.groups()
+                if matched_interface == "lo" or re_bad_addr.match(matched_ip):
+                    matched_interface = None
+                    matched_ip = None
+                elif matched_ip and matched_interface in interfaces:
+                    self._logger.debug('get_ip_address: found: %s %s' %
+                                       (matched_interface, matched_ip))
+                    return matched_ip
+        self._logger.debug('get_ip_address: not found')
+        return matched_ip
 
     # File management methods
 
     def remount(self, timeout=None):
         """Remount /system/ in read/write mode
 
         :param timeout: The maximum time in
             seconds for any spawned adb process to complete before throwing
@@ -1328,44 +1429,66 @@ class ADBDevice(ADBCommand):
             set in the ADBDevice constructor is used.
         :type timeout: integer or None
         :param bool root: Flag specifying if the command should
             be executed as root.
         :raises: * ADBTimeoutError
                  * ADBRootError
                  * ADBError
         """
+        # Note that on some tests such as webappstartup, an error
+        # occurs during recursive calls to chmod where a "No such file
+        # or directory" error will occur for the
+        # /data/data/org.mozilla.fennec/files/mozilla/*.webapp0/lock
+        # which is a symbolic link to a socket: lock ->
+        # 127.0.0.1:+<port>.  On Linux, chmod -R ignores symbolic
+        # links but it appear Android's version does not. We ignore
+        # this type of error, but pass on any other errors that are
+        # detected.
         path = posixpath.normpath(path.strip())
         self._logger.debug('chmod: path=%s, recursive=%s, mask=%s, root=%s' %
                            (path, recursive, mask, root))
-        self.shell_output("chmod %s %s" % (mask, path),
-                          timeout=timeout, root=root)
-        if recursive and self.is_dir(path, timeout=timeout, root=root):
-            files = self.list_files(path, timeout=timeout, root=root)
-            for f in files:
-                entry = path + "/" + f
-                self._logger.debug('chmod: entry=%s' % entry)
-                if self.is_dir(entry, timeout=timeout, root=root):
-                    self._logger.debug('chmod: recursion entry=%s' % entry)
-                    self.chmod(entry, recursive=recursive, mask=mask,
-                               timeout=timeout, root=root)
-                elif self.is_file(entry, timeout=timeout, root=root):
-                    try:
-                        self.shell_output("chmod %s %s" % (mask, entry),
-                                          timeout=timeout, root=root)
-                        self._logger.debug('chmod: file entry=%s' % entry)
-                    except ADBError as e:
-                        if e.message.find('No such file or directory'):
-                            # some kind of race condition is causing files
-                            # to disappear. Catch and report the error here.
-                            self._logger.warning('chmod: File %s vanished!: %s' %
-                                                 (entry, e))
-                else:
-                    self._logger.warning('chmod: entry %s does not exist' %
-                                         entry)
+        if not recursive:
+            self.shell_output("chmod %s %s" % (mask, path),
+                              timeout=timeout, root=root)
+            return
+
+        if self._chmod_R:
+            try:
+                self.shell_output("chmod -R %s %s" % (mask, path),
+                                  timeout=timeout, root=root)
+            except ADBError as e:
+                if e.message.find('No such file or directory') == -1:
+                    raise
+                self._logger.warning('chmod -R %s %s: Ignoring Error: %s' %
+                                     (mask, path, e.message))
+            return
+        # Obtain a list of the directories and files which match path
+        # and construct a shell script which explictly calls chmod on
+        # each of them.
+        entries = self.ls(path, recursive=recursive, timeout=timeout,
+                          root=root)
+        tmpf = None
+        chmodsh = None
+        try:
+            tmpf = tempfile.NamedTemporaryFile(delete=False)
+            for entry in entries:
+                tmpf.write('chmod %s %s\n' % (mask, entry))
+            tmpf.close()
+            chmodsh = '/data/local/tmp/%s' % os.path.basename(tmpf.name)
+            self.push(tmpf.name, chmodsh)
+            self.shell_output('chmod 777 %s' % chmodsh, timeout=timeout,
+                              root=root)
+            self.shell_output('sh -c %s' % chmodsh, timeout=timeout,
+                              root=root)
+        finally:
+            if tmpf:
+                os.unlink(tmpf.name)
+            if chmodsh:
+                self.rm(chmodsh, timeout=timeout, root=root)
 
     def exists(self, path, timeout=None, root=False):
         """Returns True if the path exists on the device.
 
         :param str path: The directory name on the device.
         :param timeout: The maximum time in
             seconds for any spawned adb process to complete before
             throwing an ADBTimeoutError.
@@ -1450,21 +1573,104 @@ class ADBDevice(ADBCommand):
             try:
                 data = self.shell_output("%s %s" % (self._ls, path),
                                          timeout=timeout,
                                          root=root).split('\r\n')
                 self._logger.debug('list_files: data: %s' % data)
             except ADBError:
                 self._logger.error('Ignoring exception in ADBDevice.list_files\n%s' %
                                    traceback.format_exc())
-                pass
         data[:] = [item for item in data if item]
         self._logger.debug('list_files: %s' % data)
         return data
 
+    def ls(self, path, recursive=False, timeout=None, root=False):
+        """Return a list of matching files/directories on the device.
+
+        The ls method emulates the behavior of the ls shell command.
+        It differs from the list_files method by supporting wild cards
+        and returning matches even if the path is not a directory and
+        by allowing a recursive listing.
+
+        ls /sdcard always returns /sdcard and not the contents of the
+        sdcard path. The ls method makes the behavior consistent with
+        others paths by adjusting /sdcard to /sdcard/. Note this is
+        also the case of other sdcard related paths such as
+        /storage/emulated/legacy but no adjustment is made in those
+        cases.
+
+        The ls method works around a Nexus 4 bug which prevents
+        recursive listing of directories on the sdcard unless the path
+        ends with "/*" by adjusting sdcard paths ending in "/" to end
+        with "/*". This adjustment is only made on official Nexus 4
+        builds with property ro.product.model "Nexus 4". Note that
+        this will fail to return any "hidden" files or directories
+        which begin with ".".
+
+        :param str path: The directory name on the device.
+        :param bool recursive: Flag specifying if a recursive listing
+            is to be returned. If recursive is False, the returned
+            matches will be relative to the path. If recursive is True,
+            the returned matches will be absolute paths.
+        :param timeout: The maximum time in
+            seconds for any spawned adb process to complete before
+            throwing an ADBTimeoutError.
+            This timeout is per adb call. The total time spent
+            may exceed this value. If it is not specified, the value
+            set in the ADBDevice constructor is used.
+        :type timeout: integer or None
+        :param bool root: Flag specifying if the command should
+            be executed as root.
+        :returns: list of files/directories contained in the directory.
+        :raises: * ADBTimeoutError
+                 * ADBRootError
+        """
+        path = posixpath.normpath(path.strip())
+        parent = ''
+        entries = {}
+
+        if path == '/sdcard':
+            path += '/'
+
+        # Android 2.3 and later all appear to support ls -R however
+        # Nexus 4 does not perform a recursive search on the sdcard
+        # unless the path is a directory with * wild card.
+        if not recursive:
+            recursive_flag = ''
+        else:
+            recursive_flag = '-R'
+            if path.startswith('/sdcard') and path.endswith('/'):
+                model = self.shell_output('getprop ro.product.model',
+                                          timeout=timeout,
+                                          root=root)
+                if model == 'Nexus 4':
+                    path += '*'
+        lines = self.shell_output('%s %s %s' % (self._ls, recursive_flag, path),
+                                  timeout=timeout,
+                                  root=root).split('\r\n')
+        for line in lines:
+            line = line.strip()
+            if not line:
+                parent = ''
+                continue
+            if line.endswith(':'):  # This is a directory
+                parent = line.replace(':', '/')
+                entry = parent
+                # Remove earlier entry which is marked as a file.
+                if parent[:-1] in entries:
+                    del entries[parent[:-1]]
+            elif parent:
+                entry = "%s%s" % (parent, line)
+            else:
+                entry = line
+            entries[entry] = 1
+        entry_list = entries.keys()
+        entry_list.sort()
+        return entry_list
+
     def mkdir(self, path, parents=False, timeout=None, root=False):
         """Create a directory on the device.
 
         :param str path: The directory name on the device
             to be created.
         :param bool parents: Flag indicating if the parent directories are
             also to be created. Think mkdir -p path.
         :param timeout: The maximum time in
@@ -1660,17 +1866,17 @@ class ADBDevice(ADBCommand):
                 line = adb_process.stdout_file.readline()
             self._logger.debug('get_process_list: %s' % ret)
             return ret
         finally:
             if adb_process and isinstance(adb_process.stdout_file, file):
                 adb_process.stdout_file.close()
                 adb_process.stderr_file.close()
 
-    def kill(self, pids, sig=None,  attempts=3, wait=5,
+    def kill(self, pids, sig=None, attempts=3, wait=5,
              timeout=None, root=False):
         """Kills processes on the device given a list of process ids.
 
         :param list pids: process ids to be killed.
         :param sig: signal to be sent to the process.
         :type sig: integer or None
         :param integer attempts: number of attempts to try to
             kill the processes.
@@ -1973,17 +2179,17 @@ class ADBDevice(ADBCommand):
             may exceed this value. If it is not specified, the value
             set in the ADB constructor is used.
         :raises: * ADBTimeoutError
                  * ADBError
         """
         directives = ['battery', 'disk', 'id', 'os', 'process', 'systime',
                       'uptime']
 
-        if (directive in directives):
+        if directive in directives:
             directives = [directive]
 
         info = {}
         if 'battery' in directives:
             info['battery'] = self.get_battery_percentage(timeout=timeout)
         if 'disk' in directives:
             info['disk'] = self.shell_output('df /data /system /sdcard',
                                              timeout=timeout).splitlines()
@@ -1995,15 +2201,15 @@ class ADBDevice(ADBCommand):
         if 'process' in directives:
             ps = self.shell_output('ps', timeout=timeout)
             info['process'] = ps.splitlines()
         if 'systime' in directives:
             info['systime'] = self.shell_output('date', timeout=timeout)
         if 'uptime' in directives:
             uptime = self.shell_output('uptime', timeout=timeout)
             if uptime:
-                m = re.match('up time: ((\d+) days, )*(\d{2}):(\d{2}):(\d{2})',
+                m = re.match(r'up time: ((\d+) days, )*(\d{2}):(\d{2}):(\d{2})',
                              uptime)
                 if m:
                     uptime = '%d days %d hours %d minutes %d seconds' % tuple(
                         [int(g or 0) for g in m.groups()[1:]])
                 info['uptime'] = uptime
         return info
--- a/testing/mozbase/mozdevice/mozdevice/adb_android.py
+++ b/testing/mozbase/mozdevice/mozdevice/adb_android.py
@@ -88,16 +88,19 @@ class ADBAndroid(ADBDevice):
         try:
             self.selinux = True
             if self.shell_output('getenforce', timeout=timeout) != 'Permissive':
                 self._logger.info('Setting SELinux Permissive Mode')
                 self.shell_output("setenforce Permissive", timeout=timeout, root=True)
         except ADBError:
             self.selinux = False
 
+        self.version = int(self.shell_output("getprop ro.build.version.sdk",
+                                             timeout=timeout))
+
     def reboot(self, timeout=None):
         """Reboots the device.
 
         :param timeout: optional integer specifying the maximum time in
             seconds for any spawned adb process to complete before
             throwing an ADBTimeoutError.
             This timeout is per adb call. The total time spent
             may exceed this value. If it is not specified, the value
@@ -190,19 +193,18 @@ class ADBAndroid(ADBDevice):
             failure = 'Unknown failure'
             success = True
             try:
                 state = self.get_state(timeout=timeout)
                 if state != 'device':
                     failure = "Device state: %s" % state
                     success = False
                 else:
-                    if (self.selinux and
-                        self.shell_output('getenforce',
-                                          timeout=timeout) != 'Permissive'):
+                    if (self.selinux and self.shell_output('getenforce',
+                                                           timeout=timeout) != 'Permissive'):
                         self._logger.info('Setting SELinux Permissive Mode')
                         self.shell_output("setenforce Permissive", timeout=timeout, root=True)
                     if self.is_dir(ready_path, timeout=timeout, root=True):
                         self.rmdir(ready_path, timeout=timeout, root=True)
                     self.mkdir(ready_path, timeout=timeout, root=True)
                     self.rmdir(ready_path, timeout=timeout, root=True)
                     # Invoke the pm list commands to see if it is up and
                     # running.
@@ -260,17 +262,21 @@ class ADBAndroid(ADBDevice):
             throwing an ADBTimeoutError.
             This timeout is per adb call. The total time spent
             may exceed this value. If it is not specified, the value
             set in the ADB constructor is used.
         :type timeout: integer or None
         :raises: * ADBTimeoutError
                  * ADBError
         """
-        data = self.command_output(["install", apk_path], timeout=timeout)
+        cmd = ["install"]
+        if self.version >= version_codes.M:
+            cmd.append("-g")
+        cmd.append(apk_path)
+        data = self.command_output(cmd, timeout=timeout)
         if data.find('Success') == -1:
             raise ADBError("install failed for %s. Got: %s" %
                            (apk_path, data))
 
     def is_app_installed(self, app_name, timeout=None):
         """Returns True if an app is installed on the device.
 
         :param str app_name: The name of the app to be checked.
@@ -329,19 +335,19 @@ class ADBAndroid(ADBDevice):
         acmd = ["am", "start"] + \
             ["-W" if wait else '', "-n", "%s/%s" % (app_name, activity_name)]
 
         if intent:
             acmd.extend(["-a", intent])
 
         if extras:
             for (key, val) in extras.iteritems():
-                if type(val) is int:
+                if isinstance(val, int):
                     extra_type_param = "--ei"
-                elif type(val) is bool:
+                elif isinstance(val, bool):
                     extra_type_param = "--ez"
                 else:
                     extra_type_param = "--es"
                 acmd.extend([extra_type_param, str(key), str(val)])
 
         if url:
             acmd.extend(["-d", url])
 
@@ -386,18 +392,18 @@ class ADBAndroid(ADBDevice):
             for (env_count, (env_key, env_val)) in enumerate(moz_env.iteritems()):
                 extras["env" + str(env_count)] = env_key + "=" + env_val
 
         # Additional command line arguments that fennec will read and use (e.g.
         # with a custom profile)
         if extra_args:
             extras['args'] = " ".join(extra_args)
 
-        self.launch_application(app_name, "org.mozilla.gecko.BrowserApp", intent, url=url,
-                                extras=extras,
+        self.launch_application(app_name, "org.mozilla.gecko.BrowserApp",
+                                intent, url=url, extras=extras,
                                 wait=wait, fail_if_running=fail_if_running,
                                 timeout=timeout)
 
     def stop_application(self, app_name, timeout=None, root=False):
         """Stops the specified application
 
         For Android 3.0+, we use the "am force-stop" to do this, which
         is reliable and does not require root. For earlier versions of
@@ -413,19 +419,17 @@ class ADBAndroid(ADBDevice):
             may exceed this value. If it is not specified, the value
             set in the ADB constructor is used.
         :type timeout: integer or None
         :param bool root: Flag specifying if the command should be
             executed as root.
         :raises: * ADBTimeoutError
                  * ADBError
         """
-        version = self.shell_output("getprop ro.build.version.sdk",
-                                    timeout=timeout, root=root)
-        if int(version) >= version_codes.HONEYCOMB:
+        if self.version >= version_codes.HONEYCOMB:
             self.shell_output("am force-stop %s" % app_name,
                               timeout=timeout, root=root)
         else:
             num_tries = 0
             max_tries = 5
             while self.process_exist(app_name, timeout=timeout):
                 if num_tries > max_tries:
                     raise ADBError("Couldn't successfully kill %s after %s "
@@ -475,12 +479,15 @@ class ADBAndroid(ADBDevice):
             throwing an ADBTimeoutError.
             This timeout is per adb call. The total time spent
             may exceed this value. If it is not specified, the value
             set in the ADB constructor is used.
         :type timeout: integer or None
         :raises: * ADBTimeoutError
                  * ADBError
         """
-        output = self.command_output(["install", "-r", apk_path],
-                                     timeout=timeout)
+        cmd = ["install", "-r"]
+        if self.version >= version_codes.M:
+            cmd.append("-g")
+        cmd.append(apk_path)
+        output = self.command_output(cmd, timeout=timeout)
         self.reboot(timeout=timeout)
         return output
--- a/testing/mozbase/mozdevice/mozdevice/adb_b2g.py
+++ b/testing/mozbase/mozdevice/mozdevice/adb_b2g.py
@@ -95,17 +95,17 @@ class ADBB2G(ADBDevice):
             set in the ADB constructor is used.
         :raises: * ADBTimeoutError
                  * ADBError
         """
         info = super(ADBB2G, self).get_info(directive=directive,
                                             timeout=timeout)
 
         directives = ['memtotal']
-        if (directive in directives):
+        if directive in directives:
             directives = [directive]
 
         if 'memtotal' in directives:
             info['memtotal'] = self.get_memory_total(timeout=timeout)
         return info
 
     def is_device_ready(self, timeout=None):
         """Returns True if the device is ready.
--- a/testing/mozharness/scripts/android_emulator_unittest.py
+++ b/testing/mozharness/scripts/android_emulator_unittest.py
@@ -145,18 +145,17 @@ class AndroidEmulatorTest(BlobUploadMixi
         dirs['abs_test_install_dir'] = os.path.join(
             abs_dirs['abs_work_dir'], 'tests')
         dirs['abs_xre_dir'] = os.path.join(
             abs_dirs['abs_work_dir'], 'hostutils')
         dirs['abs_modules_dir'] = os.path.join(
             dirs['abs_test_install_dir'], 'modules')
         dirs['abs_blob_upload_dir'] = os.path.join(
             abs_dirs['abs_work_dir'], 'blobber_upload_dir')
-        dirs['abs_emulator_dir'] = os.path.join(
-            abs_dirs['abs_work_dir'], 'emulator')
+        dirs['abs_emulator_dir'] = abs_dirs['abs_work_dir']
         dirs['abs_mochitest_dir'] = os.path.join(
             dirs['abs_test_install_dir'], 'mochitest')
         dirs['abs_marionette_dir'] = os.path.join(
             dirs['abs_test_install_dir'], 'marionette', 'marionette')
         dirs['abs_marionette_tests_dir'] = os.path.join(
             dirs['abs_test_install_dir'], 'marionette', 'tests', 'testing',
             'marionette', 'harness', 'marionette', 'tests')
         dirs['abs_avds_dir'] = self.config.get("avds_dir", "/home/cltbld/.android")
@@ -555,18 +554,24 @@ class AndroidEmulatorTest(BlobUploadMixi
         dirs = self.query_abs_dirs()
 
         # FIXME
         # Clobbering and re-unpacking would not be needed if we had a way to
         # check whether the unpacked content already present match the
         # contents of the tar ball
         self.rmtree(dirs['abs_avds_dir'])
         self.mkdir_p(dirs['abs_avds_dir'])
-        url = self._get_repo_url(c["tooltool_manifest_path"])
-        self._tooltool_fetch(url, dirs['abs_avds_dir'])
+        if 'avd_url' in c:
+            # Intended for experimental setups to evaluate an avd prior to
+            # tooltool deployment.
+            url = c['avd_url']
+            self.download_unpack(url, dirs['abs_avds_dir'])
+        else:
+            url = self._get_repo_url(c["tooltool_manifest_path"])
+            self._tooltool_fetch(url, dirs['abs_avds_dir'])
 
         avd_home_dir = self.abs_dirs['abs_avds_dir']
         if avd_home_dir != "/home/cltbld/.android":
             # Modify the downloaded avds to point to the right directory.
             cmd = [
                 'bash', '-c',
                 'sed -i "s|/home/cltbld/.android|%s|" %s/test-*.ini' %
                 (avd_home_dir, os.path.join(avd_home_dir, 'avd'))
deleted file mode 100644
--- a/testing/mozharness/scripts/androidx86_emulator_unittest.py
+++ /dev/null
@@ -1,839 +0,0 @@
-#!/usr/bin/env python
-# ***** BEGIN LICENSE BLOCK *****
-# 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/.
-# ***** END LICENSE BLOCK *****
-
-import copy
-import glob
-import os
-import sys
-import signal
-import socket
-import subprocess
-import telnetlib
-import time
-import tempfile
-
-# load modules from parent dir
-sys.path.insert(1, os.path.dirname(sys.path[0]))
-
-from mozprocess import ProcessHandler
-
-from mozharness.base.log import FATAL
-from mozharness.base.script import BaseScript, PostScriptRun, PreScriptAction, PostScriptAction
-from mozharness.base.vcs.vcsbase import VCSMixin
-from mozharness.mozilla.blob_upload import BlobUploadMixin, blobupload_config_options
-from mozharness.mozilla.mozbase import MozbaseMixin
-from mozharness.mozilla.buildbot import TBPL_WORST_LEVEL_TUPLE
-from mozharness.mozilla.testing.testbase import TestingMixin, testing_config_options
-from mozharness.mozilla.testing.unittest import EmulatorMixin
-
-
-class AndroidEmulatorTest(BlobUploadMixin, TestingMixin, EmulatorMixin, VCSMixin, BaseScript, MozbaseMixin):
-    config_options = [[
-        ["--robocop-url"],
-        {"action": "store",
-         "dest": "robocop_url",
-         "default": None,
-         "help": "URL to the robocop apk",
-         }
-    ], [
-        ["--test-suite"],
-        {"action": "append",
-         "dest": "test_suites",
-         }
-    ], [
-        ["--adb-path"],
-        {"action": "store",
-         "dest": "adb_path",
-         "default": None,
-         "help": "Path to adb",
-         }
-    ]] + copy.deepcopy(testing_config_options) + \
-        copy.deepcopy(blobupload_config_options)
-
-    error_list = [
-    ]
-
-    virtualenv_requirements = [
-    ]
-
-    virtualenv_modules = [
-    ]
-
-    app_name = None
-
-    def __init__(self, require_config_file=False):
-        super(AndroidEmulatorTest, self).__init__(
-            config_options=self.config_options,
-            all_actions=['clobber',
-                         'read-buildbot-config',
-                         'setup-avds',
-                         'start-emulators',
-                         'download-and-extract',
-                         'create-virtualenv',
-                         'install',
-                         'run-tests',
-                        ],
-            default_actions=['clobber',
-                             'start-emulators',
-                             'download-and-extract',
-                             'create-virtualenv',
-                             'install',
-                             'run-tests',
-                            ],
-            require_config_file=require_config_file,
-            config={
-                'virtualenv_modules': self.virtualenv_modules,
-                'virtualenv_requirements': self.virtualenv_requirements,
-                'require_test_zip': True,
-                # IP address of the host as seen from the emulator
-                'remote_webserver': '10.0.2.2',
-            }
-        )
-
-        # these are necessary since self.config is read only
-        c = self.config
-        abs_dirs = self.query_abs_dirs()
-        self.adb_path = self.query_exe('adb')
-        self.installer_url = c.get('installer_url')
-        self.installer_path = c.get('installer_path')
-        self.test_url = c.get('test_url')
-        self.test_manifest = c.get('test_manifest')
-        self.robocop_url = c.get('robocop_url')
-        self.robocop_path = os.path.join(abs_dirs['abs_work_dir'], "robocop.apk")
-        self.minidump_stackwalk_path = c.get("minidump_stackwalk_path")
-        self.emulators = c.get('emulators')
-        self.test_suite_definitions = c['test_suite_definitions']
-        self.test_suites = c.get('test_suites')
-        for suite in self.test_suites:
-            assert suite in self.test_suite_definitions
-        self.xre_path = None
-
-    def _query_tests_dir(self, suite_name):
-        dirs = self.query_abs_dirs()
-        suite_category = self.test_suite_definitions[suite_name]["category"]
-        try:
-            test_dir = self.config["suite_definitions"][suite_category]["testsdir"]
-        except:
-            test_dir = suite_category
-
-        return os.path.join(dirs['abs_test_install_dir'], test_dir)
-
-    def query_abs_dirs(self):
-        if self.abs_dirs:
-            return self.abs_dirs
-        abs_dirs = super(AndroidEmulatorTest, self).query_abs_dirs()
-        dirs = {}
-        dirs['abs_test_install_dir'] = os.path.join(
-            abs_dirs['abs_work_dir'], 'tests')
-        dirs['abs_xre_dir'] = os.path.join(
-            abs_dirs['abs_work_dir'], 'hostutils')
-        dirs['abs_modules_dir'] = os.path.join(
-            dirs['abs_test_install_dir'], 'modules')
-        dirs['abs_blob_upload_dir'] = os.path.join(
-            abs_dirs['abs_work_dir'], 'blobber_upload_dir')
-        dirs['abs_emulator_dir'] = os.path.join(
-            abs_dirs['abs_work_dir'], 'emulator')
-        dirs['abs_mochitest_dir'] = os.path.join(
-            dirs['abs_test_install_dir'], 'mochitest')
-        dirs['abs_avds_dir'] = self.config.get("avds_dir", "/home/cltbld/.android")
-
-        for key in dirs.keys():
-            if key not in abs_dirs:
-                abs_dirs[key] = dirs[key]
-        self.abs_dirs = abs_dirs
-        return self.abs_dirs
-
-    @PreScriptAction('create-virtualenv')
-    def _pre_create_virtualenv(self, action):
-        dirs = self.query_abs_dirs()
-
-        if os.path.isdir(dirs['abs_mochitest_dir']):
-            # mochitest is the only thing that needs this
-            requirements = os.path.join(dirs['abs_mochitest_dir'],
-                        'websocketprocessbridge',
-                        'websocketprocessbridge_requirements.txt')
-
-            self.register_virtualenv_module(requirements=[requirements],
-                                            two_pass=True)
-
-    def _build_arg(self, option, value):
-        """
-        Build a command line argument
-        """
-        if not value:
-            return []
-        return [str(option), str(value)]
-
-    def _redirectSUT(self, emulator_index):
-        '''
-        This redirects the default SUT ports for a given emulator.
-        This is needed if more than one emulator is started.
-        '''
-        emulator = self.emulators[emulator_index]
-        emuport = emulator["emulator_port"]
-        sutport1 = emulator["sut_port1"]
-        sutport2 = emulator["sut_port2"]
-        attempts = 0
-        tn = None
-        redirect_completed = False
-        while attempts < 5:
-            if attempts == 0:
-                self.info("Sleeping 10 seconds")
-                time.sleep(10)
-            else:
-                self.info("Sleeping 30 seconds")
-                time.sleep(30)
-            attempts += 1
-            self.info("  Attempt #%d to redirect ports: (%d, %d, %d)" %
-                      (attempts, emuport, sutport1, sutport2))
-            try:
-                tn = telnetlib.Telnet('localhost', emuport, 300)
-                break
-            except socket.error, e:
-                self.info("Trying again after exception: %s" % str(e))
-                pass
-
-        if tn is not None:
-            res = tn.read_until('OK')
-            if res.find('OK') == -1:
-                self.warning('initial OK prompt not received from emulator: ' + str(res))
-            tn.write('redir add tcp:' + str(sutport1) + ':' + str(self.config["default_sut_port1"]) + '\n')
-            tn.write('redir add tcp:' + str(sutport2) + ':' + str(self.config["default_sut_port2"]) + '\n')
-            tn.write('quit\n')
-            res = tn.read_all()
-            if res.find('OK') == -1:
-                self.warning('error adding redirect: ' + str(res))
-            else:
-                redirect_completed = True
-        else:
-            self.warning('failed to establish a telnet connection with the emulator')
-        return redirect_completed
-
-    def _launch_emulator(self, emulator_index):
-        emulator = self.emulators[emulator_index]
-        env = self.query_env()
-
-        # Set $LD_LIBRARY_PATH to self.dirs['abs_work_dir'] so that
-        # the emulator picks up the symlink to libGL.so.1 that we
-        # constructed in start_emulators.
-        env['LD_LIBRARY_PATH'] = self.abs_dirs['abs_work_dir']
-
-        avd_home_dir = self.abs_dirs['abs_avds_dir']
-        env['ANDROID_AVD_HOME'] = os.path.join(avd_home_dir, 'avd')
-
-        command = [
-            "emulator", "-avd", emulator["name"],
-            "-port", str(emulator["emulator_port"]),
-        ]
-        if "emulator_extra_args" in self.config:
-            command += self.config["emulator_extra_args"].split()
-
-        tmp_file = tempfile.NamedTemporaryFile(mode='w')
-        tmp_stdout = open(tmp_file.name, 'w')
-        self.info("Created temp file %s." % tmp_file.name)
-        self.info("Trying to start the emulator with this command: %s" % ' '.join(command))
-        proc = subprocess.Popen(command, stdout=tmp_stdout, stderr=tmp_stdout, env=env)
-        return {
-            "process": proc,
-            "tmp_file": tmp_file,
-            "tmp_stdout": tmp_stdout
-        }
-
-    def _check_emulator(self, emulator):
-        self.info('Checking emulator %s' % emulator["name"])
-
-        if self.config["device_manager"] == "sut":
-            attempts = 0
-            tn = None
-            contacted_sut = False
-            while attempts < 8 and not contacted_sut:
-                if attempts != 0:
-                    self.info("Sleeping 30 seconds")
-                    time.sleep(30)
-                attempts += 1
-                self.info("  Attempt #%d to connect to SUT on port %d" %
-                          (attempts, emulator["sut_port1"]))
-                try:
-                    tn = telnetlib.Telnet('localhost', emulator["sut_port1"], 10)
-                    if tn is not None:
-                        self.info('Connected to port %d' % emulator["sut_port1"])
-                        res = tn.read_until('$>', 10)
-                        tn.write('quit\n')
-                        if res.find('$>') == -1:
-                            self.warning('Unexpected SUT response: %s' % res)
-                        else:
-                            self.info('SUT response: %s' % res)
-                            contacted_sut = True
-                        tn.read_all()
-                    else:
-                        self.warning('Unable to connect to the SUT agent on port %d' % emulator["sut_port1"])
-                except socket.error, e:
-                    self.info('Trying again after socket error: %s' % str(e))
-                    pass
-                except EOFError:
-                    self.info('Trying again after EOF')
-                    pass
-                except:
-                    self.info('Trying again after unexpected exception')
-                    pass
-                finally:
-                    if tn is not None:
-                        tn.close()
-            if not contacted_sut:
-                self.warning('Unable to communicate with SUT agent on port %d' % emulator["sut_port1"])
-
-        attempts = 0
-        tn = None
-        contacted_emu = False
-        while attempts < 4:
-            if attempts != 0:
-                self.info("Sleeping 30 seconds")
-                time.sleep(30)
-            attempts += 1
-            self.info("  Attempt #%d to connect to emulator on port %d" %
-                      (attempts, emulator["emulator_port"]))
-            try:
-                tn = telnetlib.Telnet('localhost', emulator["emulator_port"], 10)
-                if tn is not None:
-                    self.info('Connected to port %d' % emulator["emulator_port"])
-                    res = tn.read_until('OK', 10)
-                    self.info(res)
-                    tn.write('avd status\n')
-                    res = tn.read_until('OK', 10)
-                    self.info('avd status: %s' % res)
-                    tn.write('redir list\n')
-                    res = tn.read_until('OK', 10)
-                    self.info('redir list: %s' % res)
-                    tn.write('network status\n')
-                    res = tn.read_until('OK', 10)
-                    self.info('network status: %s' % res)
-                    tn.write('quit\n')
-                    tn.read_all()
-                    tn.close()
-                    contacted_emu = True
-                    break
-                else:
-                    self.warning('Unable to connect to the emulator on port %d' % emulator["emulator_port"])
-            except socket.error, e:
-                self.info('Trying again after socket error: %s' % str(e))
-                pass
-            except EOFError:
-                self.info('Trying again after EOF')
-                pass
-            except:
-                self.info('Trying again after unexpected exception')
-                pass
-            finally:
-                if tn is not None:
-                    tn.close()
-        if not contacted_emu:
-            self.warning('Unable to communicate with emulator on port %d' % emulator["emulator_port"])
-
-        ps_cmd = [self.adb_path, '-s', emulator["device_id"], 'shell', 'ps']
-        p = subprocess.Popen(ps_cmd, stdout=subprocess.PIPE)
-        out, err = p.communicate()
-        self.info('%s:\n%s\n%s' % (ps_cmd, out, err))
-
-    def _dump_host_state(self):
-        p = subprocess.Popen(['ps', '-ef'], stdout=subprocess.PIPE)
-        out, err = p.communicate()
-        self.info("output from ps -ef follows...")
-        if out:
-            self.info(out)
-        if err:
-            self.info(err)
-        p = subprocess.Popen(['netstat', '-a', '-p', '-n', '-t', '-u'], stdout=subprocess.PIPE)
-        out, err = p.communicate()
-        self.info("output from netstat -a -p -n -t -u follows...")
-        if out:
-            self.info(out)
-        if err:
-            self.info(err)
-
-    def _dump_emulator_log(self, emulator_index):
-        emulator = self.emulators[emulator_index]
-        self.info("##### %s emulator log begins" % emulator["name"])
-        output = self.read_from_file(self.emulator_procs[emulator_index]["tmp_file"].name, verbose=False)
-        if output:
-            self.info(output)
-        self.info("##### %s emulator log ends" % emulator["name"])
-
-    def _kill_processes(self, process_name):
-        p = subprocess.Popen(['ps', '-A'], stdout=subprocess.PIPE)
-        out, err = p.communicate()
-        self.info("Let's kill every process called %s" % process_name)
-        for line in out.splitlines():
-            if process_name in line:
-                pid = int(line.split(None, 1)[0])
-                self.info("Killing pid %d." % pid)
-                os.kill(pid, signal.SIGKILL)
-
-    @PostScriptRun
-    def _post_script(self):
-        self._kill_processes(self.config["emulator_process_name"])
-
-    # XXX: This and android_panda.py's function might make sense to take higher up
-    def _download_robocop_apk(self):
-        dirs = self.query_abs_dirs()
-        self.apk_url = self.installer_url[:self.installer_url.rfind('/')]
-        robocop_url = self.apk_url + '/robocop.apk'
-        self.info("Downloading robocop...")
-        self.download_file(robocop_url, 'robocop.apk', dirs['abs_work_dir'], error_level=FATAL)
-
-    def _query_package_name(self):
-        if self.app_name is None:
-            #find appname from package-name.txt - assumes download-and-extract has completed successfully
-            apk_dir = self.abs_dirs['abs_work_dir']
-            self.apk_path = os.path.join(apk_dir, self.installer_path)
-            unzip = self.query_exe("unzip")
-            package_path = os.path.join(apk_dir, 'package-name.txt')
-            unzip_cmd = [unzip, '-q', '-o',  self.apk_path]
-            self.run_command(unzip_cmd, cwd=apk_dir, halt_on_failure=True)
-            self.app_name = str(self.read_from_file(package_path, verbose=True)).rstrip()
-        return self.app_name
-
-    def preflight_install(self):
-        # in the base class, this checks for mozinstall, but we don't use it
-        pass
-
-    def _build_command(self, emulator, suite_name):
-        c = self.config
-        dirs = self.query_abs_dirs()
-        suite_category = self.test_suite_definitions[suite_name]["category"]
-
-        if suite_category not in c["suite_definitions"]:
-            self.fatal("Key '%s' not defined in the config!" % suite_category)
-
-        cmd = [
-            self.query_python_path('python'),
-            '-u',
-            os.path.join(
-                self._query_tests_dir(suite_name),
-                c["suite_definitions"][suite_category]["run_filename"]
-            ),
-        ]
-
-        raw_log_file = os.path.join(dirs['abs_blob_upload_dir'],
-                                    '%s_raw.log' % suite_name)
-        error_summary_file = os.path.join(dirs['abs_blob_upload_dir'],
-                                          '%s_errorsummary.log' % suite_name)
-        str_format_values = {
-            'app': self._query_package_name(),
-            'remote_webserver': c['remote_webserver'],
-            'xre_path': self.xre_path,
-            'utility_path': self.xre_path,
-            'http_port': emulator['http_port'],
-            'ssl_port': emulator['ssl_port'],
-            'certs_path': os.path.join(dirs['abs_work_dir'], 'tests/certs'),
-            # TestingMixin._download_and_extract_symbols() will set
-            # self.symbols_path when downloading/extracting.
-            'symbols_path': self.symbols_path,
-            'modules_dir': dirs['abs_modules_dir'],
-            'installer_path': self.installer_path,
-            'raw_log_file': raw_log_file,
-            'error_summary_file': error_summary_file,
-            'dm_trans': c['device_manager'],
-        }
-        if self.config["device_manager"] == "sut":
-            str_format_values.update({
-                'device_ip': c['device_ip'],
-                'device_port': str(emulator['sut_port1']),
-            })
-        for option in c["suite_definitions"][suite_category]["options"]:
-            cmd.extend([option % str_format_values])
-
-        for arg in self.test_suite_definitions[suite_name]["extra_args"]:
-            argname = arg.split('=')[0]
-            # only add the extra arg if it wasn't already defined by in-tree configs
-            if any(a.split('=')[0] == argname for a in cmd):
-                continue
-            cmd.append(arg)
-
-        try_options, try_tests = self.try_args(suite_category)
-        cmd.extend(try_options)
-        cmd.extend(self.query_tests_args(
-            self.config["suite_definitions"][suite_category].get("tests"),
-            self.test_suite_definitions[suite_name].get("tests"),
-            try_tests))
-
-        return cmd
-
-    def preflight_run_tests(self):
-        super(AndroidEmulatorTest, self).preflight_run_tests()
-
-        if not os.path.isfile(self.adb_path):
-            self.fatal("The adb binary '%s' is not a valid file!" % self.adb_path)
-
-    def _trigger_test(self, suite_name, emulator_index):
-        """
-        Run a test suite on an emulator
-
-        We return a dictionary with the following information:
-         - subprocess object that is running the test on the emulator
-         - the filename where the stdout is going to
-         - the stdout where the output is going to
-         - the suite name that is associated
-        """
-        cmd = self._build_command(self.emulators[emulator_index], suite_name)
-
-        try:
-            cwd = self._query_tests_dir(suite_name)
-        except:
-            self.fatal("Don't know how to run --test-suite '%s'!" % suite_name)
-
-        env = self.query_env()
-        if self.query_minidump_stackwalk():
-            env['MINIDUMP_STACKWALK'] = self.minidump_stackwalk_path
-        env['MOZ_UPLOAD_DIR'] = self.query_abs_dirs()['abs_blob_upload_dir']
-        env['MINIDUMP_SAVE_PATH'] = self.query_abs_dirs()['abs_blob_upload_dir']
-
-        self.info("Running on %s the command %s" % (self.emulators[emulator_index]["name"], subprocess.list2cmdline(cmd)))
-        tmp_file = tempfile.NamedTemporaryFile(mode='w')
-        tmp_stdout = open(tmp_file.name, 'w')
-        self.info("Created temp file %s." % tmp_file.name)
-        return {
-            "process": subprocess.Popen(cmd, cwd=cwd, stdout=tmp_stdout, stderr=tmp_stdout, env=env),
-            "tmp_file": tmp_file,
-            "tmp_stdout": tmp_stdout,
-            "suite_name": suite_name,
-            "emulator_index": emulator_index
-        }
-
-    def _get_repo_url(self, path):
-        """
-           Return a url for a file (typically a tooltool manifest) in this hg repo
-           and using this revision (or mozilla-central/default if repo/rev cannot
-           be determined).
-
-           :param path specifies the directory path to the file of interest.
-        """
-        if 'GECKO_HEAD_REPOSITORY' in os.environ and 'GECKO_HEAD_REV' in os.environ:
-            # probably taskcluster
-            repo = os.environ['GECKO_HEAD_REPOSITORY']
-            revision = os.environ['GECKO_HEAD_REV']
-        elif self.buildbot_config and 'properties' in self.buildbot_config:
-            # probably buildbot
-            repo = 'https://hg.mozilla.org/%s' % self.buildbot_config['properties']['repo_path']
-            revision = self.buildbot_config['properties']['revision']
-        else:
-            # something unexpected!
-            repo = 'https://hg.mozilla.org/mozilla-central'
-            revision = 'default'
-            self.warning('Unable to find repo/revision for manifest; using mozilla-central/default')
-        url = '%s/raw-file/%s/%s' % (
-            repo,
-            revision,
-            path)
-        return url
-
-    def _tooltool_fetch(self, url, dir):
-        c = self.config
-
-        manifest_path = self.download_file(
-            url,
-            file_name='releng.manifest',
-            parent_dir=dir
-        )
-
-        if not os.path.exists(manifest_path):
-            self.fatal("Could not retrieve manifest needed to retrieve "
-                       "artifacts from %s" % manifest_path)
-
-        self.tooltool_fetch(manifest_path,
-                            output_dir=dir,
-                            cache=c.get("tooltool_cache", None))
-
-    ##########################################
-    ### Actions for AndroidEmulatorTest ###
-    ##########################################
-    def setup_avds(self):
-        '''
-        If tooltool cache mechanism is enabled, the cached version is used by
-        the fetch command. If the manifest includes an "unpack" field, tooltool
-        will unpack all compressed archives mentioned in the manifest.
-        '''
-        c = self.config
-        dirs = self.query_abs_dirs()
-
-        # FIXME
-        # Clobbering and re-unpacking would not be needed if we had a way to
-        # check whether the unpacked content already present match the
-        # contents of the tar ball
-        self.rmtree(dirs['abs_avds_dir'])
-        self.mkdir_p(dirs['abs_avds_dir'])
-        url = self._get_repo_url(c["tooltool_manifest_path"])
-        self._tooltool_fetch(url, dirs['abs_avds_dir'])
-
-        avd_home_dir = self.abs_dirs['abs_avds_dir']
-        if avd_home_dir != "/home/cltbld/.android":
-            # Modify the downloaded avds to point to the right directory.
-            cmd = [
-                'bash', '-c',
-                'sed -i "s|/home/cltbld/.android|%s|" %s/test-*.ini' %
-                (avd_home_dir, os.path.join(avd_home_dir, 'avd'))
-            ]
-            proc = ProcessHandler(cmd)
-            proc.run()
-            proc.wait()
-
-
-    def start_emulators(self):
-        '''
-        This action starts the emulators and redirects the two SUT ports for each one of them
-        '''
-        assert len(self.test_suites) <= len(self.emulators), \
-            "We can't run more tests that the number of emulators we start"
-
-        if 'emulator_url' in self.config or 'emulator_manifest' in self.config or 'tools_manifest' in self.config:
-            self.install_emulator()
-
-        if not self.config.get("developer_mode"):
-            # We kill compiz because it sometimes prevents us from starting the emulators
-            self._kill_processes("compiz")
-            self._kill_processes("xpcshell")
-
-        # We add a symlink for libGL.so because the emulator dlopen()s it by that name
-        # even though the installed library on most systems without dev packages is
-        # libGL.so.1
-        linkfile = os.path.join(self.abs_dirs['abs_work_dir'], "libGL.so")
-        self.info("Attempting to establish symlink for %s" % linkfile)
-        try:
-            os.unlink(linkfile)
-        except OSError:
-            pass
-        for libdir in ["/usr/lib/x86_64-linux-gnu/mesa",
-                       "/usr/lib/i386-linux-gnu/mesa",
-                       "/usr/lib/mesa"]:
-            libfile = os.path.join(libdir, "libGL.so.1")
-            if os.path.exists(libfile):
-                self.info("Symlinking %s -> %s" % (linkfile, libfile))
-                self.mkdir_p(self.abs_dirs['abs_work_dir'])
-                os.symlink(libfile, linkfile)
-                break
-
-        attempts = 0
-        redirect_failed = True
-        # Launch the required emulators and redirect the SUT ports for each. If unable
-        # to redirect the SUT ports, kill the emulators and try starting them again.
-        # The wait-and-retry logic is necessary because the emulators intermittently fail
-        # to respond to telnet connections immediately after startup: bug 949740. In this
-        # case, the emulator log shows "ioctl(KVM_CREATE_VM) failed: Interrupted system call".
-        # We do not know how to avoid this error and the only way we have found to
-        # recover is to kill the emulator and start again.
-        while attempts < 3 and redirect_failed:
-            if attempts > 0:
-                self.info("Sleeping 30 seconds before retry")
-                time.sleep(30)
-            attempts += 1
-            self.info('Attempt #%d to launch emulators...' % attempts)
-            self._dump_host_state()
-            self.emulator_procs = []
-            emulator_index = 0
-            redirect_failed = False
-            for test in self.test_suites:
-                emulator_proc = self._launch_emulator(emulator_index)
-                self.emulator_procs.append(emulator_proc)
-                if self.config["device_manager"] == "sut":
-                    if self._redirectSUT(emulator_index):
-                        emulator = self.emulators[emulator_index]
-                        self.info("%s: %s; sut port: %s/%s" %
-                                  (emulator["name"], emulator["emulator_port"], emulator["sut_port1"], emulator["sut_port2"]))
-                        emulator_index += 1
-                    else:
-                        self._dump_emulator_log(emulator_index)
-                        self._kill_processes(self.config["emulator_process_name"])
-                        redirect_failed = True
-                        break
-        if redirect_failed:
-            self.fatal('We have not been able to establish a telnet connection with the emulator')
-
-        # Verify that we can communicate with each emulator
-        emulator_index = 0
-        for test in self.test_suites:
-            emulator = self.emulators[emulator_index]
-            emulator_index += 1
-            self._check_emulator(emulator)
-        # Start logcat for each emulator. Each adb process runs until the
-        # corresponding emulator is killed. Output is written directly to
-        # the blobber upload directory so that it is uploaded automatically
-        # at the end of the job.
-        self.mkdir_p(self.abs_dirs['abs_blob_upload_dir'])
-        emulator_index = 0
-        for test in self.test_suites:
-            emulator = self.emulators[emulator_index]
-            emulator_index += 1
-            logcat_filename = 'logcat-%s.log' % emulator["device_id"]
-            logcat_path = os.path.join(self.abs_dirs['abs_blob_upload_dir'], logcat_filename)
-            logcat_cmd = '%s -s %s logcat -v time Trace:S StrictMode:S ExchangeService:S > %s &' % \
-                (self.adb_path, emulator["device_id"], logcat_path)
-            self.info(logcat_cmd)
-            os.system(logcat_cmd)
-        # Create the /data/anr directory on each emulator image.
-        emulator_index = 0
-        for test in self.test_suites:
-            emulator = self.emulators[emulator_index]
-            emulator_index += 1
-            mkdir_cmd = [self.adb_path, '-s', emulator["device_id"], 'shell', 'mkdir', '/data/anr']
-            p = subprocess.Popen(mkdir_cmd, stdout=subprocess.PIPE)
-            out, err = p.communicate()
-            self.info('%s:\n%s\n%s' % (mkdir_cmd, out, err))
-
-    def download_and_extract(self):
-        # This will download and extract the fennec.apk and tests.zip
-        suite_categories = set([self.test_suite_definitions[c]['category']
-                                for c in self.test_suites])
-        super(AndroidEmulatorTest, self).download_and_extract(suite_categories=suite_categories)
-        dirs = self.query_abs_dirs()
-
-        for suite_name in self.test_suites:
-            if suite_name.startswith('robocop'):
-                self._download_robocop_apk()
-                break
-
-        self.rmtree(dirs['abs_xre_dir'])
-        self.mkdir_p(dirs['abs_xre_dir'])
-        if self.config["hostutils_manifest_path"]:
-            url = self._get_repo_url(self.config["hostutils_manifest_path"])
-            self._tooltool_fetch(url, dirs['abs_xre_dir'])
-            for p in glob.glob(os.path.join(dirs['abs_xre_dir'], 'host-utils-*')):
-                if os.path.isdir(p) and os.path.isfile(os.path.join(p, 'xpcshell')):
-                    self.xre_path = p
-            if not self.xre_path:
-                self.fatal("xre path not found in %s" % dirs['abs_xre_dir'])
-        else:
-            self.fatal("configure hostutils_manifest_path!")
-
-    def install(self):
-        assert self.installer_path is not None, \
-            "Either add installer_path to the config or use --installer-path."
-
-        emulator_index = 0
-        for suite_name in self.test_suites:
-            emulator = self.emulators[emulator_index]
-            emulator_index += 1
-
-            config = {
-                'device-id': emulator["device_id"],
-                'enable_automation': True,
-                'device_package_name': self._query_package_name()
-            }
-            config = dict(config.items() + self.config.items())
-
-            # Wait for Android to finish booting
-            completed = None
-            retries = 0
-            while retries < 30:
-                completed = self.get_output_from_command([self.adb_path,
-                    "-s", emulator['device_id'], "shell",
-                    "getprop", "sys.boot_completed"])
-                if completed == '1':
-                    break
-                time.sleep(10)
-                retries = retries + 1
-            if completed != '1':
-                self.warning('Retries exhausted waiting for Android boot.')
-
-            cmd = [self.adb_path, '-s', emulator['device_id'], 'shell', 'getprop', 'ro.build.version.sdk']
-            p = subprocess.Popen(cmd, stdout=subprocess.PIPE)
-            out, err = p.communicate()
-            sdk_level = out
-
-            # Install Fennec
-            self.info("Installing Fennec for %s" % emulator["name"])
-            if int(sdk_level) >= 23:
-                cmd = [self.adb_path, '-s', emulator['device_id'], 'install', '-r', '-g', self.installer_path]
-            else:
-                cmd = [self.adb_path, '-s', emulator['device_id'], 'install', '-r', self.installer_path]
-            p = subprocess.Popen(cmd, stdout=subprocess.PIPE)
-            out, err = p.communicate()
-
-            # Install the robocop apk if required
-            if suite_name.startswith('robocop'):
-                self.info("Installing Robocop for %s" % emulator["name"])
-                if int(sdk_level) >= 23:
-                    cmd = [self.adb_path, '-s', emulator['device_id'], 'install', '-r', '-g', self.robocop_path]
-                else:
-                    cmd = [self.adb_path, '-s', emulator['device_id'], 'install', '-r', self.robocop_path]
-                p = subprocess.Popen(cmd, stdout=subprocess.PIPE)
-                out, err = p.communicate()
-
-            self.info("Finished installing apps for %s" % emulator["name"])
-
-    def run_tests(self):
-        """
-        Run the tests
-        """
-        procs = []
-
-        emulator_index = 0
-        for suite_name in self.test_suites:
-            procs.append(self._trigger_test(suite_name, emulator_index))
-            emulator_index += 1
-
-        joint_tbpl_status = None
-        joint_log_level = None
-        start_time = int(time.time())
-        while True:
-            for p in procs:
-                emulator_index = p["emulator_index"]
-                return_code = p["process"].poll()
-                if return_code is not None:
-                    suite_name = p["suite_name"]
-                    # To make reading the log of the suite not mix with the previous line
-                    sys.stdout.write('\n')
-                    self.info("##### %s log begins" % p["suite_name"])
-                    # Let's close the stdout
-                    p["tmp_stdout"].close()
-                    # Let's read the file that now has the output
-                    output = self.read_from_file(p["tmp_file"].name, verbose=False)
-                    # Let's parse the output (which also prints it)
-                    # and determine what the results should be
-                    parser = self.get_test_output_parser(
-                        self.test_suite_definitions[p["suite_name"]]["category"],
-                        config=self.config,
-                        log_obj=self.log_obj,
-                        error_list=self.error_list)
-                    for line in output.splitlines():
-                        parser.parse_single_line(line)
-
-                    # After parsing each line we should know what the summary for this suite should be
-                    tbpl_status, log_level = parser.evaluate_parser(0)
-                    parser.append_tinderboxprint_line(p["suite_name"])
-                    # After running all jobs we will report the worst status of all emulator runs
-                    joint_tbpl_status = self.worst_level(tbpl_status, joint_tbpl_status, TBPL_WORST_LEVEL_TUPLE)
-                    joint_log_level = self.worst_level(log_level, joint_log_level)
-
-                    self.info("##### %s log ends" % p["suite_name"])
-                    self._dump_emulator_log(emulator_index)
-                    procs.remove(p)
-            if procs == []:
-                break
-            else:
-                # Every 5 minutes let's print something to stdout
-                # so buildbot won't kill the process due to lack of output
-                if int(time.time()) - start_time > 5 * 60:
-                    self.info('#')
-                    start_time = int(time.time())
-                time.sleep(30)
-
-        self.buildbot_status(joint_tbpl_status, level=joint_log_level)
-
-    @PostScriptAction('run-tests')
-    def stop_emulators(self, action, success=None):
-        '''
-        Report emulator health, then make sure that every emulator has been stopped
-        '''
-        emulator_index = 0
-        for test in self.test_suites:
-            emulator = self.emulators[emulator_index]
-            emulator_index += 1
-            self._check_emulator(emulator)
-        self._kill_processes(self.config["emulator_process_name"])
-
-if __name__ == '__main__':
-    emulatorTest = AndroidEmulatorTest()
-    emulatorTest.run_and_exit()
--- a/widget/cocoa/nsDragService.h
+++ b/widget/cocoa/nsDragService.h
@@ -34,17 +34,17 @@ public:
   NS_IMETHOD GetNumDropItems(uint32_t * aNumItems);
 
 protected:
   virtual ~nsDragService();
 
 private:
 
   NSImage* ConstructDragImage(nsIDOMNode* aDOMNode,
-                              nsIntRect* aDragRect,
+                              mozilla::LayoutDeviceIntRect* aDragRect,
                               nsIScriptableRegion* aRegion);
   bool IsValidType(NSString* availableType, bool allowFileURL);
   NSString* GetStringForType(NSPasteboardItem* item, const NSString* type,
                              bool allowFileURL = false);
   NSString* GetTitleForURL(NSPasteboardItem* item);
   NSString* GetFilePath(NSPasteboardItem* item);
 
   nsCOMPtr<nsIArray> mDataItems; // only valid for a drag started within gecko
--- a/widget/cocoa/nsDragService.mm
+++ b/widget/cocoa/nsDragService.mm
@@ -121,51 +121,40 @@ static nsresult SetUpDragClipboard(nsIAr
 
   return NS_OK;
 
   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
 }
 
 NSImage*
 nsDragService::ConstructDragImage(nsIDOMNode* aDOMNode,
-                                  nsIntRect* aDragRect,
+                                  LayoutDeviceIntRect* aDragRect,
                                   nsIScriptableRegion* aRegion)
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
 
-  NSPoint screenPoint =
-    nsCocoaUtils::ConvertPointToScreen([gLastDragView window],
-                                       [gLastDragMouseDownEvent locationInWindow]);
-  // Y coordinates are bottom to top, so reverse this
-  screenPoint.y = nsCocoaUtils::FlippedScreenY(screenPoint.y);
-
   CGFloat scaleFactor = nsCocoaUtils::GetBackingScaleFactor(gLastDragView);
 
   RefPtr<SourceSurface> surface;
   nsPresContext* pc;
-  nsresult rv = DrawDrag(aDOMNode, aRegion,
-                         NSToIntRound(screenPoint.x),
-                         NSToIntRound(screenPoint.y),
+  nsresult rv = DrawDrag(aDOMNode, aRegion, mScreenPosition,
                          aDragRect, &surface, &pc);
-  if (!aDragRect->width || !aDragRect->height) {
+  if (pc && (!aDragRect->width || !aDragRect->height)) {
     // just use some suitable defaults
     int32_t size = nsCocoaUtils::CocoaPointsToDevPixels(20, scaleFactor);
-    aDragRect->SetRect(nsCocoaUtils::CocoaPointsToDevPixels(screenPoint.x, scaleFactor),
-                       nsCocoaUtils::CocoaPointsToDevPixels(screenPoint.y, scaleFactor),
-                       size, size);
+    aDragRect->SetRect(pc->CSSPixelsToDevPixels(mScreenPosition.x),
+                       pc->CSSPixelsToDevPixels(mScreenPosition.y), size, size);
   }
 
   if (NS_FAILED(rv) || !surface)
     return nil;
 
   uint32_t width = aDragRect->width;
   uint32_t height = aDragRect->height;
 
-
-
   RefPtr<DataSourceSurface> dataSurface =
     Factory::CreateDataSourceSurface(IntSize(width, height),
                                      SurfaceFormat::B8G8R8A8);
   DataSourceSurface::MappedSurface map;
   if (!dataSurface->Map(DataSourceSurface::MapType::READ_WRITE, &map)) {
     return nil;
   }
 
@@ -313,39 +302,40 @@ nsDragService::InvokeDragSessionImpl(nsI
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
 
   mDataItems = aTransferableArray;
 
   // put data on the clipboard
   if (NS_FAILED(SetUpDragClipboard(aTransferableArray)))
     return NS_ERROR_FAILURE;
 
-  nsIntRect dragRect(0, 0, 20, 20);
+  CGFloat scaleFactor = nsCocoaUtils::GetBackingScaleFactor(gLastDragView);
+
+  LayoutDeviceIntRect dragRect(0, 0, 20, 20);
   NSImage* image = ConstructDragImage(mSourceNode, &dragRect, aDragRgn);
   if (!image) {
     // if no image was returned, just draw a rectangle
     NSSize size;
-    size.width = dragRect.width;
-    size.height = dragRect.height;
+    size.width = nsCocoaUtils::DevPixelsToCocoaPoints(dragRect.width, scaleFactor);
+    size.height = nsCocoaUtils::DevPixelsToCocoaPoints(dragRect.height, scaleFactor);
     image = [[NSImage alloc] initWithSize:size];
     [image lockFocus];
     [[NSColor grayColor] set];
     NSBezierPath* path = [NSBezierPath bezierPath];
     [path setLineWidth:2.0];
     [path moveToPoint:NSMakePoint(0, 0)];
     [path lineToPoint:NSMakePoint(0, size.height)];
     [path lineToPoint:NSMakePoint(size.width, size.height)];
     [path lineToPoint:NSMakePoint(size.width, 0)];
     [path lineToPoint:NSMakePoint(0, 0)];
     [path stroke];
     [image unlockFocus];
   }
 
   LayoutDeviceIntPoint pt(dragRect.x, dragRect.YMost());
-  CGFloat scaleFactor = nsCocoaUtils::GetBackingScaleFactor(gLastDragView);
   NSPoint point = nsCocoaUtils::DevPixelsToCocoaPoints(pt, scaleFactor);
   point.y = nsCocoaUtils::FlippedScreenY(point.y);
 
   point = nsCocoaUtils::ConvertPointFromScreen([gLastDragView window], point);
   NSPoint localPoint = [gLastDragView convertPoint:point fromView:nil];
  
   // Save the transferables away in case a promised file callback is invoked.
   gDraggedTransferables = aTransferableArray;
--- a/widget/gtk/nsDragService.cpp
+++ b/widget/gtk/nsDragService.cpp
@@ -414,17 +414,17 @@ nsDragService::InvokeDragSessionImpl(nsI
     return rv;
 }
 
 bool
 nsDragService::SetAlphaPixmap(SourceSurface *aSurface,
                               GdkDragContext *aContext,
                               int32_t aXOffset,
                               int32_t aYOffset,
-                              const nsIntRect& dragRect)
+                              const LayoutDeviceIntRect& dragRect)
 {
     GdkScreen* screen = gtk_widget_get_screen(mHiddenWidget);
 
     // Transparent drag icons need, like a lot of transparency-related things,
     // a compositing X window manager
     if (!gdk_screen_is_composited(screen))
       return false;
 
@@ -478,17 +478,17 @@ nsDragService::SetAlphaPixmap(SourceSurf
     cairo_surface_t *surf = cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
                                                        dragRect.width,
                                                        dragRect.height);
     if (!surf)
         return false;
 
     RefPtr<DrawTarget> dt = gfxPlatform::CreateDrawTargetForData(
                                 cairo_image_surface_get_data(surf),
-                                dragRect.Size(),
+                                nsIntSize(dragRect.width, dragRect.height),
                                 cairo_image_surface_get_stride(surf),
                                 SurfaceFormat::B8G8R8A8);
     if (!dt)
         return false;
 
     dt->ClearRect(Rect(0, 0, dragRect.width, dragRect.height));
     dt->DrawSurface(aSurface,
                     Rect(0, 0, dragRect.width, dragRect.height),
@@ -1623,29 +1623,28 @@ nsDragService::SourceDataGet(GtkWidget  
     }
 }
 
 void nsDragService::SetDragIcon(GdkDragContext* aContext)
 {
     if (!mHasImage && !mSelection)
         return;
 
-    nsIntRect dragRect;
+    LayoutDeviceIntRect dragRect;
     nsPresContext* pc;
     RefPtr<SourceSurface> surface;
-    DrawDrag(mSourceNode, mSourceRegion, mScreenX, mScreenY,
+    DrawDrag(mSourceNode, mSourceRegion, mScreenPosition,
              &dragRect, &surface, &pc);
     if (!pc)
         return;
 
-    int32_t sx = mScreenX, sy = mScreenY;
-    ConvertToUnscaledDevPixels(pc, &sx, &sy);
-
-    int32_t offsetX = sx - dragRect.x;
-    int32_t offsetY = sy - dragRect.y;
+    LayoutDeviceIntPoint screenPoint =
+      ConvertToUnscaledDevPixels(pc, mScreenPosition);
+    int32_t offsetX = screenPoint.x - dragRect.x;
+    int32_t offsetY = screenPoint.y - dragRect.y;
 
     // If a popup is set as the drag image, use its widget. Otherwise, use
     // the surface that DrawDrag created.
     //
     // XXX: Disable drag popups on GTK 3.19.4 and above: see bug 1264454.
     //      Fix this once a new GTK version ships that does not destroy our
     //      widget in gtk_drag_set_icon_widget.
     if (mDragPopup && gtk_check_version(3, 19, 4)) {
--- a/widget/gtk/nsDragService.h
+++ b/widget/gtk/nsDragService.h
@@ -202,17 +202,17 @@ private:
     GtkTargetList *GetSourceList(void);
 
     // attempts to create a semi-transparent drag image. Returns TRUE if
     // successful, FALSE if not
     bool SetAlphaPixmap(SourceSurface *aPixbuf,
                         GdkDragContext  *aContext,
                         int32_t          aXOffset,
                         int32_t          aYOffset,
-                        const nsIntRect &dragRect);
+                        const mozilla::LayoutDeviceIntRect &dragRect);
 
     gboolean Schedule(DragTask aTask, nsWindow *aWindow,
                       GdkDragContext *aDragContext,
                       mozilla::LayoutDeviceIntPoint aWindowPoint, guint aTime);
 
     // Callback for g_idle_add_full() to run mScheduledTask.
     static gboolean TaskDispatchCallback(gpointer data);
     gboolean RunScheduledTask();
--- a/widget/nsBaseDragService.cpp
+++ b/widget/nsBaseDragService.cpp
@@ -52,18 +52,17 @@ using namespace mozilla::image;
 
 nsBaseDragService::nsBaseDragService()
   : mCanDrop(false), mOnlyChromeDrop(false), mDoingDrag(false),
     mHasImage(false), mUserCancelled(false),
     mDragEventDispatchedToChildProcess(false),
     mDragAction(DRAGDROP_ACTION_NONE),
     mDragActionFromChildProcess(DRAGDROP_ACTION_UNINITIALIZED), mTargetSize(0,0),
     mContentPolicyType(nsIContentPolicy::TYPE_OTHER),
-    mScreenX(-1), mScreenY(-1), mSuppressLevel(0),
-    mInputSource(nsIDOMMouseEvent::MOZ_SOURCE_MOUSE)
+    mSuppressLevel(0), mInputSource(nsIDOMMouseEvent::MOZ_SOURCE_MOUSE)
 {
 }
 
 nsBaseDragService::~nsBaseDragService()
 {
 }
 
 NS_IMPL_ISUPPORTS(nsBaseDragService, nsIDragService, nsIDragSession)
@@ -257,18 +256,18 @@ nsBaseDragService::InvokeDragSessionWith
 
   mDataTransfer = aDataTransfer;
   mSelection = nullptr;
   mHasImage = true;
   mDragPopup = nullptr;
   mImage = aImage;
   mImageOffset = CSSIntPoint(aImageX, aImageY);
 
-  aDragEvent->GetScreenX(&mScreenX);
-  aDragEvent->GetScreenY(&mScreenY);
+  aDragEvent->GetScreenX(&mScreenPosition.x);
+  aDragEvent->GetScreenY(&mScreenPosition.y);
   aDragEvent->GetMozInputSource(&mInputSource);
 
   nsresult rv = InvokeDragSession(aDOMNode, aTransferableArray,
                                   aRegion, aActionType,
                                   nsIContentPolicy::TYPE_INTERNAL_IMAGE);
 
   if (NS_FAILED(rv)) {
     mImage = nullptr;
@@ -292,18 +291,18 @@ nsBaseDragService::InvokeDragSessionWith
 
   mDataTransfer = aDataTransfer;
   mSelection = aSelection;
   mHasImage = true;
   mDragPopup = nullptr;
   mImage = nullptr;
   mImageOffset = CSSIntPoint();
 
-  aDragEvent->GetScreenX(&mScreenX);
-  aDragEvent->GetScreenY(&mScreenY);
+  aDragEvent->GetScreenX(&mScreenPosition.x);
+  aDragEvent->GetScreenY(&mScreenPosition.y);
   aDragEvent->GetMozInputSource(&mInputSource);
 
   // just get the focused node from the selection
   // XXXndeakin this should actually be the deepest node that contains both
   // endpoints of the selection
   nsCOMPtr<nsIDOMNode> node;
   aSelection->GetFocusNode(getter_AddRefs(node));
 
@@ -354,18 +353,18 @@ nsBaseDragService::StartDragSession()
 }
 
 void
 nsBaseDragService::OpenDragPopup()
 {
   if (mDragPopup) {
     nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
     if (pm) {
-      pm->ShowPopupAtScreen(mDragPopup, mScreenX - mImageOffset.x,
-                            mScreenY - mImageOffset.y, false, nullptr);
+      pm->ShowPopupAtScreen(mDragPopup, mScreenPosition.x - mImageOffset.x,
+                            mScreenPosition.y - mImageOffset.y, false, nullptr);
     }
   }
 }
 
 int32_t
 nsBaseDragService::TakeChildProcessDragAction()
 {
   // If the last event was dispatched to the child process, use the drag action
@@ -414,18 +413,17 @@ nsBaseDragService::EndDragSession(bool a
   mSourceNode = nullptr;
   mSelection = nullptr;
   mDataTransfer = nullptr;
   mHasImage = false;
   mUserCancelled = false;
   mDragPopup = nullptr;
   mImage = nullptr;
   mImageOffset = CSSIntPoint();
-  mScreenX = -1;
-  mScreenY = -1;
+  mScreenPosition = CSSIntPoint();
   mEndDragPoint = LayoutDeviceIntPoint(0, 0);
   mInputSource = nsIDOMMouseEvent::MOZ_SOURCE_MOUSE;
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsBaseDragService::FireDragEventAtSource(EventMessage aEventMessage)
@@ -494,29 +492,28 @@ GetPresShellForContent(nsIDOMNode* aDOMN
   }
 
   return nullptr;
 }
 
 nsresult
 nsBaseDragService::DrawDrag(nsIDOMNode* aDOMNode,
                             nsIScriptableRegion* aRegion,
-                            int32_t aScreenX, int32_t aScreenY,
-                            nsIntRect* aScreenDragRect,
+                            CSSIntPoint aScreenPosition,
+                            LayoutDeviceIntRect* aScreenDragRect,
                             RefPtr<SourceSurface>* aSurface,
                             nsPresContext** aPresContext)
 {
   *aSurface = nullptr;
   *aPresContext = nullptr;
 
   // use a default size, in case of an error.
-  aScreenDragRect->x = aScreenX - mImageOffset.x;
-  aScreenDragRect->y = aScreenY - mImageOffset.y;
-  aScreenDragRect->width = 1;
-  aScreenDragRect->height = 1;
+  aScreenDragRect->MoveTo(aScreenPosition.x - mImageOffset.x,
+                          aScreenPosition.y - mImageOffset.y);
+  aScreenDragRect->SizeTo(1, 1);
 
   // if a drag image was specified, use that, otherwise, use the source node
   nsCOMPtr<nsIDOMNode> dragNode = mImage ? mImage.get() : aDOMNode;
 
   // get the presshell for the node being dragged. If the drag image is not in
   // a document or has no frame, get the presshell from the source drag node
   nsIPresShell* presShell = GetPresShellForContent(dragNode);
   if (!presShell && mImage)
@@ -527,105 +524,89 @@ nsBaseDragService::DrawDrag(nsIDOMNode* 
   *aPresContext = presShell->GetPresContext();
 
   nsCOMPtr<nsIFrameLoaderOwner> flo = do_QueryInterface(dragNode);
   if (flo) {
     RefPtr<nsFrameLoader> fl = flo->GetFrameLoader();
     if (fl) {
       mozilla::dom::TabParent* tp =
         static_cast<mozilla::dom::TabParent*>(fl->GetRemoteBrowser());
-      if (tp) {
-        int32_t x, y;
-        tp->TakeDragVisualization(*aSurface, x, y);
-        if (*aSurface) {
-          if (mImage) {
-            // Just clear the surface if chrome has overridden it with an image.
-            *aSurface = nullptr;
-          } else {
-            nsIFrame* f = fl->GetOwnerContent()->GetPrimaryFrame();
-            if (f) {
-              aScreenDragRect->x = x;
-              aScreenDragRect->y = y;
-              aScreenDragRect->width = (*aSurface)->GetSize().width;
-              aScreenDragRect->height = (*aSurface)->GetSize().height;
-            }
-            return NS_OK;
-          }
+      if (tp && tp->TakeDragVisualization(*aSurface, aScreenDragRect)) {
+        if (mImage) {
+          // Just clear the surface if chrome has overridden it with an image.
+          *aSurface = nullptr;
         }
+
+        return NS_OK;
       }
     }
   }
 
   // convert mouse position to dev pixels of the prescontext
-  int32_t sx = aScreenX, sy = aScreenY;
-  ConvertToUnscaledDevPixels(*aPresContext, &sx, &sy);
-
-  aScreenDragRect->x = sx - mImageOffset.x;
-  aScreenDragRect->y = sy - mImageOffset.y;
+  CSSIntPoint screenPosition(aScreenPosition);
+  screenPosition.x -= mImageOffset.x;
+  screenPosition.y -= mImageOffset.y;
+  LayoutDeviceIntPoint screenPoint = ConvertToUnscaledDevPixels(*aPresContext, screenPosition);
+  aScreenDragRect->x = screenPoint.x;
+  aScreenDragRect->y = screenPoint.y;
 
   // check if drag images are disabled
   bool enableDragImages = Preferences::GetBool(DRAGIMAGES_PREF, true);
 
   // didn't want an image, so just set the screen rectangle to the frame size
   if (!enableDragImages || !mHasImage) {
     // if a region was specified, set the screen rectangle to the area that
     // the region occupies
+    nsIntRect dragRect;
     if (aRegion) {
       // the region's coordinates are relative to the root frame
+      aRegion->GetBoundingBox(&dragRect.x, &dragRect.y, &dragRect.width, &dragRect.height);
+
       nsIFrame* rootFrame = presShell->GetRootFrame();
-      if (rootFrame) {
-        nsIntRect dragRect;
-        aRegion->GetBoundingBox(&dragRect.x, &dragRect.y, &dragRect.width, &dragRect.height);
-        dragRect = ToAppUnits(dragRect, nsPresContext::AppUnitsPerCSSPixel()).
-                            ToOutsidePixels((*aPresContext)->AppUnitsPerDevPixel());
-
-        nsIntRect screenRect = rootFrame->GetScreenRect();
-        aScreenDragRect->SetRect(screenRect.x + dragRect.x, screenRect.y + dragRect.y,
-                                 dragRect.width, dragRect.height);
-      }
+      nsIntRect screenRect = rootFrame->GetScreenRect();
+      dragRect.MoveBy(screenRect.TopLeft());
     }
     else {
       // otherwise, there was no region so just set the rectangle to
       // the size of the primary frame of the content.
       nsCOMPtr<nsIContent> content = do_QueryInterface(dragNode);
       nsIFrame* frame = content->GetPrimaryFrame();
       if (frame) {
-        nsIntRect screenRect = frame->GetScreenRect();
-        aScreenDragRect->SetRect(screenRect.x, screenRect.y,
-                                 screenRect.width, screenRect.height);
+        dragRect = frame->GetScreenRect();
       }
     }
 
+    dragRect = ToAppUnits(dragRect, nsPresContext::AppUnitsPerCSSPixel()).
+                          ToOutsidePixels((*aPresContext)->AppUnitsPerDevPixel());
+    aScreenDragRect->SizeTo(dragRect.width, dragRect.height);
     return NS_OK;
   }
 
   // draw the image for selections
   if (mSelection) {
-    nsIntPoint pnt(aScreenDragRect->x, aScreenDragRect->y);
+    LayoutDeviceIntPoint pnt(aScreenDragRect->TopLeft());
     *aSurface = presShell->RenderSelection(mSelection, pnt, aScreenDragRect,
         mImage ? 0 : nsIPresShell::RENDER_AUTO_SCALE);
     return NS_OK;
   }
 
   // if a custom image was specified, check if it is an image node and draw
   // using the source rather than the displayed image. But if mImage isn't
   // an image or canvas, fall through to RenderNode below.
   if (mImage) {
     nsCOMPtr<nsIContent> content = do_QueryInterface(dragNode);
     HTMLCanvasElement *canvas = HTMLCanvasElement::FromContentOrNull(content);
     if (canvas) {
-      return DrawDragForImage(nullptr, canvas, sx, sy,
-                              aScreenDragRect, aSurface);
+      return DrawDragForImage(*aPresContext, nullptr, canvas, aScreenDragRect, aSurface);
     }
 
     nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(dragNode);
     // for image nodes, create the drag image from the actual image data
     if (imageLoader) {
-      return DrawDragForImage(imageLoader, nullptr, sx, sy,
-                              aScreenDragRect, aSurface);
+      return DrawDragForImage(*aPresContext, imageLoader, nullptr, aScreenDragRect, aSurface);
     }
 
     // If the image is a popup, use that as the image. This allows custom drag
     // images that can change during the drag, but means that any platform
     // default image handling won't occur.
     // XXXndeakin this should be chrome-only
 
     nsIFrame* frame = content->GetPrimaryFrame();
@@ -662,37 +643,36 @@ nsBaseDragService::DrawDrag(nsIDOMNode* 
             // if the dragnnode contains a image, set RENDER_IS_IMAGE flag
             renderFlags = renderFlags | nsIPresShell::RENDER_IS_IMAGE;
             break;
           }
           count++;
         }
       }
     }
-    nsIntPoint pnt(aScreenDragRect->x, aScreenDragRect->y);
+    LayoutDeviceIntPoint pnt(aScreenDragRect->TopLeft());
     *aSurface = presShell->RenderNode(dragNode, aRegion ? &clipRegion : nullptr,
-                                    pnt, aScreenDragRect,
-                                    renderFlags);
+                                      pnt, aScreenDragRect,
+                                      renderFlags);
   }
 
-  // if an image was specified, reposition the drag rectangle to
-  // the supplied offset in mImageX and mImageY.
+  // If an image was specified, reset the position from the offset that was supplied.
   if (mImage) {
-    aScreenDragRect->x = sx - mImageOffset.x;
-    aScreenDragRect->y = sy - mImageOffset.y;
+    aScreenDragRect->x = screenPoint.x;
+    aScreenDragRect->y = screenPoint.y;
   }
 
   return NS_OK;
 }
 
 nsresult
-nsBaseDragService::DrawDragForImage(nsIImageLoadingContent* aImageLoader,
+nsBaseDragService::DrawDragForImage(nsPresContext* aPresContext,
+                                    nsIImageLoadingContent* aImageLoader,
                                     HTMLCanvasElement* aCanvas,
-                                    int32_t aScreenX, int32_t aScreenY,
-                                    nsIntRect* aScreenDragRect,
+                                    LayoutDeviceIntRect* aScreenDragRect,
                                     RefPtr<SourceSurface>* aSurface)
 {
   nsCOMPtr<imgIContainer> imgContainer;
   if (aImageLoader) {
     nsCOMPtr<imgIRequest> imgRequest;
     nsresult rv = aImageLoader->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST,
                                           getter_AddRefs(imgRequest));
     NS_ENSURE_SUCCESS(rv, rv);
@@ -700,29 +680,37 @@ nsBaseDragService::DrawDragForImage(nsII
       return NS_ERROR_NOT_AVAILABLE;
 
     rv = imgRequest->GetImage(getter_AddRefs(imgContainer));
     NS_ENSURE_SUCCESS(rv, rv);
     if (!imgContainer)
       return NS_ERROR_NOT_AVAILABLE;
 
     // use the size of the image as the size of the drag image
-    imgContainer->GetWidth(&aScreenDragRect->width);
-    imgContainer->GetHeight(&aScreenDragRect->height);
+    int32_t imageWidth, imageHeight;
+    rv = imgContainer->GetWidth(&imageWidth);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    rv = imgContainer->GetHeight(&imageHeight);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    aScreenDragRect->width = aPresContext->CSSPixelsToDevPixels(imageWidth);
+    aScreenDragRect->height = aPresContext->CSSPixelsToDevPixels(imageHeight);
   }
   else {
+    // XXX The canvas size should be converted to dev pixels.
     NS_ASSERTION(aCanvas, "both image and canvas are null");
     nsIntSize sz = aCanvas->GetSize();
     aScreenDragRect->width = sz.width;
     aScreenDragRect->height = sz.height;
   }
 
-  nsIntSize srcSize = aScreenDragRect->Size();
-  nsIntSize destSize = srcSize;
-
+  nsIntSize destSize;
+  destSize.width = aScreenDragRect->width;
+  destSize.height = aScreenDragRect->height;
   if (destSize.width == 0 || destSize.height == 0)
     return NS_ERROR_FAILURE;
 
   nsresult result = NS_OK;
   if (aImageLoader) {
     RefPtr<DrawTarget> dt =
       gfxPlatform::GetPlatform()->
         CreateOffscreenContentDrawTarget(destSize,
@@ -745,23 +733,23 @@ nsBaseDragService::DrawDragForImage(nsII
     *aSurface = dt->Snapshot();
   } else {
     *aSurface = aCanvas->GetSurfaceSnapshot();
   }
 
   return result;
 }
 
-void
+LayoutDeviceIntPoint
 nsBaseDragService::ConvertToUnscaledDevPixels(nsPresContext* aPresContext,
-                                              int32_t* aScreenX, int32_t* aScreenY)
+                                              CSSIntPoint aScreenPosition)
 {
   int32_t adj = aPresContext->DeviceContext()->AppUnitsPerDevPixelAtUnitFullZoom();
-  *aScreenX = nsPresContext::CSSPixelsToAppUnits(*aScreenX) / adj;
-  *aScreenY = nsPresContext::CSSPixelsToAppUnits(*aScreenY) / adj;
+  return LayoutDeviceIntPoint(nsPresContext::CSSPixelsToAppUnits(aScreenPosition.x) / adj,
+                              nsPresContext::CSSPixelsToAppUnits(aScreenPosition.y) / adj);
 }
 
 NS_IMETHODIMP
 nsBaseDragService::Suppress()
 {
   EndDragSession(false);
   ++mSuppressLevel;
   return NS_OK;
--- a/widget/nsBaseDragService.h
+++ b/widget/nsBaseDragService.h
@@ -82,53 +82,53 @@ protected:
   /**
    * Draw the drag image, if any, to a surface and return it. The drag image
    * is constructed from mImage if specified, or aDOMNode if mImage is null.
    *
    * aRegion may be used to draw only a subset of the element. This region
    * should be supplied using x and y coordinates measured in css pixels
    * that are relative to the upper-left corner of the window.
    *
-   * aScreenX and aScreenY should be the screen coordinates of the mouse click
-   * for the drag. These are in desktop pixels.
+   * aScreenPosition should be the screen coordinates of the mouse click
+   * for the drag. These are in CSS pixels.
    *
    * On return, aScreenDragRect will contain the screen coordinates of the
    * area being dragged. This is used by the platform-specific part of the
    * drag service to determine the drag feedback. This rect will be in the
    * device pixels of the presContext.
    *
    * If there is no drag image, the returned surface will be null, but
    * aScreenDragRect will still be set to the drag area.
    *
    * aPresContext will be set to the nsPresContext used determined from
    * whichever of mImage or aDOMNode is used.
    */
   nsresult DrawDrag(nsIDOMNode* aDOMNode,
                     nsIScriptableRegion* aRegion,
-                    int32_t aScreenX, int32_t aScreenY,
-                    nsIntRect* aScreenDragRect,
+                    mozilla::CSSIntPoint aScreenPosition,
+                    mozilla::LayoutDeviceIntRect* aScreenDragRect,
                     RefPtr<SourceSurface>* aSurface,
                     nsPresContext **aPresContext);
 
   /**
    * Draw a drag image for an image node specified by aImageLoader or aCanvas.
    * This is called by DrawDrag.
    */
-  nsresult DrawDragForImage(nsIImageLoadingContent* aImageLoader,
+  nsresult DrawDragForImage(nsPresContext *aPresContext,
+                            nsIImageLoadingContent* aImageLoader,
                             mozilla::dom::HTMLCanvasElement* aCanvas,
-                            int32_t aScreenX, int32_t aScreenY,
-                            nsIntRect* aScreenDragRect,
+                            mozilla::LayoutDeviceIntRect* aScreenDragRect,
                             RefPtr<SourceSurface>* aSurface);
 
   /**
-   * Convert aScreenX and aScreenY from CSS pixels into unscaled device pixels.
+   * Convert aScreenPosition from CSS pixels into unscaled device pixels.
    */
-  void
+  mozilla::LayoutDeviceIntPoint
   ConvertToUnscaledDevPixels(nsPresContext* aPresContext,
-                             int32_t* aScreenX, int32_t* aScreenY);
+                             mozilla::CSSIntPoint aScreenPosition);
 
   /**
    * If the drag image is a popup, open the popup when the drag begins.
    */
   void OpenDragPopup();
 
   // Returns true if a drag event was dispatched to a child process after
   // the previous TakeDragEventDispatchedToChildProcess() call.
@@ -168,20 +168,18 @@ protected:
   // set if a selection is being dragged
   nsCOMPtr<nsISelection> mSelection;
 
   // set if the image in mImage is a popup. If this case, the popup will be opened
   // and moved instead of using a drag image.
   nsCOMPtr<nsIContent> mDragPopup;
 
   // the screen position where drag gesture occurred, used for positioning the
-  // drag image when no image is specified. If a value is -1, no event was
-  // supplied so the screen position is not known
-  int32_t mScreenX;
-  int32_t mScreenY;
+  // drag image.
+  mozilla::CSSIntPoint mScreenPosition;
 
   // the screen position where the drag ended
   mozilla::LayoutDeviceIntPoint mEndDragPoint;
 
   uint32_t mSuppressLevel;
 
   // The input source of the drag event. Possible values are from nsIDOMMouseEvent.
   uint16_t mInputSource;
--- a/widget/nsDragServiceProxy.cpp
+++ b/widget/nsDragServiceProxy.cpp
@@ -38,48 +38,44 @@ nsDragServiceProxy::InvokeDragSessionImp
   NS_ENSURE_STATE(child);
   nsTArray<mozilla::dom::IPCDataTransfer> dataTransfers;
   nsContentUtils::TransferablesToIPCTransferables(aArrayTransferables,
                                                   dataTransfers,
                                                   false,
                                                   child->Manager(),
                                                   nullptr);
 
+  LayoutDeviceIntRect dragRect;
   if (mHasImage || mSelection) {
-    nsIntRect dragRect;
     nsPresContext* pc;
     RefPtr<mozilla::gfx::SourceSurface> surface;
-    DrawDrag(mSourceNode, aRegion, mScreenX, mScreenY,
-             &dragRect, &surface, &pc);
+    DrawDrag(mSourceNode, aRegion, mScreenPosition, &dragRect, &surface, &pc);
 
     if (surface) {
       RefPtr<mozilla::gfx::DataSourceSurface> dataSurface =
         surface->GetDataSurface();
       if (dataSurface) {
         size_t length;
         int32_t stride;
         Shmem surfaceData;
         nsContentUtils::GetSurfaceData(dataSurface, &length, &stride, child,
                                        &surfaceData);
         // Save the surface data to shared memory.
         if (!surfaceData.IsReadable() || !surfaceData.get<char>()) {
           NS_WARNING("Failed to create shared memory for drag session.");
           return NS_ERROR_FAILURE;
         }
 
-        mozilla::gfx::IntSize size = dataSurface->GetSize();
         mozilla::Unused <<
           child->SendInvokeDragSession(dataTransfers, aActionType, surfaceData,
-                                       size.width, size.height, stride,
-                                       static_cast<uint8_t>(dataSurface->GetFormat()),
-                                       dragRect.x, dragRect.y);
+                                       stride, static_cast<uint8_t>(dataSurface->GetFormat()),
+                                       dragRect);
         StartDragSession();
         return NS_OK;
       }
     }
   }
 
   mozilla::Unused << child->SendInvokeDragSession(dataTransfers, aActionType,
-                                                  mozilla::void_t(),
-                                                  0, 0, 0, 0, 0, 0);
+                                                  mozilla::void_t(), 0, 0, dragRect);
   StartDragSession();
   return NS_OK;
 }
--- a/widget/windows/nsDragService.cpp
+++ b/widget/windows/nsDragService.cpp
@@ -73,22 +73,20 @@ nsDragService::CreateDragImage(nsIDOMNod
   if (!psdi)
     return false;
 
   memset(psdi, 0, sizeof(SHDRAGIMAGE));
   if (!aDOMNode)
     return false;
 
   // Prepare the drag image
-  nsIntRect dragRect;
+  LayoutDeviceIntRect dragRect;
   RefPtr<SourceSurface> surface;
   nsPresContext* pc;
-  DrawDrag(aDOMNode, aRegion,
-           mScreenX, mScreenY,
-           &dragRect, &surface, &pc);
+  DrawDrag(aDOMNode, aRegion, mScreenPosition, &dragRect, &surface, &pc);
   if (!surface)
     return false;
 
   uint32_t bmWidth = dragRect.width, bmHeight = dragRect.height;
 
   if (bmWidth == 0 || bmHeight == 0)
     return false;
 
@@ -145,26 +143,20 @@ nsDragService::CreateDragImage(nsIDOMNod
       CopySurfaceDataToPackedArray(map.mData, static_cast<uint8_t*>(lpBits),
                                    dataSurface->GetSize(), map.mStride,
                                    BytesPerPixel(dataSurface->GetFormat()));
     }
 
     psdi->sizeDragImage.cx = bmWidth;
     psdi->sizeDragImage.cy = bmHeight;
 
-    // Mouse position in center
-    if (mScreenX == -1 || mScreenY == -1) {
-      psdi->ptOffset.x = (uint32_t)((float)bmWidth/2.0f);
-      psdi->ptOffset.y = (uint32_t)((float)bmHeight/2.0f);
-    } else {
-      int32_t sx = mScreenX, sy = mScreenY;
-      ConvertToUnscaledDevPixels(pc, &sx, &sy);
-      psdi->ptOffset.x = sx - dragRect.x;
-      psdi->ptOffset.y = sy - dragRect.y;
-    }
+    LayoutDeviceIntPoint screenPoint =
+      ConvertToUnscaledDevPixels(pc, mScreenPosition);
+    psdi->ptOffset.x = screenPoint.x - dragRect.x;
+    psdi->ptOffset.y = screenPoint.y - dragRect.y;
 
     DeleteDC(hdcSrc);
   }
 
   dataSurface->Unmap();
 
   return psdi->hbmpDragImage != nullptr;
 }