Merge latest green fx-team changeset and mozilla-central; a=merge
authorEd Morley <emorley@mozilla.com>
Thu, 12 Jun 2014 15:07:14 +0100
changeset 188336 4f98802de6ce5396af692fcaab82955a5b32a932
parent 188335 3bd2b5b8e46d3ac6126ea217b5e268a7b6ca3616 (current diff)
parent 188311 1d9513f22934f62fc9228b7e027feaa59695a0f6 (diff)
child 188391 c482c28b35b625f0a93e62871c8d4b77cb31230b
push id26951
push useremorley@mozilla.com
push dateThu, 12 Jun 2014 14:07:43 +0000
treeherdermozilla-central@4f98802de6ce [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone33.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge latest green fx-team changeset and mozilla-central; a=merge
dom/filehandle/FileHandle.cpp
dom/filehandle/FileHandle.h
dom/filehandle/LockedFile.cpp
dom/filehandle/LockedFile.h
dom/filehandle/test/test_lockedfile_lifetimes.html
dom/filehandle/test/test_lockedfile_lifetimes_nested.html
dom/filehandle/test/test_lockedfile_ordering.html
dom/filehandle/test/test_overlapping_lockedfiles.html
dom/filehandle/test/test_readonly_lockedfiles.html
dom/indexedDB/IDBFileHandle.cpp
dom/indexedDB/IDBFileHandle.h
dom/webidl/FileHandle.webidl
dom/webidl/IDBFileHandle.webidl
dom/webidl/LockedFile.webidl
--- a/content/base/src/nsContentList.cpp
+++ b/content/base/src/nsContentList.cpp
@@ -513,17 +513,17 @@ nsContentList::Item(uint32_t aIndex, boo
     nsIDocument* doc = mRootNode->GetCurrentDoc();
     if (doc) {
       // Flush pending content changes Bug 4891.
       doc->FlushPendingNotifications(Flush_ContentAndNotify);
     }
   }
 
   if (mState != LIST_UP_TO_DATE)
-    PopulateSelf(std::min(aIndex, UINT32_MAX - 1) + 1);
+    PopulateSelf(std::min(aIndex, uint32_t(UINT32_MAX - 1)) + 1);
 
   ASSERT_IN_SYNC;
   NS_ASSERTION(!mRootNode || mState != LIST_DIRTY,
                "PopulateSelf left the list in a dirty (useless) state!");
 
   return mElements.SafeElementAt(aIndex);
 }
 
--- a/content/html/content/src/HTMLMediaElement.cpp
+++ b/content/html/content/src/HTMLMediaElement.cpp
@@ -1275,17 +1275,17 @@ NS_IMETHODIMP HTMLMediaElement::GetSeeki
 
 /* attribute double currentTime; */
 double
 HTMLMediaElement::CurrentTime() const
 {
   if (mSrcStream) {
     MediaStream* stream = GetSrcMediaStream();
     if (stream) {
-      return MediaTimeToSeconds(stream->GetCurrentTime());
+      return stream->StreamTimeToSeconds(stream->GetCurrentTime());
     }
   }
 
   if (mDecoder) {
     return mDecoder->GetCurrentTime();
   }
 
   return 0.0;
--- a/content/media/AudioNodeStream.cpp
+++ b/content/media/AudioNodeStream.cpp
@@ -551,23 +551,25 @@ AudioNodeStream::FinishOutput()
 
 double
 AudioNodeStream::TimeFromDestinationTime(AudioNodeStream* aDestination,
                                          double aSeconds)
 {
   MOZ_ASSERT(aDestination->SampleRate() == SampleRate());
 
   double destinationSeconds = std::max(0.0, aSeconds);
-  StreamTime streamTime = SecondsToMediaTime(destinationSeconds);
+  StreamTime streamTime =
+    aDestination->SecondsToStreamTimeRoundDown(destinationSeconds);
   // MediaTime does not have the resolution of double
-  double offset = destinationSeconds - MediaTimeToSeconds(streamTime);
+  double offset =
+    destinationSeconds - aDestination->StreamTimeToSeconds(streamTime);
 
   GraphTime graphTime = aDestination->StreamTimeToGraphTime(streamTime);
   StreamTime thisStreamTime = GraphTimeToStreamTimeOptimistic(graphTime);
-  double thisSeconds = MediaTimeToSeconds(thisStreamTime) + offset;
+  double thisSeconds = StreamTimeToSeconds(thisStreamTime) + offset;
   MOZ_ASSERT(thisSeconds >= 0.0);
   return thisSeconds;
 }
 
 TrackTicks
 AudioNodeStream::TicksFromDestinationTime(MediaStream* aDestination,
                                           double aSeconds)
 {
@@ -583,12 +585,12 @@ AudioNodeStream::TicksFromDestinationTim
 double
 AudioNodeStream::DestinationTimeFromTicks(AudioNodeStream* aDestination,
                                           TrackTicks aPosition)
 {
   MOZ_ASSERT(SampleRate() == aDestination->SampleRate());
   StreamTime sourceTime = TicksToTimeRoundDown(SampleRate(), aPosition);
   GraphTime graphTime = StreamTimeToGraphTime(sourceTime);
   StreamTime destinationTime = aDestination->GraphTimeToStreamTimeOptimistic(graphTime);
-  return MediaTimeToSeconds(destinationTime);
+  return StreamTimeToSeconds(destinationTime);
 }
 
 }
--- a/content/media/DOMMediaStream.cpp
+++ b/content/media/DOMMediaStream.cpp
@@ -159,17 +159,18 @@ DOMMediaStream::WrapObject(JSContext* aC
 }
 
 double
 DOMMediaStream::CurrentTime()
 {
   if (!mStream) {
     return 0.0;
   }
-  return MediaTimeToSeconds(mStream->GetCurrentTime() - mLogicalStreamStartTime);
+  return mStream->
+    StreamTimeToSeconds(mStream->GetCurrentTime() - mLogicalStreamStartTime);
 }
 
 void
 DOMMediaStream::GetAudioTracks(nsTArray<nsRefPtr<AudioStreamTrack> >& aTracks)
 {
   for (uint32_t i = 0; i < mTracks.Length(); ++i) {
     AudioStreamTrack* t = mTracks[i]->AsAudioStreamTrack();
     if (t) {
--- a/content/media/MediaDecoder.cpp
+++ b/content/media/MediaDecoder.cpp
@@ -217,28 +217,30 @@ MediaDecoder::DecodedStreamData::~Decode
   mStream->Destroy();
 }
 
 MediaDecoder::DecodedStreamGraphListener::DecodedStreamGraphListener(MediaStream* aStream,
                                                                      DecodedStreamData* aData)
   : mData(aData),
     mMutex("MediaDecoder::DecodedStreamData::mMutex"),
     mStream(aStream),
-    mLastOutputTime(aStream->GetCurrentTime()),
+    mLastOutputTime(aStream->
+                    StreamTimeToMicroseconds(aStream->GetCurrentTime())),
     mStreamFinishedOnMainThread(false)
 {
 }
 
 void
 MediaDecoder::DecodedStreamGraphListener::NotifyOutput(MediaStreamGraph* aGraph,
                                                        GraphTime aCurrentTime)
 {
   MutexAutoLock lock(mMutex);
   if (mStream) {
-    mLastOutputTime = mStream->GraphTimeToStreamTime(aCurrentTime);
+    mLastOutputTime = mStream->
+      StreamTimeToMicroseconds(mStream->GraphTimeToStreamTime(aCurrentTime));
   }
 }
 
 void
 MediaDecoder::DecodedStreamGraphListener::DoNotifyFinished()
 {
   if (mData && mData->mDecoder) {
     if (mData->mDecoder->GetState() == PLAY_STATE_PLAYING) {
--- a/content/media/MediaDecoder.h
+++ b/content/media/MediaDecoder.h
@@ -390,17 +390,18 @@ public:
 
   struct DecodedStreamData {
     typedef gfx::IntSize IntSize;
 
     DecodedStreamData(MediaDecoder* aDecoder,
                       int64_t aInitialTime, SourceMediaStream* aStream);
     ~DecodedStreamData();
 
-    StreamTime GetLastOutputTime() { return mListener->GetLastOutputTime(); }
+    // microseconds
+    int64_t GetLastOutputTime() { return mListener->GetLastOutputTime(); }
     bool IsFinished() { return mListener->IsFinishedOnMainThread(); }
 
     // The following group of fields are protected by the decoder's monitor
     // and can be read or written on any thread.
     int64_t mLastAudioPacketTime; // microseconds
     int64_t mLastAudioPacketEndTime; // microseconds
     // Count of audio frames written to the stream
     int64_t mAudioFramesWritten;
@@ -439,17 +440,17 @@ public:
   class DecodedStreamGraphListener : public MediaStreamListener {
   public:
     DecodedStreamGraphListener(MediaStream* aStream, DecodedStreamData* aData);
     virtual void NotifyOutput(MediaStreamGraph* aGraph, GraphTime aCurrentTime) MOZ_OVERRIDE;
     virtual void NotifyFinished(MediaStreamGraph* aGraph) MOZ_OVERRIDE;
 
     void DoNotifyFinished();
 
-    StreamTime GetLastOutputTime()
+    int64_t GetLastOutputTime() // microseconds
     {
       MutexAutoLock lock(mMutex);
       return mLastOutputTime;
     }
     void Forget()
     {
       NS_ASSERTION(NS_IsMainThread(), "Main thread only");
       mData = nullptr;
@@ -472,17 +473,17 @@ public:
   private:
     // Main thread only
     DecodedStreamData* mData;
 
     Mutex mMutex;
     // Protected by mMutex
     nsRefPtr<MediaStream> mStream;
     // Protected by mMutex
-    StreamTime mLastOutputTime;
+    int64_t mLastOutputTime; // microseconds
     // Protected by mMutex
     bool mStreamFinishedOnMainThread;
   };
 
   struct OutputStreamData {
     void Init(ProcessedMediaStream* aStream, bool aFinishWhenEnded)
     {
       mStream = aStream;
--- a/content/media/MediaDecoderStateMachine.cpp
+++ b/content/media/MediaDecoderStateMachine.cpp
@@ -421,17 +421,18 @@ void MediaDecoderStateMachine::SendStrea
         mediaStream->AppendToTrack(TRACK_AUDIO, &output);
       }
       if (AudioQueue().IsFinished() && !stream->mHaveSentFinishAudio) {
         mediaStream->EndTrack(TRACK_AUDIO);
         stream->mHaveSentFinishAudio = true;
       }
       minLastAudioPacketTime = std::min(minLastAudioPacketTime, stream->mLastAudioPacketTime);
       endPosition = std::max(endPosition,
-          TicksToTimeRoundDown(mInfo.mAudio.mRate, stream->mAudioFramesWritten));
+          mediaStream->TicksToTimeRoundDown(mInfo.mAudio.mRate,
+                                            stream->mAudioFramesWritten));
     }
 
     if (mInfo.HasVideo()) {
       nsAutoTArray<VideoData*,10> video;
       // It's OK to hold references to the VideoData only the decoder thread
       // pops from the queue.
       VideoQueue().GetElementsAfter(stream->mNextVideoTime, &video);
       VideoSegment output;
@@ -464,17 +465,17 @@ void MediaDecoderStateMachine::SendStrea
       if (output.GetDuration() > 0) {
         mediaStream->AppendToTrack(TRACK_VIDEO, &output);
       }
       if (VideoQueue().IsFinished() && !stream->mHaveSentFinishVideo) {
         mediaStream->EndTrack(TRACK_VIDEO);
         stream->mHaveSentFinishVideo = true;
       }
       endPosition = std::max(endPosition,
-          TicksToTimeRoundDown(RATE_VIDEO, stream->mNextVideoTime - stream->mInitialTime));
+          mediaStream->TicksToTimeRoundDown(RATE_VIDEO, stream->mNextVideoTime - stream->mInitialTime));
     }
 
     if (!stream->mHaveSentFinish) {
       stream->mStream->AdvanceKnownTracksTime(endPosition);
     }
 
     if (finished && !stream->mHaveSentFinish) {
       stream->mHaveSentFinish = true;
@@ -1406,18 +1407,18 @@ void MediaDecoderStateMachine::SetSyncPo
   mSyncPointInDecodedStream = mStartTime + mPlayDuration;
 }
 
 int64_t MediaDecoderStateMachine::GetCurrentTimeViaMediaStreamSync()
 {
   AssertCurrentThreadInMonitor();
   NS_ASSERTION(mSyncPointInDecodedStream >= 0, "Should have set up sync point");
   DecodedStreamData* stream = mDecoder->GetDecodedStream();
-  StreamTime streamDelta = stream->GetLastOutputTime() - mSyncPointInMediaStream;
-  return mSyncPointInDecodedStream + MediaTimeToMicroseconds(streamDelta);
+  int64_t streamDelta = stream->GetLastOutputTime() - mSyncPointInMediaStream;
+  return mSyncPointInDecodedStream + streamDelta;
 }
 
 void MediaDecoderStateMachine::StartPlayback()
 {
   DECODER_LOG(PR_LOG_DEBUG, "StartPlayback()");
 
   NS_ASSERTION(!IsPlaying(), "Shouldn't be playing when StartPlayback() is called");
   AssertCurrentThreadInMonitor();
--- a/content/media/MediaDecoderStateMachine.h
+++ b/content/media/MediaDecoderStateMachine.h
@@ -727,17 +727,17 @@ protected:
   // The time that playback started from the system clock. This is used for
   // timing the presentation of video frames when there's no audio.
   // Accessed only via the state machine thread.
   TimeStamp mPlayStartTime;
 
   // When we start writing decoded data to a new DecodedDataStream, or we
   // restart writing due to PlaybackStarted(), we record where we are in the
   // MediaStream and what that corresponds to in the media.
-  StreamTime mSyncPointInMediaStream;
+  int64_t mSyncPointInMediaStream; // microseconds
   int64_t mSyncPointInDecodedStream; // microseconds
 
   // When the playbackRate changes, and there is no audio clock, it is necessary
   // to reset the mPlayStartTime. This is done next time the clock is queried,
   // when this member is true. Access protected by decoder monitor.
   bool mResetPlayStartTime;
 
   // The amount of time we've spent playing already the media. The current
--- a/content/media/MediaSegment.h
+++ b/content/media/MediaSegment.h
@@ -11,62 +11,39 @@
 #include "mozilla/TimeStamp.h"
 #endif
 #include <algorithm>
 #include "Latency.h"
 
 namespace mozilla {
 
 /**
- * Track rate in Hz. Maximum 1 << TRACK_RATE_MAX_BITS Hz. This
+ * Track or graph rate in Hz. Maximum 1 << TRACK_RATE_MAX_BITS Hz. This
  * maximum avoids overflow in conversions between track rates and conversions
  * from seconds.
  */
 typedef int32_t TrackRate;
 const int64_t TRACK_RATE_MAX_BITS = 20;
 const TrackRate TRACK_RATE_MAX = 1 << TRACK_RATE_MAX_BITS;
 
 /**
- * We represent media times in 64-bit fixed point. So 1 MediaTime is
- * 1/(2^MEDIA_TIME_FRAC_BITS) seconds.  We want to make sure that multiplying
- * MediaTime by a TrackRate doesn't overflow, so we set its max accordingly.
+ * A number of ticks at a rate determined by some underlying track (e.g.
+ * audio sample rate). We want to make sure that multiplying TrackTicks by
+ * a TrackRate doesn't overflow, so we set its max accordingly.
+ */
+typedef int64_t TrackTicks;
+const int64_t TRACK_TICKS_MAX = INT64_MAX >> TRACK_RATE_MAX_BITS;
+
+/**
+ * We represent media times in 64-bit audio frame counts or ticks.
+ * All audio tracks in a MediaStreamGraph have the same sample rate and all
+ * streams in the graph measure time using ticks at the same audio rate.
  */
 typedef int64_t MediaTime;
-const int64_t MEDIA_TIME_FRAC_BITS = 20;
-const int64_t MEDIA_TIME_MAX = INT64_MAX >> TRACK_RATE_MAX_BITS;
-
-inline MediaTime MillisecondsToMediaTime(int32_t aMS)
-{
-  return (MediaTime(aMS) << MEDIA_TIME_FRAC_BITS)/1000;
-}
-
-inline MediaTime SecondsToMediaTime(double aS)
-{
-  NS_ASSERTION(aS <= (MEDIA_TIME_MAX >> MEDIA_TIME_FRAC_BITS),
-               "Out of range");
-  return MediaTime(aS * (1 << MEDIA_TIME_FRAC_BITS));
-}
-
-inline double MediaTimeToSeconds(MediaTime aTime)
-{
-  return aTime*(1.0/(1 << MEDIA_TIME_FRAC_BITS));
-}
-
-inline int64_t MediaTimeToMicroseconds(MediaTime aTime)
-{
-  return aTime*(1000000.0/(1 << MEDIA_TIME_FRAC_BITS));
-}
-
-/**
- * A number of ticks at a rate determined by some underlying track (e.g.
- * audio sample rate). We want to make sure that multiplying TrackTicks by
- * 2^MEDIA_TIME_FRAC_BITS doesn't overflow, so we set its max accordingly.
- */
-typedef int64_t TrackTicks;
-const int64_t TRACK_TICKS_MAX = INT64_MAX >> MEDIA_TIME_FRAC_BITS;
+const int64_t MEDIA_TIME_MAX = TRACK_TICKS_MAX;
 
 /**
  * A MediaSegment is a chunk of media data sequential in time. Different
  * types of data have different subclasses of MediaSegment, all inheriting
  * from MediaSegmentBase.
  * All MediaSegment data is timed using TrackTicks. The actual tick rate
  * is defined on a per-track basis. For some track types, this can be
  * a fixed constant for all tracks of that type (e.g. 1MHz for video).
--- a/content/media/MediaStreamGraph.cpp
+++ b/content/media/MediaStreamGraph.cpp
@@ -43,22 +43,16 @@ namespace mozilla {
 #ifdef PR_LOGGING
 PRLogModuleInfo* gMediaStreamGraphLog;
 #define STREAM_LOG(type, msg) PR_LOG(gMediaStreamGraphLog, type, msg)
 #else
 #define STREAM_LOG(type, msg)
 #endif
 
 /**
- * We make the initial mCurrentTime nonzero so that zero times can have
- * special meaning if necessary.
- */
-static const int32_t INITIAL_CURRENT_TIME = 1;
-
-/**
  * The singleton graph instance.
  */
 static MediaStreamGraphImpl* gGraph;
 
 MediaStreamGraphImpl::~MediaStreamGraphImpl()
 {
   NS_ASSERTION(IsEmpty(),
                "All streams should have been destroyed by messages from the main thread");
@@ -344,28 +338,27 @@ MediaStreamGraphImpl::GetAudioPosition(M
   if (aStream->mAudioOutputStreams.IsEmpty()) {
     return mCurrentTime;
   }
   int64_t positionInFrames = aStream->mAudioOutputStreams[0].mStream->GetPositionInFrames();
   if (positionInFrames < 0) {
     return mCurrentTime;
   }
   return aStream->mAudioOutputStreams[0].mAudioPlaybackStartTime +
-      TicksToTimeRoundDown(mSampleRate,
-                           positionInFrames);
+    RateConvertTicksRoundDown(GraphRate(), mSampleRate, positionInFrames);
 }
 
 void
 MediaStreamGraphImpl::UpdateCurrentTime()
 {
   GraphTime prevCurrentTime, nextCurrentTime;
   if (mRealtime) {
     TimeStamp now = TimeStamp::Now();
     prevCurrentTime = mCurrentTime;
-    nextCurrentTime = INITIAL_CURRENT_TIME +
+    nextCurrentTime =
       SecondsToMediaTime((now - mInitialTimeStamp).ToSeconds());
 
     mCurrentTimeStamp = now;
     STREAM_LOG(PR_LOG_DEBUG+1, ("Updating current time to %f (real %f, mStateComputedTime %f)",
                MediaTimeToSeconds(nextCurrentTime),
                (now - mInitialTimeStamp).ToSeconds(),
                MediaTimeToSeconds(mStateComputedTime)));
   } else {
@@ -911,27 +904,24 @@ MediaStreamGraphImpl::PlayAudio(MediaStr
     MediaStream::AudioOutputStream& audioOutput = aStream->mAudioOutputStreams[i];
     StreamBuffer::Track* track = aStream->mBuffer.FindTrack(audioOutput.mTrackID);
     AudioSegment* audio = track->Get<AudioSegment>();
     AudioSegment output;
     MOZ_ASSERT(track->GetRate() == mSampleRate);
 
     // offset and audioOutput.mLastTickWritten can differ by at most one sample,
     // because of the rounding issue. We track that to ensure we don't skip a
-    // sample, or play a sample twice.
+    // sample. One sample may be played twice, but this should not happen
+    // again during an unblocked sequence of track samples.
     TrackTicks offset = track->TimeToTicksRoundDown(GraphTimeToStreamTime(aStream, aFrom));
-    if (!audioOutput.mLastTickWritten) {
-        audioOutput.mLastTickWritten = offset;
-    }
-    if (audioOutput.mLastTickWritten != offset) {
+    if (audioOutput.mLastTickWritten &&
+        audioOutput.mLastTickWritten != offset) {
       // If there is a global underrun of the MSG, this property won't hold, and
       // we reset the sample count tracking.
-      if (mozilla::Abs(audioOutput.mLastTickWritten - offset) != 1) {
-        audioOutput.mLastTickWritten = offset;
-      } else {
+      if (offset - audioOutput.mLastTickWritten == 1) {
         offset = audioOutput.mLastTickWritten;
       }
     }
 
     // We don't update aStream->mBufferStartTime here to account for
     // time spent blocked. Instead, we'll update it in UpdateCurrentTime after the
     // blocked period has completed. But we do need to make sure we play from the
     // right offsets in the stream buffer, even if we've already written silence for
@@ -945,50 +935,50 @@ MediaStreamGraphImpl::PlayAudio(MediaStr
       // Check how many ticks of sound we can provide if we are blocked some
       // time in the middle of this cycle.
       TrackTicks toWrite = 0;
       if (end >= aTo) {
         toWrite = ticksNeeded;
       } else {
         toWrite = TimeToTicksRoundDown(mSampleRate, end - aFrom);
       }
+      ticksNeeded -= toWrite;
 
       if (blocked) {
         output.InsertNullDataAtStart(toWrite);
         STREAM_LOG(PR_LOG_DEBUG+1, ("MediaStream %p writing %ld blocking-silence samples for %f to %f (%ld to %ld)\n",
                                     aStream, toWrite, MediaTimeToSeconds(t), MediaTimeToSeconds(end),
                                     offset, offset + toWrite));
-        ticksNeeded -= toWrite;
       } else {
         TrackTicks endTicksNeeded = offset + toWrite;
         TrackTicks endTicksAvailable = audio->GetDuration();
+        STREAM_LOG(PR_LOG_DEBUG+1, ("MediaStream %p writing %ld samples for %f to %f (samples %ld to %ld)\n",
+                                     aStream, toWrite, MediaTimeToSeconds(t), MediaTimeToSeconds(end),
+                                     offset, endTicksNeeded));
+
         if (endTicksNeeded <= endTicksAvailable) {
           output.AppendSlice(*audio, offset, endTicksNeeded);
+          offset = endTicksNeeded;
         } else {
           MOZ_ASSERT(track->IsEnded(), "Not enough data, and track not ended.");
           // If we are at the end of the track, maybe write the remaining
           // samples, and pad with/output silence.
           if (endTicksNeeded > endTicksAvailable &&
               offset < endTicksAvailable) {
             output.AppendSlice(*audio, offset, endTicksAvailable);
-            ticksNeeded -= endTicksAvailable - offset;
             toWrite -= endTicksAvailable - offset;
+            offset = endTicksAvailable;
           }
           output.AppendNullData(toWrite);
         }
         output.ApplyVolume(volume);
-        STREAM_LOG(PR_LOG_DEBUG+1, ("MediaStream %p writing %ld samples for %f to %f (samples %ld to %ld)\n",
-                                     aStream, toWrite, MediaTimeToSeconds(t), MediaTimeToSeconds(end),
-                                     offset, endTicksNeeded));
-        ticksNeeded -= toWrite;
       }
       t = end;
-      offset += toWrite;
-      audioOutput.mLastTickWritten += toWrite;
     }
+    audioOutput.mLastTickWritten = offset;
 
     // Need unique id for stream & track - and we want it to match the inserter
     output.WriteTo(LATENCY_STREAM_ID(aStream, track->GetID()),
                    audioOutput.mStream, mMixer);
   }
   return ticksWritten;
 }
 
@@ -1155,31 +1145,21 @@ MediaStreamGraphImpl::EnsureNextIteratio
  * Returns smallest value of t such that
  * TimeToTicksRoundUp(aSampleRate, t) is a multiple of WEBAUDIO_BLOCK_SIZE
  * and floor(TimeToTicksRoundUp(aSampleRate, t)/WEBAUDIO_BLOCK_SIZE) >
  * floor(TimeToTicksRoundUp(aSampleRate, aTime)/WEBAUDIO_BLOCK_SIZE).
  */
 static GraphTime
 RoundUpToNextAudioBlock(TrackRate aSampleRate, GraphTime aTime)
 {
-  TrackTicks ticks = TimeToTicksRoundUp(aSampleRate, aTime);
+  TrackTicks ticks = aTime;
   uint64_t block = ticks >> WEBAUDIO_BLOCK_SIZE_BITS;
   uint64_t nextBlock = block + 1;
   TrackTicks nextTicks = nextBlock << WEBAUDIO_BLOCK_SIZE_BITS;
-  // Find the smallest time t such that TimeToTicksRoundUp(aSampleRate,t) == nextTicks
-  // That's the smallest integer t such that
-  //   t*aSampleRate > ((nextTicks - 1) << MEDIA_TIME_FRAC_BITS)
-  // Both sides are integers, so this is equivalent to
-  //   t*aSampleRate >= ((nextTicks - 1) << MEDIA_TIME_FRAC_BITS) + 1
-  //   t >= (((nextTicks - 1) << MEDIA_TIME_FRAC_BITS) + 1)/aSampleRate
-  //   t = ceil((((nextTicks - 1) << MEDIA_TIME_FRAC_BITS) + 1)/aSampleRate)
-  // Using integer division, that's
-  //   t = (((nextTicks - 1) << MEDIA_TIME_FRAC_BITS) + 1 + aSampleRate - 1)/aSampleRate
-  //     = ((nextTicks - 1) << MEDIA_TIME_FRAC_BITS)/aSampleRate + 1
-  return ((nextTicks - 1) << MEDIA_TIME_FRAC_BITS)/aSampleRate + 1;
+  return nextTicks;
 }
 
 void
 MediaStreamGraphImpl::ProduceDataForStreamsBlockByBlock(uint32_t aStreamIndex,
                                                         TrackRate aSampleRate,
                                                         GraphTime aFrom,
                                                         GraphTime aTo)
 {
@@ -1868,16 +1848,17 @@ MediaStream::Graph()
   return mGraph;
 }
 
 void
 MediaStream::SetGraphImpl(MediaStreamGraphImpl* aGraph)
 {
   MOZ_ASSERT(!mGraph, "Should only be called once");
   mGraph = aGraph;
+  mBuffer.InitGraphRate(aGraph->GraphRate());
 }
 
 void
 MediaStream::SetGraphImpl(MediaStreamGraph* aGraph)
 {
   MediaStreamGraphImpl* graph = static_cast<MediaStreamGraphImpl*>(aGraph);
   SetGraphImpl(graph);
 }
@@ -2642,18 +2623,18 @@ ProcessedMediaStream::DestroyImpl()
   for (int32_t i = mInputs.Length() - 1; i >= 0; --i) {
     mInputs[i]->Disconnect();
   }
   MediaStream::DestroyImpl();
   GraphImpl()->SetStreamOrderDirty();
 }
 
 MediaStreamGraphImpl::MediaStreamGraphImpl(bool aRealtime, TrackRate aSampleRate)
-  : mCurrentTime(INITIAL_CURRENT_TIME)
-  , mStateComputedTime(INITIAL_CURRENT_TIME)
+  : mCurrentTime(0)
+  , mStateComputedTime(0)
   , mProcessingGraphUpdateIndex(0)
   , mPortCount(0)
   , mMonitor("MediaStreamGraphImpl")
   , mLifecycleState(LIFECYCLE_THREAD_NOT_STARTED)
   , mWaitState(WAITSTATE_RUNNING)
   , mEndTime(GRAPH_TIME_MAX)
   , mSampleRate(aSampleRate)
   , mNeedAnotherIteration(false)
@@ -2899,17 +2880,18 @@ MediaStreamGraph::StartNonRealtimeProces
 {
   NS_ASSERTION(NS_IsMainThread(), "main thread only");
 
   MediaStreamGraphImpl* graph = static_cast<MediaStreamGraphImpl*>(this);
   NS_ASSERTION(!graph->mRealtime, "non-realtime only");
 
   if (graph->mNonRealtimeProcessing)
     return;
-  graph->mEndTime = graph->mCurrentTime + TicksToTimeRoundUp(aRate, aTicksToProcess);
+  graph->mEndTime = graph->mCurrentTime +
+    RateConvertTicksRoundUp(graph->GraphRate(), aRate, aTicksToProcess);
   graph->mNonRealtimeProcessing = true;
   graph->EnsureRunInStableState();
 }
 
 void
 ProcessedMediaStream::AddInput(MediaInputPort* aPort)
 {
   mInputs.AppendElement(aPort);
--- a/content/media/MediaStreamGraph.h
+++ b/content/media/MediaStreamGraph.h
@@ -474,16 +474,41 @@ public:
     mConsumers.RemoveElement(aPort);
   }
   uint32_t ConsumerCount()
   {
     return mConsumers.Length();
   }
   const StreamBuffer& GetStreamBuffer() { return mBuffer; }
   GraphTime GetStreamBufferStartTime() { return mBufferStartTime; }
+
+  double StreamTimeToSeconds(StreamTime aTime)
+  {
+    return TrackTicksToSeconds(mBuffer.GraphRate(), aTime);
+  }
+  int64_t StreamTimeToMicroseconds(StreamTime aTime)
+  {
+    return TimeToTicksRoundDown(1000000, aTime);
+  }
+  StreamTime SecondsToStreamTimeRoundDown(double aS)
+  {
+    return SecondsToTicksRoundDown(mBuffer.GraphRate(), aS);
+  }
+  TrackTicks TimeToTicksRoundUp(TrackRate aRate, StreamTime aTime)
+  {
+    return RateConvertTicksRoundUp(aRate, mBuffer.GraphRate(), aTime);
+  }
+  TrackTicks TimeToTicksRoundDown(TrackRate aRate, StreamTime aTime)
+  {
+    return RateConvertTicksRoundDown(aRate, mBuffer.GraphRate(), aTime);
+  }
+  StreamTime TicksToTimeRoundDown(TrackRate aRate, TrackTicks aTicks)
+  {
+    return RateConvertTicksRoundDown(mBuffer.GraphRate(), aRate, aTicks);
+  }
   /**
    * Convert graph time to stream time. aTime must be <= mStateComputedTime
    * to ensure we know exactly how much time this stream will be blocked during
    * the interval.
    */
   StreamTime GraphTimeToStreamTime(GraphTime aTime);
   /**
    * Convert graph time to stream time. aTime can be > mStateComputedTime,
--- a/content/media/MediaStreamGraphImpl.h
+++ b/content/media/MediaStreamGraphImpl.h
@@ -387,17 +387,36 @@ public:
    * Pause all AudioStreams being written to by MediaStreams
    */
   void PauseAllAudioOutputs();
   /**
    * Resume all AudioStreams being written to by MediaStreams
    */
   void ResumeAllAudioOutputs();
 
-  TrackRate AudioSampleRate() { return mSampleRate; }
+  TrackRate AudioSampleRate() const { return mSampleRate; }
+  TrackRate GraphRate() const { return mSampleRate; }
+
+  double MediaTimeToSeconds(GraphTime aTime)
+  {
+    return TrackTicksToSeconds(GraphRate(), aTime);
+  }
+  GraphTime SecondsToMediaTime(double aS)
+  {
+    return SecondsToTicksRoundDown(GraphRate(), aS);
+  }
+  GraphTime MillisecondsToMediaTime(int32_t aMS)
+  {
+    return RateConvertTicksRoundDown(GraphRate(), 1000, aMS);
+  }
+
+  TrackTicks TimeToTicksRoundDown(TrackRate aRate, StreamTime aTime)
+  {
+    return RateConvertTicksRoundDown(aRate, GraphRate(), aTime);
+  }
 
   // Data members
 
   /**
    * Media graph thread.
    * Readonly after initialization on the main thread.
    */
   nsCOMPtr<nsIThread> mThread;
--- a/content/media/StreamBuffer.cpp
+++ b/content/media/StreamBuffer.cpp
@@ -76,29 +76,29 @@ StreamBuffer::FindTrack(TrackID aID)
     }
   }
   return nullptr;
 }
 
 void
 StreamBuffer::ForgetUpTo(StreamTime aTime)
 {
-  // Round to nearest 50ms so we don't spend too much time pruning segments.
-  const MediaTime roundTo = MillisecondsToMediaTime(50);
-  StreamTime forget = (aTime/roundTo)*roundTo;
-  if (forget <= mForgottenTime) {
+  // Only prune if there is a reasonable chunk (50ms @ 48kHz) to forget, so we
+  // don't spend too much time pruning segments.
+  const StreamTime minChunkSize = 2400;
+  if (aTime < mForgottenTime + minChunkSize) {
     return;
   }
-  mForgottenTime = forget;
+  mForgottenTime = aTime;
 
   for (uint32_t i = 0; i < mTracks.Length(); ++i) {
     Track* track = mTracks[i];
-    if (track->IsEnded() && track->GetEndTimeRoundDown() <= forget) {
+    if (track->IsEnded() && track->GetEndTimeRoundDown() <= aTime) {
       mTracks.RemoveElementAt(i);
       --i;
       continue;
     }
-    TrackTicks forgetTo = std::min(track->GetEnd() - 1, track->TimeToTicksRoundDown(forget));
+    TrackTicks forgetTo = std::min(track->GetEnd() - 1, track->TimeToTicksRoundDown(aTime));
     track->ForgetUpTo(forgetTo);
   }
 }
 
 }
--- a/content/media/StreamBuffer.h
+++ b/content/media/StreamBuffer.h
@@ -20,49 +20,46 @@ const StreamTime STREAM_TIME_MAX = MEDIA
 /**
  * Unique ID for track within a StreamBuffer. Tracks from different
  * StreamBuffers may have the same ID; this matters when appending StreamBuffers,
  * since tracks with the same ID are matched. Only IDs greater than 0 are allowed.
  */
 typedef int32_t TrackID;
 const TrackID TRACK_NONE = 0;
 
-inline TrackTicks TimeToTicksRoundUp(TrackRate aRate, StreamTime aTime)
+inline TrackTicks RateConvertTicksRoundDown(TrackRate aOutRate,
+                                            TrackRate aInRate,
+                                            TrackTicks aTicks)
 {
-  NS_ASSERTION(0 < aRate && aRate <= TRACK_RATE_MAX, "Bad rate");
-  NS_ASSERTION(0 <= aTime && aTime <= STREAM_TIME_MAX, "Bad time");
-  return (aTime*aRate + (1 << MEDIA_TIME_FRAC_BITS) - 1) >> MEDIA_TIME_FRAC_BITS;
+  NS_ASSERTION(0 < aOutRate && aOutRate <= TRACK_RATE_MAX, "Bad out rate");
+  NS_ASSERTION(0 < aInRate && aInRate <= TRACK_RATE_MAX, "Bad in rate");
+  NS_ASSERTION(0 <= aTicks && aTicks <= TRACK_TICKS_MAX, "Bad ticks");
+  return (aTicks * aOutRate) / aInRate;
+}
+inline TrackTicks RateConvertTicksRoundUp(TrackRate aOutRate,
+                                          TrackRate aInRate, TrackTicks aTicks)
+{
+  NS_ASSERTION(0 < aOutRate && aOutRate <= TRACK_RATE_MAX, "Bad out rate");
+  NS_ASSERTION(0 < aInRate && aInRate <= TRACK_RATE_MAX, "Bad in rate");
+  NS_ASSERTION(0 <= aTicks && aTicks <= TRACK_TICKS_MAX, "Bad ticks");
+  return (aTicks * aOutRate + aInRate - 1) / aInRate;
 }
 
-inline TrackTicks TimeToTicksRoundDown(TrackRate aRate, StreamTime aTime)
-{
-  NS_ASSERTION(0 < aRate && aRate <= TRACK_RATE_MAX, "Bad rate");
-  NS_ASSERTION(0 <= aTime && aTime <= STREAM_TIME_MAX, "Bad time");
-  return (aTime*aRate) >> MEDIA_TIME_FRAC_BITS;
-}
-
-inline StreamTime TicksToTimeRoundUp(TrackRate aRate, TrackTicks aTicks)
+inline TrackTicks SecondsToTicksRoundDown(TrackRate aRate, double aSeconds)
 {
   NS_ASSERTION(0 < aRate && aRate <= TRACK_RATE_MAX, "Bad rate");
-  NS_ASSERTION(0 <= aTicks && aTicks <= TRACK_TICKS_MAX, "Bad samples");
-  return ((aTicks << MEDIA_TIME_FRAC_BITS) + aRate - 1)/aRate;
+  NS_ASSERTION(0 <= aSeconds && aSeconds <= TRACK_TICKS_MAX/TRACK_RATE_MAX,
+               "Bad seconds");
+  return aSeconds * aRate;
 }
-
-inline StreamTime TicksToTimeRound(TrackRate aRate, TrackTicks aTicks)
+inline double TrackTicksToSeconds(TrackRate aRate, TrackTicks aTicks)
 {
   NS_ASSERTION(0 < aRate && aRate <= TRACK_RATE_MAX, "Bad rate");
-  NS_ASSERTION(0 <= aTicks && aTicks <= TRACK_TICKS_MAX, "Bad samples");
-  return ((aTicks << MEDIA_TIME_FRAC_BITS) + aRate/2)/aRate;
-}
-
-inline StreamTime TicksToTimeRoundDown(TrackRate aRate, TrackTicks aTicks)
-{
-  NS_ASSERTION(0 < aRate && aRate <= TRACK_RATE_MAX, "Bad rate");
-  NS_WARN_IF_FALSE(0 <= aTicks && aTicks <= TRACK_TICKS_MAX, "Bad samples");
-  return (aTicks << MEDIA_TIME_FRAC_BITS)/aRate;
+  NS_ASSERTION(0 <= aTicks && aTicks <= TRACK_TICKS_MAX, "Bad ticks");
+  return static_cast<double>(aTicks)/aRate;
 }
 
 /**
  * This object contains the decoded data for a stream's tracks.
  * A StreamBuffer can be appended to. Logically a StreamBuffer only gets longer,
  * but we also have the ability to "forget" data before a certain time that
  * we know won't be used again. (We prune a whole number of seconds internally.)
  *
@@ -81,30 +78,31 @@ public:
    * Tracks have a unique ID assigned at creation. This allows us to identify
    * the same track across StreamBuffers. A StreamBuffer should never have
    * two tracks with the same ID (even if they don't overlap in time).
    * TODO Tracks can also be enabled and disabled over time.
    * TODO Add TimeVarying<TrackTicks,bool> mEnabled.
    * Takes ownership of aSegment.
    */
   class Track {
-  public:
-    Track(TrackID aID, TrackRate aRate, TrackTicks aStart, MediaSegment* aSegment)
+    Track(TrackID aID, TrackRate aRate, TrackTicks aStart, MediaSegment* aSegment, TrackRate aGraphRate)
       : mStart(aStart),
         mSegment(aSegment),
         mRate(aRate),
+        mGraphRate(aGraphRate),
         mID(aID),
         mEnded(false)
     {
       MOZ_COUNT_CTOR(Track);
 
       NS_ASSERTION(aID > TRACK_NONE, "Bad track ID");
       NS_ASSERTION(0 < aRate && aRate <= TRACK_RATE_MAX, "Invalid rate");
       NS_ASSERTION(0 <= aStart && aStart <= aSegment->GetDuration(), "Bad start position");
     }
+  public:
     ~Track()
     {
       MOZ_COUNT_DTOR(Track);
     }
     template <class T> T* Get() const
     {
       if (mSegment->GetType() == T::StaticType()) {
         return static_cast<T*>(mSegment.get());
@@ -114,29 +112,29 @@ public:
     MediaSegment* GetSegment() const { return mSegment; }
     TrackRate GetRate() const { return mRate; }
     TrackID GetID() const { return mID; }
     bool IsEnded() const { return mEnded; }
     TrackTicks GetStart() const { return mStart; }
     TrackTicks GetEnd() const { return mSegment->GetDuration(); }
     StreamTime GetEndTimeRoundDown() const
     {
-      return mozilla::TicksToTimeRoundDown(mRate, mSegment->GetDuration());
+      return TicksToTimeRoundDown(mSegment->GetDuration());
     }
     StreamTime GetStartTimeRoundDown() const
     {
-      return mozilla::TicksToTimeRoundDown(mRate, mStart);
+      return TicksToTimeRoundDown(mStart);
     }
     TrackTicks TimeToTicksRoundDown(StreamTime aTime) const
     {
-      return mozilla::TimeToTicksRoundDown(mRate, aTime);
+      return RateConvertTicksRoundDown(mRate, mGraphRate, aTime);
     }
     StreamTime TicksToTimeRoundDown(TrackTicks aTicks) const
     {
-      return mozilla::TicksToTimeRoundDown(mRate, aTicks);
+      return RateConvertTicksRoundDown(mGraphRate, mRate, aTicks);
     }
     MediaSegment::Type GetType() const { return mSegment->GetType(); }
 
     void SetEnded() { mEnded = true; }
     void AppendFrom(Track* aTrack)
     {
       NS_ASSERTION(!mEnded, "Can't append to ended track");
       NS_ASSERTION(aTrack->mID == mID, "IDs must match");
@@ -168,17 +166,18 @@ public:
   protected:
     friend class StreamBuffer;
 
     // Start offset is in ticks at rate mRate
     TrackTicks mStart;
     // The segment data starts at the start of the owning StreamBuffer, i.e.,
     // there's mStart silence/no video at the beginning.
     nsAutoPtr<MediaSegment> mSegment;
-    TrackRate mRate; // rate in ticks per second
+    TrackRate mRate; // track rate in ticks per second
+    TrackRate mGraphRate; // graph rate in StreamTime per second
     // Unique ID
     TrackID mID;
     // True when the track ends with the data in mSegment
     bool mEnded;
   };
 
   class CompareTracksByID {
   public:
@@ -187,16 +186,19 @@ public:
     }
     bool LessThan(Track* aA, Track* aB) const {
       return aA->GetID() < aB->GetID();
     }
   };
 
   StreamBuffer()
     : mTracksKnownTime(0), mForgottenTime(0)
+#ifdef DEBUG
+    , mGraphRateIsSet(false)
+#endif
   {
     MOZ_COUNT_CTOR(StreamBuffer);
   }
   ~StreamBuffer()
   {
     MOZ_COUNT_DTOR(StreamBuffer);
   }
 
@@ -206,34 +208,55 @@ public:
     amount += mTracks.SizeOfExcludingThis(aMallocSizeOf);
     for (size_t i = 0; i < mTracks.Length(); i++) {
       amount += mTracks[i]->SizeOfIncludingThis(aMallocSizeOf);
     }
     return amount;
   }
 
   /**
+   * Initialize the graph rate for use in calculating StreamTimes from track
+   * ticks.  Called when a MediaStream's graph pointer is initialized.
+   */
+  void InitGraphRate(TrackRate aGraphRate)
+  {
+    mGraphRate = aGraphRate;
+#if DEBUG
+    MOZ_ASSERT(!mGraphRateIsSet);
+    mGraphRateIsSet = true;
+#endif
+  }
+
+  TrackRate GraphRate() const
+  {
+    MOZ_ASSERT(mGraphRateIsSet);
+    return mGraphRate;
+  }
+
+  /**
    * Takes ownership of aSegment. Don't do this while iterating, or while
    * holding a Track reference.
    * aSegment must have aStart worth of null data.
    */
   Track& AddTrack(TrackID aID, TrackRate aRate, TrackTicks aStart, MediaSegment* aSegment)
   {
+    NS_ASSERTION(!FindTrack(aID), "Track with this ID already exists");
+
+    Track* track = new Track(aID, aRate, aStart, aSegment, GraphRate());
+    mTracks.InsertElementSorted(track, CompareTracksByID());
+
     if (mTracksKnownTime == STREAM_TIME_MAX) {
       // There exists code like
       // http://mxr.mozilla.org/mozilla-central/source/media/webrtc/signaling/src/mediapipeline/MediaPipeline.cpp?rev=96b197deb91e&mark=1292-1297#1292
       NS_WARNING("Adding track to StreamBuffer that should have no more tracks");
     } else {
-      NS_ASSERTION(TimeToTicksRoundDown(aRate, mTracksKnownTime) <= aStart,
+      NS_ASSERTION(track->TimeToTicksRoundDown(mTracksKnownTime) <= aStart,
                    "Start time too early");
     }
-    NS_ASSERTION(!FindTrack(aID), "Track with this ID already exists");
-
-    return **mTracks.InsertElementSorted(new Track(aID, aRate, aStart, aSegment),
-                                         CompareTracksByID());
+    return *track;
   }
   void AdvanceKnownTracksTime(StreamTime aKnownTime)
   {
     NS_ASSERTION(aKnownTime >= mTracksKnownTime, "Can't move tracks-known time earlier");
     mTracksKnownTime = aKnownTime;
   }
 
   /**
@@ -304,20 +327,24 @@ public:
    * Returns the latest time passed to ForgetUpTo.
    */
   StreamTime GetForgottenDuration()
   {
     return mForgottenTime;
   }
 
 protected:
+  TrackRate mGraphRate; // StreamTime per second
   // Any new tracks added will start at or after this time. In other words, the track
   // list is complete and correct for all times less than this time.
   StreamTime mTracksKnownTime;
   StreamTime mForgottenTime;
   // All known tracks for this StreamBuffer
   nsTArray<nsAutoPtr<Track> > mTracks;
+#ifdef DEBUG
+  bool mGraphRateIsSet;
+#endif
 };
 
 }
 
 #endif /* MOZILLA_STREAMBUFFER_H_ */
 
--- a/content/media/webaudio/AudioContext.cpp
+++ b/content/media/webaudio/AudioContext.cpp
@@ -529,17 +529,18 @@ AudioContext::DestinationStream() const
     return Destination()->Stream();
   }
   return nullptr;
 }
 
 double
 AudioContext::CurrentTime() const
 {
-  return MediaTimeToSeconds(Destination()->Stream()->GetCurrentTime()) +
+  MediaStream* stream = Destination()->Stream();
+  return stream->StreamTimeToSeconds(stream->GetCurrentTime()) +
       ExtraCurrentTime();
 }
 
 void
 AudioContext::Shutdown()
 {
   mIsShutDown = true;
 
--- a/content/media/webaudio/WebAudioUtils.h
+++ b/content/media/webaudio/WebAudioUtils.h
@@ -101,17 +101,17 @@ namespace WebAudioUtils {
 
   inline double DiscreteTimeConstantForSampleRate(double timeConstant, double sampleRate)
   {
     return 1.0 - std::exp(-1.0 / (sampleRate * timeConstant));
   }
 
   inline bool IsTimeValid(double aTime)
   {
-    return aTime >= 0 &&  aTime <= (MEDIA_TIME_MAX >> MEDIA_TIME_FRAC_BITS);
+    return aTime >= 0 && aTime <= (MEDIA_TIME_MAX >> TRACK_RATE_MAX_BITS);
   }
 
   /**
    * Converts a floating point value to an integral type in a safe and
    * platform agnostic way.  The following program demonstrates the kinds
    * of ways things can go wrong depending on the CPU architecture you're
    * compiling for:
    *
--- a/content/media/webrtc/MediaEngineDefault.cpp
+++ b/content/media/webrtc/MediaEngineDefault.cpp
@@ -278,17 +278,17 @@ MediaEngineDefaultVideoSource::NotifyPul
   VideoSegment segment;
   MonitorAutoLock lock(mMonitor);
   if (mState != kStarted) {
     return;
   }
 
   // Note: we're not giving up mImage here
   nsRefPtr<layers::Image> image = mImage;
-  TrackTicks target = TimeToTicksRoundUp(USECS_PER_S, aDesiredTime);
+  TrackTicks target = aSource->TimeToTicksRoundUp(USECS_PER_S, aDesiredTime);
   TrackTicks delta = target - aLastEndTime;
 
   if (delta > 0) {
     // nullptr images are allowed
     IntSize size(image ? mOpts.mWidth : 0, image ? mOpts.mHeight : 0);
     segment.AppendFrame(image.forget(), delta, size);
     // This can fail if either a) we haven't added the track yet, or b)
     // we've removed or finished the track.
--- a/content/media/webrtc/MediaEngineTabVideoSource.cpp
+++ b/content/media/webrtc/MediaEngineTabVideoSource.cpp
@@ -156,17 +156,17 @@ void
 MediaEngineTabVideoSource::
 NotifyPull(MediaStreamGraph*, SourceMediaStream* aSource, mozilla::TrackID aID, mozilla::StreamTime aDesiredTime, mozilla::TrackTicks& aLastEndTime)
 {
   VideoSegment segment;
   MonitorAutoLock mon(mMonitor);
 
   // Note: we're not giving up mImage here
   nsRefPtr<layers::CairoImage> image = mImage;
-  TrackTicks target = TimeToTicksRoundUp(USECS_PER_S, aDesiredTime);
+  TrackTicks target = aSource->TimeToTicksRoundUp(USECS_PER_S, aDesiredTime);
   TrackTicks delta = target - aLastEndTime;
   if (delta > 0) {
     // nullptr images are allowed
     gfx::IntSize size = image ? image->GetSize() : IntSize(0, 0);
     segment.AppendFrame(image.forget().downcast<layers::Image>(), delta, size);
     // This can fail if either a) we haven't added the track yet, or b)
     // we've removed or finished the track.
     if (aSource->AppendToTrack(aID, &(segment))) {
--- a/content/media/webrtc/MediaEngineWebRTCAudio.cpp
+++ b/content/media/webrtc/MediaEngineWebRTCAudio.cpp
@@ -379,17 +379,17 @@ void
 MediaEngineWebRTCAudioSource::NotifyPull(MediaStreamGraph* aGraph,
                                          SourceMediaStream *aSource,
                                          TrackID aID,
                                          StreamTime aDesiredTime,
                                          TrackTicks &aLastEndTime)
 {
   // Ignore - we push audio data
 #ifdef DEBUG
-  TrackTicks target = TimeToTicksRoundUp(SAMPLE_FREQUENCY, aDesiredTime);
+  TrackTicks target = aSource->TimeToTicksRoundUp(SAMPLE_FREQUENCY, aDesiredTime);
   TrackTicks delta = target - aLastEndTime;
   LOG(("Audio: NotifyPull: aDesiredTime %ld, target %ld, delta %ld",(int64_t) aDesiredTime, (int64_t) target, (int64_t) delta));
   aLastEndTime = target;
 #endif
 }
 
 nsresult
 MediaEngineWebRTCAudioSource::Snapshot(uint32_t aDuration, nsIDOMFile** aFile)
--- a/content/media/webrtc/MediaEngineWebRTCVideo.cpp
+++ b/content/media/webrtc/MediaEngineWebRTCVideo.cpp
@@ -138,17 +138,17 @@ MediaEngineWebRTCVideoSource::NotifyPull
   VideoSegment segment;
 
   MonitorAutoLock lock(mMonitor);
   if (mState != kStarted)
     return;
 
   // Note: we're not giving up mImage here
   nsRefPtr<layers::Image> image = mImage;
-  TrackTicks target = TimeToTicksRoundUp(USECS_PER_S, aDesiredTime);
+  TrackTicks target = aSource->TimeToTicksRoundUp(USECS_PER_S, aDesiredTime);
   TrackTicks delta = target - aLastEndTime;
   LOGFRAME(("NotifyPull, desired = %ld, target = %ld, delta = %ld %s", (int64_t) aDesiredTime,
             (int64_t) target, (int64_t) delta, image ? "" : "<null>"));
 
   // Bug 846188 We may want to limit incoming frames to the requested frame rate
   // mFps - if you want 30FPS, and the camera gives you 60FPS, this could
   // cause issues.
   // We may want to signal if the actual frame rate is below mMinFPS -
--- a/content/svg/content/src/SVGStyleElement.cpp
+++ b/content/svg/content/src/SVGStyleElement.cpp
@@ -78,17 +78,17 @@ SVGStyleElement::BindToTree(nsIDocument*
 
   return rv;
 }
 
 void
 SVGStyleElement::UnbindFromTree(bool aDeep, bool aNullParent)
 {
   nsCOMPtr<nsIDocument> oldDoc = GetCurrentDoc();
-  ShadowRoot* oldShadow = GetShadowRoot();
+  ShadowRoot* oldShadow = GetContainingShadow();
   SVGStyleElementBase::UnbindFromTree(aDeep, aNullParent);
   UpdateStyleSheetInternal(oldDoc, oldShadow);
 }
 
 nsresult
 SVGStyleElement::SetAttr(int32_t aNameSpaceID, nsIAtom* aName,
                          nsIAtom* aPrefix, const nsAString& aValue,
                          bool aNotify)
--- a/dom/base/DOMException.cpp
+++ b/dom/base/DOMException.cpp
@@ -64,17 +64,17 @@ enum DOM4ErrorTypeCodeMap {
   TransactionInactiveError = 0,
   ReadOnlyError            = 0,
   VersionError             = 0,
 
   /* File API errors http://dev.w3.org/2006/webapi/FileAPI/#ErrorAndException */
   NotReadableError         = 0,
 
   /* FileHandle API errors */
-  LockedFileInactiveError = 0,
+  FileHandleInactiveError = 0,
 
   /* WebCrypto errors https://dvcs.w3.org/hg/webcrypto-api/raw-file/tip/spec/Overview.html#dfn-DataError */
   OperationError           = 0,
 };
 
 #define DOM4_MSG_DEF(name, message, nsresult) {(nsresult), name, #name, message},
 #define DOM_MSG_DEF(val, message) {(val), NS_ERROR_GET_CODE(val), #val, message},
 
--- a/dom/base/StructuredCloneTags.h
+++ b/dom/base/StructuredCloneTags.h
@@ -18,17 +18,17 @@ enum StructuredCloneTags {
   // These tags are used only for main thread structured clone.
   SCTAG_DOM_BLOB,
 
   // This tag is obsolete and exists only for backwards compatibility with
   // existing IndexedDB databases.
   SCTAG_DOM_FILE_WITHOUT_LASTMODIFIEDDATE,
 
   SCTAG_DOM_FILELIST,
-  SCTAG_DOM_FILEHANDLE,
+  SCTAG_DOM_MUTABLEFILE,
   SCTAG_DOM_FILE,
 
   // These tags are used for both main thread and workers.
   SCTAG_DOM_IMAGEDATA,
   SCTAG_DOM_MAP_MESSAGEPORT,
 
   SCTAG_DOM_FUNCTION,
 
--- a/dom/base/domerr.msg
+++ b/dom/base/domerr.msg
@@ -123,16 +123,16 @@ DOM_MSG_DEF(NS_ERROR_INVALID_ARG        
 DOM_MSG_DEF(NS_ERROR_NO_AGGREGATION                , "Component does not support aggregation")
 DOM_MSG_DEF(NS_ERROR_NOT_AVAILABLE                 , "Component is not available")
 DOM_MSG_DEF(NS_ERROR_FACTORY_NOT_REGISTERED        , "Factory not registered")
 DOM_MSG_DEF(NS_ERROR_FACTORY_NOT_LOADED            , "Factory not loaded")
 DOM_MSG_DEF(NS_ERROR_FACTORY_NO_SIGNATURE_SUPPORT  , "Factory does not support signatures")
 DOM_MSG_DEF(NS_ERROR_FACTORY_EXISTS                , "Factory already exists")
 
 DOM4_MSG_DEF(UnknownError, "The operation failed for reasons unrelated to the file storage itself and not covered by any other error code.", NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR)
-DOM4_MSG_DEF(LockedFileInactiveError, "A request was placed against a locked file which is currently not active, or which is finished.", NS_ERROR_DOM_FILEHANDLE_LOCKEDFILE_INACTIVE_ERR)
-DOM4_MSG_DEF(ReadOnlyError, "A mutation operation was attempted in a READ_ONLY locked file.", NS_ERROR_DOM_FILEHANDLE_READ_ONLY_ERR)
+DOM4_MSG_DEF(FileHandleInactiveError, "A request was placed against a file handle which is currently not active, or which is finished.", NS_ERROR_DOM_FILEHANDLE_INACTIVE_ERR)
+DOM4_MSG_DEF(ReadOnlyError, "A mutation operation was attempted in a READ_ONLY file handle.", NS_ERROR_DOM_FILEHANDLE_READ_ONLY_ERR)
 
 DOM4_MSG_DEF(InvalidStateError, "A mutation operation was attempted on a file storage that did not allow mutations.", NS_ERROR_DOM_FILEHANDLE_NOT_ALLOWED_ERR)
-DOM4_MSG_DEF(AbortError, "A request was aborted, for example through a call to LockedFile.abort.", NS_ERROR_DOM_FILEHANDLE_ABORT_ERR)
-DOM4_MSG_DEF(QuotaExceededError, "The current locked file exceeded its quota limitations.", NS_ERROR_DOM_FILEHANDLE_QUOTA_ERR)
+DOM4_MSG_DEF(AbortError, "A request was aborted, for example through a call to FileHandle.abort.", NS_ERROR_DOM_FILEHANDLE_ABORT_ERR)
+DOM4_MSG_DEF(QuotaExceededError, "The current file handle exceeded its quota limitations.", NS_ERROR_DOM_FILEHANDLE_QUOTA_ERR)
 
 DOM_MSG_DEF(NS_ERROR_DOM_JS_EXCEPTION, "A callback threw an exception")
--- a/dom/base/nsDOMWindowUtils.cpp
+++ b/dom/base/nsDOMWindowUtils.cpp
@@ -60,21 +60,21 @@
 #include <gdk/gdk.h>
 #include <gdk/gdkx.h>
 #endif
 
 #include "Layers.h"
 #include "mozilla/layers/ShadowLayers.h"
 
 #include "mozilla/dom/Element.h"
-#include "mozilla/dom/FileHandle.h"
-#include "mozilla/dom/FileHandleBinding.h"
 #include "mozilla/dom/TabChild.h"
 #include "mozilla/dom/IDBFactoryBinding.h"
 #include "mozilla/dom/indexedDB/IndexedDatabaseManager.h"
+#include "mozilla/dom/MutableFile.h"
+#include "mozilla/dom/MutableFileBinding.h"
 #include "mozilla/dom/quota/PersistenceType.h"
 #include "mozilla/dom/quota/QuotaManager.h"
 #include "nsDOMBlobBuilder.h"
 #include "nsPrintfCString.h"
 #include "nsViewportInfo.h"
 #include "nsIFormControl.h"
 #include "nsIScriptError.h"
 #include "nsIAppShell.h"
@@ -3020,19 +3020,19 @@ NS_IMETHODIMP
 nsDOMWindowUtils::GetFileId(JS::Handle<JS::Value> aFile, JSContext* aCx,
                             int64_t* aResult)
 {
   MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome());
 
   if (!aFile.isPrimitive()) {
     JSObject* obj = aFile.toObjectOrNull();
 
-    FileHandle* fileHandle = nullptr;
-    if (NS_SUCCEEDED(UNWRAP_OBJECT(FileHandle, obj, fileHandle))) {
-      *aResult = fileHandle->GetFileId();
+    MutableFile* mutableFile = nullptr;
+    if (NS_SUCCEEDED(UNWRAP_OBJECT(MutableFile, obj, mutableFile))) {
+      *aResult = mutableFile->GetFileId();
       return NS_OK;
     }
 
     nsISupports* nativeObj =
       nsContentUtils::XPConnect()->GetNativeOfWrapper(aCx, obj);
 
     nsCOMPtr<nsIDOMBlob> blob = do_QueryInterface(nativeObj);
     if (blob) {
@@ -3666,42 +3666,16 @@ nsDOMWindowUtils::GetOMTAStyle(nsIDOMEle
   } else {
     aResult.Truncate();
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
-nsDOMWindowUtils::GetOMTAOrComputedStyle(nsIDOMElement* aElement,
-                                         const nsAString& aProperty,
-                                         nsAString& aResult)
-{
-  MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome());
-
-  // Try to get OMTA style
-  nsresult rv = GetOMTAStyle(aElement, aProperty, aResult);
-  if (NS_FAILED(rv) || !aResult.IsEmpty()) {
-    return rv;
-  }
-
-  // Otherwise, fall back to computed style
-  nsCOMPtr<Element> element = do_QueryInterface(aElement);
-  if (!element) {
-    return NS_ERROR_INVALID_ARG;
-  }
-  nsCOMPtr<nsIDOMCSSStyleDeclaration> style;
-  rv = element->GetCurrentDoc()->GetWindow()->
-    GetComputedStyle(aElement, aProperty, getter_AddRefs(style));
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  return style->GetPropertyValue(aProperty, aResult);
-}
-
-NS_IMETHODIMP
 nsDOMWindowUtils::GetContentAPZTestData(JSContext* aContext,
                                         JS::MutableHandleValue aOutContentTestData)
 {
   MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome());
 
   if (nsIWidget* widget = GetWidget()) {
     nsRefPtr<LayerManager> lm = widget->GetLayerManager();
     if (lm && lm->GetBackendType() == LayersBackend::LAYERS_CLIENT) {
--- a/dom/bindings/Bindings.conf
+++ b/dom/bindings/Bindings.conf
@@ -424,17 +424,17 @@ DOMInterfaces = {
 'Exception': {
     'headerFile': 'mozilla/dom/DOMException.h',
     'binaryNames': {
         'message': 'messageMoz',
     },
 },
 
 'FileHandle': {
-    'nativeType': 'mozilla::dom::FileHandle'
+    'nativeType': 'mozilla::dom::FileHandle',
 },
 
 'FileList': {
     'nativeType': 'nsDOMFileList',
     'headerFile': 'nsDOMFile.h',
     'resultNotAddRefed': [ 'item' ]
 },
 
@@ -633,33 +633,33 @@ DOMInterfaces = {
 'IDBDatabase': {
     'nativeType': 'mozilla::dom::indexedDB::IDBDatabase',
 },
 
 'IDBFactory': {
     'nativeType': 'mozilla::dom::indexedDB::IDBFactory',
 },
 
-'IDBFileHandle': {
-    'nativeType': 'mozilla::dom::indexedDB::IDBFileHandle',
-},
-
 'IDBIndex': {
     'nativeType': 'mozilla::dom::indexedDB::IDBIndex',
     'binaryNames': {
         'mozGetAll': 'getAll',
         'mozGetAllKeys': 'getAllKeys',
     }
 },
 
 'IDBKeyRange': {
     'nativeType': 'mozilla::dom::indexedDB::IDBKeyRange',
     'wrapperCache': False,
 },
 
+'IDBMutableFile': {
+    'nativeType': 'mozilla::dom::indexedDB::IDBMutableFile',
+},
+
 'IDBObjectStore': {
     'nativeType': 'mozilla::dom::indexedDB::IDBObjectStore',
     'implicitJSContext': [ 'createIndex' ],
     'binaryNames': {
         'mozGetAll': 'getAll'
     }
 },
 
@@ -731,20 +731,16 @@ DOMInterfaces = {
 'Location': {
     # NOTE: Before you turn on codegen for Location, make sure all the
     # Unforgeable stuff is dealt with.
     'nativeType': 'nsIDOMLocation',
     'skipGen': True,
     'register': False
 },
 
-'LockedFile': {
-    'nativeType': 'mozilla::dom::LockedFile',
-},
-
 'MediaList': {
     'nativeType': 'nsMediaList',
     'headerFile': 'nsIMediaList.h',
 },
 
 'MediaSource': [{
     'resultNotAddRefed': [ 'sourceBuffers', 'activeSourceBuffers' ],
 },
@@ -870,16 +866,20 @@ DOMInterfaces = {
 'MozTimeManager': {
     'nativeType': 'mozilla::dom::time::TimeManager',
 },
 
 'MozVoicemail': {
     'nativeType': 'mozilla::dom::Voicemail',
 },
 
+'MutableFile': {
+    'nativeType': 'mozilla::dom::MutableFile'
+},
+
 'MutationObserver': {
     'nativeType': 'nsDOMMutationObserver',
 },
 
 'MutationRecord': {
     'nativeType': 'nsDOMMutationRecord',
     'headerFile': 'nsDOMMutationObserver.h',
     'resultNotAddRefed': [ 'target', 'addedNodes', 'removedNodes',
--- a/dom/filehandle/File.cpp
+++ b/dom/filehandle/File.cpp
@@ -1,58 +1,58 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "File.h"
 
-#include "LockedFile.h"
+#include "FileHandle.h"
 #include "mozilla/Assertions.h"
 #include "nsDebug.h"
 
 namespace mozilla {
 namespace dom {
 
 using indexedDB::IndexedDatabaseManager;
 
   // Create as a file
 File::File(const nsAString& aName, const nsAString& aContentType,
-           uint64_t aLength, nsIFile* aFile, LockedFile* aLockedFile)
+           uint64_t aLength, nsIFile* aFile, FileHandle* aFileHandle)
 : nsDOMFileCC(aName, aContentType, aLength),
-  mFile(aFile), mLockedFile(aLockedFile),
+  mFile(aFile), mFileHandle(aFileHandle),
   mWholeFile(true), mStoredFile(false)
 {
   MOZ_ASSERT(mFile, "Null file!");
-  MOZ_ASSERT(mLockedFile, "Null locked file!");
+  MOZ_ASSERT(mFileHandle, "Null file handle!");
 }
 
 // Create as a stored file
 File::File(const nsAString& aName, const nsAString& aContentType,
-           uint64_t aLength, nsIFile* aFile, LockedFile* aLockedFile,
+           uint64_t aLength, nsIFile* aFile, FileHandle* aFileHandle,
            FileInfo* aFileInfo)
 : nsDOMFileCC(aName, aContentType, aLength),
-  mFile(aFile), mLockedFile(aLockedFile),
+  mFile(aFile), mFileHandle(aFileHandle),
   mWholeFile(true), mStoredFile(true)
 {
   MOZ_ASSERT(mFile, "Null file!");
-  MOZ_ASSERT(mLockedFile, "Null locked file!");
+  MOZ_ASSERT(mFileHandle, "Null file handle!");
   mFileInfos.AppendElement(aFileInfo);
 }
 
 // Create slice
 File::File(const File* aOther, uint64_t aStart, uint64_t aLength,
            const nsAString& aContentType)
 : nsDOMFileCC(aContentType, aOther->mStart + aStart, aLength),
-  mFile(aOther->mFile), mLockedFile(aOther->mLockedFile),
+  mFile(aOther->mFile), mFileHandle(aOther->mFileHandle),
   mWholeFile(false), mStoredFile(aOther->mStoredFile)
 {
-  NS_ASSERTION(mFile, "Null file!");
-  NS_ASSERTION(mLockedFile, "Null locked file!");
+  MOZ_ASSERT(mFile, "Null file!");
+  MOZ_ASSERT(mFileHandle, "Null file handle!");
 
   if (mStoredFile) {
     FileInfo* fileInfo;
 
     if (IndexedDatabaseManager::IsClosed()) {
       fileInfo = aOther->GetFileInfo();
     }
     else {
@@ -64,30 +64,30 @@ File::File(const File* aOther, uint64_t 
   }
 }
 
 File::~File()
 {
 }
 
 NS_IMPL_CYCLE_COLLECTION_INHERITED(File, nsDOMFileCC,
-                                   mLockedFile)
+                                   mFileHandle)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(File)
 NS_INTERFACE_MAP_END_INHERITING(nsDOMFileCC)
 
 NS_IMPL_ADDREF_INHERITED(File, nsDOMFileCC)
 NS_IMPL_RELEASE_INHERITED(File, nsDOMFileCC)
 
 NS_IMETHODIMP
 File::GetInternalStream(nsIInputStream **aStream)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
-  nsresult rv = mLockedFile->OpenInputStream(mWholeFile, mStart, mLength,
+  nsresult rv = mFileHandle->OpenInputStream(mWholeFile, mStart, mLength,
                                              aStream);
   NS_ENSURE_SUCCESS(rv, rv);
 
   return NS_OK;
 }
 
 already_AddRefed<nsIDOMBlob>
 File::CreateSlice(uint64_t aStart, uint64_t aLength,
--- a/dom/filehandle/File.h
+++ b/dom/filehandle/File.h
@@ -11,32 +11,32 @@
 #include "nsAutoPtr.h"
 #include "nsCOMPtr.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsDOMFile.h"
 
 namespace mozilla {
 namespace dom {
 
-class LockedFile;
+class FileHandle;
 
 class File : public nsDOMFileCC
 {
 public:
   NS_DECL_ISUPPORTS_INHERITED
 
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(File, nsDOMFileCC)
 
   // Create as a file
   File(const nsAString& aName, const nsAString& aContentType,
-       uint64_t aLength, nsIFile* aFile, LockedFile* aLockedFile);
+       uint64_t aLength, nsIFile* aFile, FileHandle* aFileHandle);
 
   // Create as a stored file
   File(const nsAString& aName, const nsAString& aContentType,
-       uint64_t aLength, nsIFile* aFile, LockedFile* aLockedFile,
+       uint64_t aLength, nsIFile* aFile, FileHandle* aFileHandle,
        FileInfo* aFileInfo);
 
   // Overrides
   NS_IMETHOD
   GetMozFullPathInternal(nsAString& aFullPath) MOZ_OVERRIDE;
 
   NS_IMETHOD
   GetInternalStream(nsIInputStream** aStream) MOZ_OVERRIDE;
@@ -67,17 +67,17 @@ protected:
   virtual bool
   IsSnapshot() const MOZ_OVERRIDE
   {
     return true;
   }
 
 private:
   nsCOMPtr<nsIFile> mFile;
-  nsRefPtr<LockedFile> mLockedFile;
+  nsRefPtr<FileHandle> mFileHandle;
 
   bool mWholeFile;
   bool mStoredFile;
 };
 
 } // namespace dom
 } // namespace mozilla
 
--- a/dom/filehandle/FileHandle.cpp
+++ b/dom/filehandle/FileHandle.cpp
@@ -1,188 +1,1062 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "FileHandle.h"
 
-#include "File.h"
+#include "AsyncHelper.h"
+#include "FileHelper.h"
 #include "FileRequest.h"
 #include "FileService.h"
-#include "LockedFile.h"
+#include "FileStreamWrappers.h"
+#include "MemoryStreams.h"
 #include "MetadataHelper.h"
-#include "mozilla/Assertions.h"
+#include "mozilla/dom/EncodingUtils.h"
 #include "mozilla/dom/FileHandleBinding.h"
-#include "mozilla/ErrorResult.h"
-#include "nsAutoPtr.h"
+#include "mozilla/EventDispatcher.h"
+#include "MutableFile.h"
 #include "nsContentUtils.h"
 #include "nsDebug.h"
 #include "nsError.h"
+#include "nsIAppShell.h"
+#include "nsIDOMEvent.h"
 #include "nsIDOMFile.h"
-#include "nsIFile.h"
+#include "nsIEventTarget.h"
+#include "nsISeekableStream.h"
 #include "nsNetUtil.h"
+#include "nsServiceManagerUtils.h"
+#include "nsString.h"
+#include "nsStringStream.h"
+#include "nsThreadUtils.h"
+#include "nsWidgetsCID.h"
+
+#define STREAM_COPY_BLOCK_SIZE 32768
 
 namespace mozilla {
 namespace dom {
 
 namespace {
 
-class GetFileHelper : public MetadataHelper
+NS_DEFINE_CID(kAppShellCID, NS_APPSHELL_CID);
+
+class ReadHelper : public FileHelper
 {
 public:
-  GetFileHelper(LockedFile* aLockedFile,
-                FileRequest* aFileRequest,
-                MetadataParameters* aParams,
-                FileHandle* aFileHandle)
-  : MetadataHelper(aLockedFile, aFileRequest, aParams),
-    mFileHandle(aFileHandle)
+  ReadHelper(FileHandle* aFileHandle,
+             FileRequest* aFileRequest,
+             uint64_t aLocation,
+             uint64_t aSize)
+  : FileHelper(aFileHandle, aFileRequest),
+    mLocation(aLocation), mSize(aSize)
+  {
+    MOZ_ASSERT(mSize, "Passed zero size!");
+  }
+
+  nsresult
+  Init();
+
+  nsresult
+  DoAsyncRun(nsISupports* aStream) MOZ_OVERRIDE;
+
+  nsresult
+  GetSuccessResult(JSContext* aCx,
+                   JS::MutableHandle<JS::Value> aVal) MOZ_OVERRIDE;
+
+protected:
+  uint64_t mLocation;
+  uint64_t mSize;
+
+  nsRefPtr<MemoryOutputStream> mStream;
+};
+
+class ReadTextHelper : public ReadHelper
+{
+public:
+  ReadTextHelper(FileHandle* aFileHandle,
+                 FileRequest* aFileRequest,
+                 uint64_t aLocation,
+                 uint64_t aSize,
+                 const nsAString& aEncoding)
+  : ReadHelper(aFileHandle, aFileRequest, aLocation, aSize),
+    mEncoding(aEncoding)
   { }
 
-  virtual nsresult
+  nsresult
   GetSuccessResult(JSContext* aCx,
                    JS::MutableHandle<JS::Value> aVal) MOZ_OVERRIDE;
 
-  virtual void
-  ReleaseObjects() MOZ_OVERRIDE
+private:
+  nsString mEncoding;
+};
+
+class WriteHelper : public FileHelper
+{
+public:
+  WriteHelper(FileHandle* aFileHandle,
+              FileRequest* aFileRequest,
+              uint64_t aLocation,
+              nsIInputStream* aStream,
+              uint64_t aLength)
+  : FileHelper(aFileHandle, aFileRequest),
+    mLocation(aLocation), mStream(aStream), mLength(aLength)
+  {
+    MOZ_ASSERT(mLength, "Passed zero length!");
+  }
+
+  nsresult
+  DoAsyncRun(nsISupports* aStream);
+
+private:
+  uint64_t mLocation;
+  nsCOMPtr<nsIInputStream> mStream;
+  uint64_t mLength;
+};
+
+class TruncateHelper : public FileHelper
+{
+public:
+  TruncateHelper(FileHandle* aFileHandle,
+                 FileRequest* aFileRequest,
+                 uint64_t aOffset)
+  : FileHelper(aFileHandle, aFileRequest),
+    mOffset(aOffset)
+  { }
+
+  nsresult
+  DoAsyncRun(nsISupports* aStream);
+
+private:
+  class AsyncTruncator : public AsyncHelper
   {
-    mFileHandle = nullptr;
+  public:
+    AsyncTruncator(nsISupports* aStream, int64_t aOffset)
+    : AsyncHelper(aStream),
+      mOffset(aOffset)
+    { }
+  protected:
+    nsresult
+    DoStreamWork(nsISupports* aStream) MOZ_OVERRIDE;
+
+    uint64_t mOffset;
+  };
+
+  uint64_t mOffset;
+};
+
+class FlushHelper : public FileHelper
+{
+public:
+  FlushHelper(FileHandle* aFileHandle,
+              FileRequest* aFileRequest)
+  : FileHelper(aFileHandle, aFileRequest)
+  { }
+
+  nsresult
+  DoAsyncRun(nsISupports* aStream);
 
-    MetadataHelper::ReleaseObjects();
+private:
+  class AsyncFlusher : public AsyncHelper
+  {
+  public:
+    AsyncFlusher(nsISupports* aStream)
+    : AsyncHelper(aStream)
+    { }
+  protected:
+    nsresult
+    DoStreamWork(nsISupports* aStream) MOZ_OVERRIDE;
+  };
+};
+
+class OpenStreamHelper : public FileHelper
+{
+public:
+  OpenStreamHelper(FileHandle* aFileHandle,
+                   bool aWholeFile,
+                   uint64_t aStart,
+                   uint64_t aLength)
+  : FileHelper(aFileHandle, nullptr),
+    mWholeFile(aWholeFile), mStart(aStart), mLength(aLength)
+  { }
+
+  nsresult
+  DoAsyncRun(nsISupports* aStream);
+
+  nsCOMPtr<nsIInputStream>&
+  Result()
+  {
+    return mStream;
   }
 
 private:
-  nsRefPtr<FileHandle> mFileHandle;
+  bool mWholeFile;
+  uint64_t mStart;
+  uint64_t mLength;
+
+  nsCOMPtr<nsIInputStream> mStream;
 };
 
+already_AddRefed<nsIDOMEvent>
+CreateGenericEvent(EventTarget* aEventOwner,
+                   const nsAString& aType, bool aBubbles, bool aCancelable)
+{
+  nsCOMPtr<nsIDOMEvent> event;
+  NS_NewDOMEvent(getter_AddRefs(event), aEventOwner, nullptr, nullptr);
+  nsresult rv = event->InitEvent(aType, aBubbles, aCancelable);
+  NS_ENSURE_SUCCESS(rv, nullptr);
+
+  event->SetTrusted(true);
+
+  return event.forget();
+}
+
 } // anonymous namespace
 
-FileHandle::FileHandle(nsPIDOMWindow* aWindow)
-  : DOMEventTargetHelper(aWindow)
+// static
+already_AddRefed<FileHandle>
+FileHandle::Create(MutableFile* aMutableFile,
+                   FileMode aMode,
+                   RequestMode aRequestMode)
 {
+  MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
+
+  nsRefPtr<FileHandle> fileHandle = new FileHandle();
+
+  fileHandle->BindToOwner(aMutableFile);
+
+  fileHandle->mMutableFile = aMutableFile;
+  fileHandle->mMode = aMode;
+  fileHandle->mRequestMode = aRequestMode;
+
+  nsCOMPtr<nsIAppShell> appShell = do_GetService(kAppShellCID);
+  NS_ENSURE_TRUE(appShell, nullptr);
+
+  nsresult rv = appShell->RunBeforeNextEvent(fileHandle);
+  NS_ENSURE_SUCCESS(rv, nullptr);
+
+  fileHandle->mCreating = true;
+
+  FileService* service = FileService::GetOrCreate();
+  NS_ENSURE_TRUE(service, nullptr);
+
+  rv = service->Enqueue(fileHandle, nullptr);
+  NS_ENSURE_SUCCESS(rv, nullptr);
+
+  return fileHandle.forget();
 }
 
-FileHandle::FileHandle(DOMEventTargetHelper* aOwner)
-  : DOMEventTargetHelper(aOwner)
+FileHandle::FileHandle()
+: mReadyState(INITIAL),
+  mMode(FileMode::Readonly),
+  mRequestMode(NORMAL),
+  mLocation(0),
+  mPendingRequests(0),
+  mAborted(false),
+  mCreating(false)
 {
+  MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
+  SetIsDOMBinding();
 }
 
 FileHandle::~FileHandle()
 {
+  MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
+}
+
+NS_IMPL_CYCLE_COLLECTION_INHERITED(FileHandle, DOMEventTargetHelper,
+                                   mMutableFile)
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(FileHandle)
+  NS_INTERFACE_MAP_ENTRY(nsIRunnable)
+NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
+
+NS_IMPL_ADDREF_INHERITED(FileHandle, DOMEventTargetHelper)
+NS_IMPL_RELEASE_INHERITED(FileHandle, DOMEventTargetHelper)
+
+nsresult
+FileHandle::PreHandleEvent(EventChainPreVisitor& aVisitor)
+{
+  MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
+
+  aVisitor.mCanHandle = true;
+  aVisitor.mParentTarget = mMutableFile;
+  return NS_OK;
+}
+
+void
+FileHandle::OnNewRequest()
+{
+  MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
+  if (!mPendingRequests) {
+    MOZ_ASSERT(mReadyState == INITIAL, "Reusing a file handle!");
+    mReadyState = LOADING;
+  }
+  ++mPendingRequests;
+}
+
+void
+FileHandle::OnRequestFinished()
+{
+  MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
+  MOZ_ASSERT(mPendingRequests, "Mismatched calls!");
+  --mPendingRequests;
+  if (!mPendingRequests) {
+    MOZ_ASSERT(mAborted || mReadyState == LOADING, "Bad state!");
+    mReadyState = FileHandle::FINISHING;
+    Finish();
+  }
+}
+
+nsresult
+FileHandle::CreateParallelStream(nsISupports** aStream)
+{
+  MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
+
+  if (mMutableFile->IsInvalid()) {
+    return NS_ERROR_NOT_AVAILABLE;
+  }
+
+  nsCOMPtr<nsISupports> stream =
+    mMutableFile->CreateStream(mMutableFile->mFile,
+                               mMode == FileMode::Readonly);
+  NS_ENSURE_TRUE(stream, NS_ERROR_FAILURE);
+
+  mParallelStreams.AppendElement(stream);
+
+  stream.forget(aStream);
+  return NS_OK;
+}
+
+nsresult
+FileHandle::GetOrCreateStream(nsISupports** aStream)
+{
+  MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
+
+  if (mMutableFile->IsInvalid()) {
+    return NS_ERROR_NOT_AVAILABLE;
+  }
+
+  if (!mStream) {
+    nsCOMPtr<nsISupports> stream =
+      mMutableFile->CreateStream(mMutableFile->mFile,
+                                 mMode == FileMode::Readonly);
+    NS_ENSURE_TRUE(stream, NS_ERROR_FAILURE);
+
+    stream.swap(mStream);
+  }
+
+  nsCOMPtr<nsISupports> stream(mStream);
+  stream.forget(aStream);
+
+  return NS_OK;
+}
+
+already_AddRefed<FileRequest>
+FileHandle::GenerateFileRequest()
+{
+  MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
+  return FileRequest::Create(GetOwner(), this, /* aWrapAsDOMRequest */ false);
 }
 
 bool
-FileHandle::IsShuttingDown()
-{
-  return FileService::IsShuttingDown();
-}
-
-// virtual
-already_AddRefed<nsISupports>
-FileHandle::CreateStream(nsIFile* aFile, bool aReadOnly)
+FileHandle::IsOpen() const
 {
-  nsresult rv;
+  MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
 
-  if (aReadOnly) {
-    nsCOMPtr<nsIInputStream> stream;
-    rv = NS_NewLocalFileInputStream(getter_AddRefs(stream), aFile, -1, -1,
-                                    nsIFileInputStream::DEFER_OPEN);
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      return nullptr;
-    }
-    return stream.forget();
+  // If we haven't started anything then we're open.
+  if (mReadyState == INITIAL) {
+    MOZ_ASSERT(FileHelper::GetCurrentFileHandle() != this,
+               "This should be some other file handle (or null)!");
+    return true;
   }
 
-  nsCOMPtr<nsIFileStream> stream;
-  rv = NS_NewLocalFileStream(getter_AddRefs(stream), aFile, -1, -1,
-                             nsIFileStream::DEFER_OPEN);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return nullptr;
-  }
-  return stream.forget();
-}
+  // If we've already started then we need to check to see if we still have the
+  // mCreating flag set. If we do (i.e. we haven't returned to the event loop
+  // from the time we were created) then we are open. Otherwise check the
+  // currently running file handles to see if it's the same. We only allow other
+  // requests to be made if this file handle is currently running.
+  if (mReadyState == LOADING) {
+    if (mCreating) {
+      return true;
+    }
 
-// virtual
-already_AddRefed<nsIDOMFile>
-FileHandle::CreateFileObject(LockedFile* aLockedFile, uint32_t aFileSize)
-{
-  nsCOMPtr<nsIDOMFile> file =
-    new File(mName, mType, aFileSize, mFile, aLockedFile);
+    if (FileHelper::GetCurrentFileHandle() == this) {
+      return true;
+    }
+  }
 
-  return file.forget();
+  return false;
 }
 
 // virtual
 JSObject*
 FileHandle::WrapObject(JSContext* aCx)
 {
   return FileHandleBinding::Wrap(aCx, this);
 }
 
-already_AddRefed<LockedFile>
-FileHandle::Open(FileMode aMode, ErrorResult& aError)
+already_AddRefed<FileRequest>
+FileHandle::GetMetadata(const DOMFileMetadataParameters& aParameters,
+                        ErrorResult& aRv)
 {
-  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
+
+  // Common state checking
+  if (!CheckState(aRv)) {
+    return nullptr;
+  }
+
+  // Do nothing if the window is closed
+  if (!GetOwner()) {
+    return nullptr;
+  }
+
+  nsRefPtr<MetadataParameters> params =
+    new MetadataParameters(aParameters.mSize, aParameters.mLastModified);
+  if (!params->IsConfigured()) {
+    aRv.ThrowTypeError(MSG_METADATA_NOT_CONFIGURED);
+    return nullptr;
+  }
+
+  nsRefPtr<FileRequest> fileRequest = GenerateFileRequest();
+
+  nsRefPtr<MetadataHelper> helper =
+    new MetadataHelper(this, fileRequest, params);
 
-  if (IsShuttingDown()) {
-    aError.Throw(NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR);
+  if (NS_WARN_IF(NS_FAILED(helper->Enqueue()))) {
+    aRv.Throw(NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR);
+    return nullptr;
+  }
+
+  return fileRequest.forget();
+}
+
+already_AddRefed<FileRequest>
+FileHandle::ReadAsArrayBuffer(uint64_t aSize, ErrorResult& aRv)
+{
+  MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
+
+  // State and argument checking for read
+  if (!CheckStateAndArgumentsForRead(aSize, aRv)) {
+    return nullptr;
+  }
+
+  // Do nothing if the window is closed
+  if (!GetOwner()) {
+    return nullptr;
+  }
+
+  nsRefPtr<FileRequest> fileRequest = GenerateFileRequest();
+
+  nsRefPtr<ReadHelper> helper =
+    new ReadHelper(this, fileRequest, mLocation, aSize);
+
+  if (NS_WARN_IF(NS_FAILED(helper->Init())) ||
+      NS_WARN_IF(NS_FAILED(helper->Enqueue()))) {
+    aRv.Throw(NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR);
     return nullptr;
   }
 
-  nsRefPtr<LockedFile> lockedFile = LockedFile::Create(this, aMode);
-  if (!lockedFile) {
-    aError.Throw(NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR);
+  mLocation += aSize;
+
+  return fileRequest.forget();
+}
+
+already_AddRefed<FileRequest>
+FileHandle::ReadAsText(uint64_t aSize, const nsAString& aEncoding,
+                       ErrorResult& aRv)
+{
+  MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
+
+  // State and argument checking for read
+  if (!CheckStateAndArgumentsForRead(aSize, aRv)) {
+    return nullptr;
+  }
+
+  // Do nothing if the window is closed
+  if (!GetOwner()) {
+    return nullptr;
+  }
+
+  nsRefPtr<FileRequest> fileRequest = GenerateFileRequest();
+
+  nsRefPtr<ReadTextHelper> helper =
+    new ReadTextHelper(this, fileRequest, mLocation, aSize, aEncoding);
+
+  if (NS_WARN_IF(NS_FAILED(helper->Init())) ||
+      NS_WARN_IF(NS_FAILED(helper->Enqueue()))) {
+    aRv.Throw(NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR);
     return nullptr;
   }
 
-  return lockedFile.forget();
+  mLocation += aSize;
+
+  return fileRequest.forget();
 }
 
-already_AddRefed<DOMRequest>
-FileHandle::GetFile(ErrorResult& aError)
+already_AddRefed<FileRequest>
+FileHandle::Truncate(const Optional<uint64_t>& aSize, ErrorResult& aRv)
 {
-  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
+
+  // State checking for write
+  if (!CheckStateForWrite(aRv)) {
+    return nullptr;
+  }
+
+  // Getting location and additional state checking for truncate
+  uint64_t location;
+  if (aSize.WasPassed()) {
+    // Just in case someone calls us from C++
+    MOZ_ASSERT(aSize.Value() != UINT64_MAX, "Passed wrong size!");
+    location = aSize.Value();
+  } else {
+    if (mLocation == UINT64_MAX) {
+      aRv.Throw(NS_ERROR_DOM_FILEHANDLE_NOT_ALLOWED_ERR);
+      return nullptr;
+    }
+    location = mLocation;
+  }
+
+  // Do nothing if the window is closed
+  if (!GetOwner()) {
+    return nullptr;
+  }
+
+  nsRefPtr<FileRequest> fileRequest = GenerateFileRequest();
+
+  nsRefPtr<TruncateHelper> helper =
+    new TruncateHelper(this, fileRequest, location);
+
+  if (NS_WARN_IF(NS_FAILED(helper->Enqueue()))) {
+    aRv.Throw(NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR);
+    return nullptr;
+  }
+
+  if (aSize.WasPassed()) {
+    mLocation = aSize.Value();
+  }
+
+  return fileRequest.forget();
+}
+
+already_AddRefed<FileRequest>
+FileHandle::Flush(ErrorResult& aRv)
+{
+  MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
+
+  // State checking for write
+  if (!CheckStateForWrite(aRv)) {
+    return nullptr;
+  }
 
   // Do nothing if the window is closed
   if (!GetOwner()) {
     return nullptr;
   }
 
-  nsRefPtr<LockedFile> lockedFile =
-    LockedFile::Create(this, FileMode::Readonly, LockedFile::PARALLEL);
-  if (!lockedFile) {
-    aError.Throw(NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR);
+  nsRefPtr<FileRequest> fileRequest = GenerateFileRequest();
+
+  nsRefPtr<FlushHelper> helper = new FlushHelper(this, fileRequest);
+
+  if (NS_WARN_IF(NS_FAILED(helper->Enqueue()))) {
+    aRv.Throw(NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR);
+    return nullptr;
+  }
+
+  return fileRequest.forget();
+}
+
+void
+FileHandle::Abort(ErrorResult& aRv)
+{
+  MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
+
+  // This method is special enough for not using generic state checking methods.
+
+  // We can't use IsOpen here since we need it to be possible to call Abort()
+  // even from outside of transaction callbacks.
+  if (mReadyState != FileHandle::INITIAL &&
+      mReadyState != FileHandle::LOADING) {
+    aRv.Throw(NS_ERROR_DOM_FILEHANDLE_NOT_ALLOWED_ERR);
+    return;
+  }
+
+  bool needToFinish = mReadyState == INITIAL;
+
+  mAborted = true;
+  mReadyState = DONE;
+
+  // Fire the abort event if there are no outstanding requests. Otherwise the
+  // abort event will be fired when all outstanding requests finish.
+  if (needToFinish) {
+    aRv = Finish();
+  }
+}
+
+NS_IMETHODIMP
+FileHandle::Run()
+{
+  MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
+
+  // We're back at the event loop, no longer newborn.
+  mCreating = false;
+
+  // Maybe set the readyState to DONE if there were no requests generated.
+  if (mReadyState == INITIAL) {
+    mReadyState = DONE;
+
+    if (NS_FAILED(Finish())) {
+      NS_WARNING("Failed to finish!");
+    }
+  }
+
+  return NS_OK;
+}
+
+nsresult
+FileHandle::OpenInputStream(bool aWholeFile, uint64_t aStart, uint64_t aLength,
+                            nsIInputStream** aResult)
+{
+  MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
+  MOZ_ASSERT(mRequestMode == PARALLEL,
+             "Don't call me in other than parallel mode!");
+
+  // Common state checking
+  ErrorResult error;
+  if (!CheckState(error)) {
+    return error.ErrorCode();
+  }
+
+  // Do nothing if the window is closed
+  if (!GetOwner()) {
+    return NS_OK;
+  }
+
+  nsRefPtr<OpenStreamHelper> helper =
+    new OpenStreamHelper(this, aWholeFile, aStart, aLength);
+
+  nsresult rv = helper->Enqueue();
+  NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR);
+
+  nsCOMPtr<nsIInputStream>& result = helper->Result();
+  NS_ENSURE_TRUE(result, NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR);
+
+  result.forget(aResult);
+  return NS_OK;
+}
+
+bool
+FileHandle::CheckState(ErrorResult& aRv)
+{
+  if (!IsOpen()) {
+    aRv.Throw(NS_ERROR_DOM_FILEHANDLE_INACTIVE_ERR);
+    return false;
+  }
+
+  return true;
+}
+
+bool
+FileHandle::CheckStateAndArgumentsForRead(uint64_t aSize, ErrorResult& aRv)
+{
+  // Common state checking
+  if (!CheckState(aRv)) {
+    return false;
+  }
+
+  // Additional state checking for read
+  if (mLocation == UINT64_MAX) {
+    aRv.Throw(NS_ERROR_DOM_FILEHANDLE_NOT_ALLOWED_ERR);
+    return false;
+  }
+
+  // Argument checking for read
+  if (!aSize) {
+    aRv.ThrowTypeError(MSG_INVALID_READ_SIZE);
+    return false;
+  }
+
+  return true;
+}
+
+bool
+FileHandle::CheckStateForWrite(ErrorResult& aRv)
+{
+  // Common state checking
+  if (!CheckState(aRv)) {
+    return false;
+  }
+
+  // Additional state checking for write
+  if (mMode != FileMode::Readwrite) {
+    aRv.Throw(NS_ERROR_DOM_FILEHANDLE_READ_ONLY_ERR);
+    return false;
+  }
+
+  return true;
+}
+
+already_AddRefed<FileRequest>
+FileHandle::WriteInternal(nsIInputStream* aInputStream, uint64_t aInputLength,
+                          bool aAppend, ErrorResult& aRv)
+{
+  MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
+
+  DebugOnly<ErrorResult> error;
+  MOZ_ASSERT(CheckStateForWrite(error));
+  MOZ_ASSERT_IF(!aAppend, mLocation != UINT64_MAX);
+  MOZ_ASSERT(aInputStream);
+  MOZ_ASSERT(aInputLength);
+  MOZ_ASSERT(GetOwner());
+
+  nsRefPtr<FileRequest> fileRequest = GenerateFileRequest();
+
+  uint64_t location = aAppend ? UINT64_MAX : mLocation;
+
+  nsRefPtr<WriteHelper> helper =
+    new WriteHelper(this, fileRequest, location, aInputStream, aInputLength);
+
+  if (NS_WARN_IF(NS_FAILED(helper->Enqueue()))) {
+    aRv.Throw(NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR);
+    return nullptr;
+  }
+
+  if (aAppend) {
+    mLocation = UINT64_MAX;
+  }
+  else {
+    mLocation += aInputLength;
+  }
+
+  return fileRequest.forget();
+}
+
+nsresult
+FileHandle::Finish()
+{
+  MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
+
+  nsRefPtr<FinishHelper> helper(new FinishHelper(this));
+
+  FileService* service = FileService::Get();
+  MOZ_ASSERT(service, "This should never be null");
+
+  nsIEventTarget* target = service->StreamTransportTarget();
+
+  nsresult rv = target->Dispatch(helper, NS_DISPATCH_NORMAL);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  return NS_OK;
+}
+
+// static
+already_AddRefed<nsIInputStream>
+FileHandle::GetInputStream(const ArrayBuffer& aValue, uint64_t* aInputLength,
+                           ErrorResult& aRv)
+{
+  aValue.ComputeLengthAndData();
+  const char* data = reinterpret_cast<const char*>(aValue.Data());
+  uint32_t length = aValue.Length();
+
+  nsCOMPtr<nsIInputStream> stream;
+  aRv = NS_NewByteInputStream(getter_AddRefs(stream), data, length,
+                              NS_ASSIGNMENT_COPY);
+  if (aRv.Failed()) {
+    return nullptr;
+  }
+
+  *aInputLength = length;
+  return stream.forget();
+}
+
+// static
+already_AddRefed<nsIInputStream>
+FileHandle::GetInputStream(nsIDOMBlob* aValue, uint64_t* aInputLength,
+                           ErrorResult& aRv)
+{
+  uint64_t length;
+  aRv = aValue->GetSize(&length);
+  if (aRv.Failed()) {
+    return nullptr;
+  }
+
+  nsCOMPtr<nsIInputStream> stream;
+  aRv = aValue->GetInternalStream(getter_AddRefs(stream));
+  if (aRv.Failed()) {
+    return nullptr;
+  }
+
+  *aInputLength = length;
+  return stream.forget();
+}
+
+// static
+already_AddRefed<nsIInputStream>
+FileHandle::GetInputStream(const nsAString& aValue, uint64_t* aInputLength,
+                           ErrorResult& aRv)
+{
+  NS_ConvertUTF16toUTF8 cstr(aValue);
+
+  nsCOMPtr<nsIInputStream> stream;
+  aRv = NS_NewCStringInputStream(getter_AddRefs(stream), cstr);
+  if (aRv.Failed()) {
     return nullptr;
   }
 
-  nsRefPtr<FileRequest> request =
-    FileRequest::Create(GetOwner(), lockedFile, /* aWrapAsDOMRequest */ true);
+  *aInputLength = cstr.Length();
+  return stream.forget();
+}
 
-  nsRefPtr<MetadataParameters> params = new MetadataParameters(true, false);
+FinishHelper::FinishHelper(FileHandle* aFileHandle)
+: mFileHandle(aFileHandle),
+  mAborted(aFileHandle->mAborted)
+{
+  mParallelStreams.SwapElements(aFileHandle->mParallelStreams);
+  mStream.swap(aFileHandle->mStream);
+}
+
+NS_IMPL_ISUPPORTS(FinishHelper, nsIRunnable)
+
+NS_IMETHODIMP
+FinishHelper::Run()
+{
+  if (NS_IsMainThread()) {
+    mFileHandle->mReadyState = FileHandle::DONE;
 
-  nsRefPtr<GetFileHelper> helper =
-    new GetFileHelper(lockedFile, request, params, this);
+    FileService* service = FileService::Get();
+    if (service) {
+      service->NotifyFileHandleCompleted(mFileHandle);
+    }
 
-  nsresult rv = helper->Enqueue();
-  if (NS_FAILED(rv)) {
-    aError.Throw(NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR);
-    return nullptr;
+    nsCOMPtr<nsIDOMEvent> event;
+    if (mAborted) {
+      event = CreateGenericEvent(mFileHandle, NS_LITERAL_STRING("abort"),
+                                 true, false);
+    }
+    else {
+      event = CreateGenericEvent(mFileHandle, NS_LITERAL_STRING("complete"),
+                                 false, false);
+    }
+    NS_ENSURE_TRUE(event, NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR);
+
+    bool dummy;
+    if (NS_FAILED(mFileHandle->DispatchEvent(event, &dummy))) {
+      NS_WARNING("Dispatch failed!");
+    }
+
+    mFileHandle = nullptr;
+
+    return NS_OK;
+  }
+
+  if (mFileHandle->mMutableFile->IsInvalid()) {
+    mAborted = true;
   }
 
-  return request.forget();
+  for (uint32_t index = 0; index < mParallelStreams.Length(); index++) {
+    nsCOMPtr<nsIInputStream> stream =
+      do_QueryInterface(mParallelStreams[index]);
+
+    if (NS_FAILED(stream->Close())) {
+      NS_WARNING("Failed to close stream!");
+    }
+
+    mParallelStreams[index] = nullptr;
+  }
+
+  if (mStream) {
+    nsCOMPtr<nsIInputStream> stream = do_QueryInterface(mStream);
+
+    if (NS_FAILED(stream->Close())) {
+      NS_WARNING("Failed to close stream!");
+    }
+
+    mStream = nullptr;
+  }
+
+  return NS_DispatchToMainThread(this);
+}
+
+nsresult
+ReadHelper::Init()
+{
+  mStream = MemoryOutputStream::Create(mSize);
+  NS_ENSURE_TRUE(mStream, NS_ERROR_FAILURE);
+
+  return NS_OK;
+}
+
+nsresult
+ReadHelper::DoAsyncRun(nsISupports* aStream)
+{
+  MOZ_ASSERT(aStream, "Passed a null stream!");
+
+  uint32_t flags = FileStreamWrapper::NOTIFY_PROGRESS;
+
+  nsCOMPtr<nsIInputStream> istream =
+    new FileInputStreamWrapper(aStream, this, mLocation, mSize, flags);
+
+  FileService* service = FileService::Get();
+  MOZ_ASSERT(service, "This should never be null");
+
+  nsIEventTarget* target = service->StreamTransportTarget();
+
+  nsCOMPtr<nsIAsyncStreamCopier> copier;
+  nsresult rv =
+    NS_NewAsyncStreamCopier(getter_AddRefs(copier), istream, mStream, target,
+                            false, true, STREAM_COPY_BLOCK_SIZE);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  rv = copier->AsyncCopy(this, nullptr);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  mRequest = do_QueryInterface(copier);
+
+  return NS_OK;
 }
 
 nsresult
-GetFileHelper::GetSuccessResult(JSContext* aCx,
-                                JS::MutableHandle<JS::Value> aVal)
+ReadHelper::GetSuccessResult(JSContext* aCx,
+                             JS::MutableHandle<JS::Value> aVal)
+{
+  JS::Rooted<JSObject*> arrayBuffer(aCx);
+  nsresult rv =
+    nsContentUtils::CreateArrayBuffer(aCx, mStream->Data(), arrayBuffer.address());
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  aVal.setObject(*arrayBuffer);
+  return NS_OK;
+}
+
+nsresult
+ReadTextHelper::GetSuccessResult(JSContext* aCx,
+                                 JS::MutableHandle<JS::Value> aVal)
+{
+  nsAutoCString encoding;
+  const nsCString& data = mStream->Data();
+  // The BOM sniffing is baked into the "decode" part of the Encoding
+  // Standard, which the File API references.
+  if (!nsContentUtils::CheckForBOM(
+        reinterpret_cast<const unsigned char *>(data.get()),
+        data.Length(),
+        encoding)) {
+    // BOM sniffing failed. Try the API argument.
+    if (!EncodingUtils::FindEncodingForLabel(mEncoding, encoding)) {
+      // API argument failed. Since we are dealing with a file system file,
+      // we don't have a meaningful type attribute for the blob available,
+      // so proceeding to the next step, which is defaulting to UTF-8.
+      encoding.AssignLiteral("UTF-8");
+    }
+  }
+
+  nsString tmpString;
+  nsresult rv = nsContentUtils::ConvertStringFromEncoding(encoding, data,
+                                                          tmpString);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  if (!xpc::StringToJsval(aCx, tmpString, aVal)) {
+    NS_WARNING("Failed to convert string!");
+    return NS_ERROR_FAILURE;
+  }
+  return NS_OK;
+}
+
+nsresult
+WriteHelper::DoAsyncRun(nsISupports* aStream)
 {
-  nsCOMPtr<nsIDOMFile> domFile =
-    mFileHandle->CreateFileObject(mLockedFile, mParams->Size());
+  MOZ_ASSERT(aStream, "Passed a null stream!");
+
+  uint32_t flags = FileStreamWrapper::NOTIFY_PROGRESS;
+
+  nsCOMPtr<nsIOutputStream> ostream =
+    new FileOutputStreamWrapper(aStream, this, mLocation, mLength, flags);
+
+  FileService* service = FileService::Get();
+  MOZ_ASSERT(service, "This should never be null");
+
+  nsIEventTarget* target = service->StreamTransportTarget();
+
+  nsCOMPtr<nsIAsyncStreamCopier> copier;
+  nsresult rv =
+    NS_NewAsyncStreamCopier(getter_AddRefs(copier), mStream, ostream, target,
+                            true, false, STREAM_COPY_BLOCK_SIZE);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  rv = copier->AsyncCopy(this, nullptr);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  mRequest = do_QueryInterface(copier);
+
+  return NS_OK;
+}
+
+nsresult
+TruncateHelper::DoAsyncRun(nsISupports* aStream)
+{
+  MOZ_ASSERT(aStream, "Passed a null stream!");
+
+  nsRefPtr<AsyncTruncator> truncator = new AsyncTruncator(aStream, mOffset);
+
+  nsresult rv = truncator->AsyncWork(this, nullptr);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  return NS_OK;
+}
+
+nsresult
+TruncateHelper::AsyncTruncator::DoStreamWork(nsISupports* aStream)
+{
+  nsCOMPtr<nsISeekableStream> sstream = do_QueryInterface(aStream);
 
-  nsresult rv =
-    nsContentUtils::WrapNative(aCx, domFile, &NS_GET_IID(nsIDOMFile), aVal);
-  NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR);
+  nsresult rv = sstream->Seek(nsISeekableStream::NS_SEEK_SET, mOffset);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  rv = sstream->SetEOF();
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  return NS_OK;
+}
+
+nsresult
+FlushHelper::DoAsyncRun(nsISupports* aStream)
+{
+  MOZ_ASSERT(aStream, "Passed a null stream!");
+
+  nsRefPtr<AsyncFlusher> flusher = new AsyncFlusher(aStream);
+
+  nsresult rv = flusher->AsyncWork(this, nullptr);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  return NS_OK;
+}
+
+nsresult
+FlushHelper::AsyncFlusher::DoStreamWork(nsISupports* aStream)
+{
+  nsCOMPtr<nsIOutputStream> ostream = do_QueryInterface(aStream);
+
+  nsresult rv = ostream->Flush();
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  return NS_OK;
+}
+
+nsresult
+OpenStreamHelper::DoAsyncRun(nsISupports* aStream)
+{
+  MOZ_ASSERT(aStream, "Passed a null stream!");
+
+  uint32_t flags = FileStreamWrapper::NOTIFY_CLOSE |
+                   FileStreamWrapper::NOTIFY_DESTROY;
+
+  mStream = mWholeFile ?
+    new FileInputStreamWrapper(aStream, this, 0, mLength, flags) :
+    new FileInputStreamWrapper(aStream, this, mStart, mLength, flags);
+
   return NS_OK;
 }
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/filehandle/FileHandle.h
+++ b/dom/filehandle/FileHandle.h
@@ -3,156 +3,323 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_FileHandle_h
 #define mozilla_dom_FileHandle_h
 
 #include "js/TypeDecls.h"
+#include "MainThreadUtils.h"
+#include "mozilla/Assertions.h"
 #include "mozilla/Attributes.h"
+#include "mozilla/dom/BindingDeclarations.h"
 #include "mozilla/dom/FileModeBinding.h"
+#include "mozilla/dom/Nullable.h"
+#include "mozilla/dom/TypedArray.h"
 #include "mozilla/DOMEventTargetHelper.h"
+#include "mozilla/ErrorResult.h"
+#include "nsAutoPtr.h"
 #include "nsCOMPtr.h"
-#include "nsString.h"
+#include "nsCycleCollectionParticipant.h"
+#include "nsIInputStream.h"
+#include "nsIRunnable.h"
+#include "nsTArray.h"
 
-class nsIDOMFile;
-class nsIFile;
-class nsIOfflineStorage;
+class nsAString;
+class nsIDOMBlob;
 class nsPIDOMWindow;
 
 namespace mozilla {
 
-class ErrorResult;
+class EventChainPreVisitor;
 
 namespace dom {
 
-class DOMRequest;
-class FileService;
-class LockedFile;
-class FinishHelper;
+class DOMFileMetadataParameters;
 class FileHelper;
-
-namespace indexedDB {
-class FileInfo;
-} // namespace indexedDB
+class FileRequest;
+class FileService;
+class FinishHelper;
+class MetadataHelper;
+class MutableFile;
 
-/**
- * This class provides a default FileHandle implementation, but it can be also
- * subclassed. The subclass can override implementation of GetFileId,
- * GetFileInfo, IsShuttingDown, IsInvalid, CreateStream, SetThreadLocals,
- * UnsetThreadLocals and CreateFileObject.
- * (for example IDBFileHandle provides IndexedDB specific implementation).
- */
-class FileHandle : public DOMEventTargetHelper
+class FileHandle : public DOMEventTargetHelper,
+                   public nsIRunnable
 {
+  friend class FileHelper;
   friend class FileService;
-  friend class LockedFile;
   friend class FinishHelper;
-  friend class FileHelper;
+  friend class MetadataHelper;
 
 public:
-  const nsAString&
-  Name() const
+  NS_DECL_ISUPPORTS_INHERITED
+  NS_DECL_NSIRUNNABLE
+
+  NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(FileHandle, DOMEventTargetHelper)
+
+  enum RequestMode
+  {
+    NORMAL = 0, // Sequential
+    PARALLEL
+  };
+
+  enum ReadyState
   {
-    return mName;
-  }
+    INITIAL = 0,
+    LOADING,
+    FINISHING,
+    DONE
+  };
+
+  static already_AddRefed<FileHandle>
+  Create(MutableFile* aMutableFile,
+         FileMode aMode,
+         RequestMode aRequestMode = NORMAL);
 
-  const nsAString&
-  Type() const
+  // nsIDOMEventTarget
+  virtual nsresult
+  PreHandleEvent(EventChainPreVisitor& aVisitor) MOZ_OVERRIDE;
+
+  nsresult
+  CreateParallelStream(nsISupports** aStream);
+
+  nsresult
+  GetOrCreateStream(nsISupports** aStream);
+
+  bool
+  IsOpen() const;
+
+  bool
+  IsAborted() const
   {
-    return mType;
+    return mAborted;
   }
 
-  virtual int64_t
-  GetFileId()
-  {
-    return -1;
-  }
-
-  virtual mozilla::dom::indexedDB::FileInfo*
-  GetFileInfo()
+  MutableFile*
+  File() const
   {
-    return nullptr;
-  }
-
-  virtual bool
-  IsShuttingDown();
-
-  virtual bool
-  IsInvalid()
-  {
-    return false;
+    return mMutableFile;
   }
 
-  // A temporary method that will be removed along with nsIOfflineStorage
-  // interface.
-  virtual nsIOfflineStorage*
-  Storage() = 0;
-
-  virtual already_AddRefed<nsISupports>
-  CreateStream(nsIFile* aFile, bool aReadOnly);
+  nsresult
+  OpenInputStream(bool aWholeFile, uint64_t aStart, uint64_t aLength,
+                  nsIInputStream** aResult);
 
-  virtual void
-  SetThreadLocals()
-  {
-  }
-
-  virtual void
-  UnsetThreadLocals()
-  {
-  }
-
-  virtual already_AddRefed<nsIDOMFile>
-  CreateFileObject(LockedFile* aLockedFile, uint32_t aFileSize);
-
-  // nsWrapperCache
+  // WrapperCache
   virtual JSObject*
   WrapObject(JSContext* aCx) MOZ_OVERRIDE;
 
   // WebIDL
   nsPIDOMWindow*
   GetParentObject() const
   {
     return GetOwner();
   }
 
-  void
-  GetName(nsString& aName) const
+  MutableFile*
+  GetMutableFile() const
+  {
+    MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
+
+    return File();
+  }
+
+  MutableFile*
+  GetFileHandle() const
+  {
+    return GetMutableFile();
+  }
+
+  FileMode
+  Mode() const
   {
-    aName = mName;
+    MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
+
+    return mMode;
+  }
+
+  bool
+  Active() const
+  {
+    MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
+
+    return IsOpen();
+  }
+
+  Nullable<uint64_t>
+  GetLocation() const
+  {
+    MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
+
+    if (mLocation == UINT64_MAX) {
+      return Nullable<uint64_t>();
+    }
+
+    return Nullable<uint64_t>(mLocation);
   }
 
   void
-  GetType(nsString& aType) const
+  SetLocation(const Nullable<uint64_t>& aLocation)
   {
-    aType = mType;
+    MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
+
+    // Null means the end-of-file.
+    if (aLocation.IsNull()) {
+      mLocation = UINT64_MAX;
+    } else {
+      mLocation = aLocation.Value();
+    }
   }
 
-  already_AddRefed<LockedFile>
-  Open(FileMode aMode, ErrorResult& aError);
+  already_AddRefed<FileRequest>
+  GetMetadata(const DOMFileMetadataParameters& aParameters, ErrorResult& aRv);
+
+  already_AddRefed<FileRequest>
+  ReadAsArrayBuffer(uint64_t aSize, ErrorResult& aRv);
+
+  already_AddRefed<FileRequest>
+  ReadAsText(uint64_t aSize, const nsAString& aEncoding, ErrorResult& aRv);
+
+  template<class T>
+  already_AddRefed<FileRequest>
+  Write(const T& aValue, ErrorResult& aRv)
+  {
+    MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
+
+    return WriteOrAppend(aValue, false, aRv);
+  }
 
-  already_AddRefed<DOMRequest>
-  GetFile(ErrorResult& aError);
+  template<class T>
+  already_AddRefed<FileRequest>
+  Append(const T& aValue, ErrorResult& aRv)
+  {
+    MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
+
+    return WriteOrAppend(aValue, true, aRv);
+  }
 
+  already_AddRefed<FileRequest>
+  Truncate(const Optional<uint64_t>& aSize, ErrorResult& aRv);
+
+  already_AddRefed<FileRequest>
+  Flush(ErrorResult& aRv);
+
+  void
+  Abort(ErrorResult& aRv);
+
+  IMPL_EVENT_HANDLER(complete)
   IMPL_EVENT_HANDLER(abort)
   IMPL_EVENT_HANDLER(error)
 
-protected:
-  FileHandle(nsPIDOMWindow* aWindow);
+private:
+  FileHandle();
+  ~FileHandle();
+
+  void
+  OnNewRequest();
+
+  void
+  OnRequestFinished();
+
+  bool
+  CheckState(ErrorResult& aRv);
+
+  bool
+  CheckStateAndArgumentsForRead(uint64_t aSize, ErrorResult& aRv);
+
+  bool
+  CheckStateForWrite(ErrorResult& aRv);
+
+  already_AddRefed<FileRequest>
+  GenerateFileRequest();
 
-  FileHandle(DOMEventTargetHelper* aOwner);
+  template<class T>
+  already_AddRefed<FileRequest>
+  WriteOrAppend(const T& aValue, bool aAppend, ErrorResult& aRv)
+  {
+    MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
+
+    // State checking for write
+    if (!CheckStateForWrite(aRv)) {
+      return nullptr;
+    }
 
-  virtual ~FileHandle();
+    // Additional state checking for write
+    if (!aAppend && mLocation == UINT64_MAX) {
+      aRv.Throw(NS_ERROR_DOM_FILEHANDLE_NOT_ALLOWED_ERR);
+      return nullptr;
+    }
+
+    uint64_t length;
+    nsCOMPtr<nsIInputStream> stream = GetInputStream(aValue, &length, aRv);
+    if (aRv.Failed()) {
+      return nullptr;
+    }
+
+    if (!length) {
+      return nullptr;
+    }
+
+    // Do nothing if the window is closed
+    if (!GetOwner()) {
+      return nullptr;
+    }
 
-  nsString mName;
-  nsString mType;
+    return WriteInternal(stream, length, aAppend, aRv);
+  }
+
+  already_AddRefed<FileRequest>
+  WriteInternal(nsIInputStream* aInputStream, uint64_t aInputLength,
+                bool aAppend, ErrorResult& aRv);
+
+  nsresult
+  Finish();
+
+  static already_AddRefed<nsIInputStream>
+  GetInputStream(const ArrayBuffer& aValue, uint64_t* aInputLength,
+                 ErrorResult& aRv);
+
+  static already_AddRefed<nsIInputStream>
+  GetInputStream(nsIDOMBlob* aValue, uint64_t* aInputLength, ErrorResult& aRv);
+
+  static already_AddRefed<nsIInputStream>
+  GetInputStream(const nsAString& aValue, uint64_t* aInputLength,
+                 ErrorResult& aRv);
 
-  nsCOMPtr<nsIFile> mFile;
+  nsRefPtr<MutableFile> mMutableFile;
+  ReadyState mReadyState;
+  FileMode mMode;
+  RequestMode mRequestMode;
+  uint64_t mLocation;
+  uint32_t mPendingRequests;
+
+  nsTArray<nsCOMPtr<nsISupports>> mParallelStreams;
+  nsCOMPtr<nsISupports> mStream;
+
+  bool mAborted;
+  bool mCreating;
+};
 
-  nsCString mStorageId;
-  nsString mFileName;
+class FinishHelper MOZ_FINAL : public nsIRunnable
+{
+  friend class FileHandle;
+
+public:
+  NS_DECL_THREADSAFE_ISUPPORTS
+  NS_DECL_NSIRUNNABLE
+
+private:
+  FinishHelper(FileHandle* aFileHandle);
+  ~FinishHelper()
+  { }
+
+  nsRefPtr<FileHandle> mFileHandle;
+  nsTArray<nsCOMPtr<nsISupports>> mParallelStreams;
+  nsCOMPtr<nsISupports> mStream;
+
+  bool mAborted;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_FileHandle_h
--- a/dom/filehandle/FileHelper.cpp
+++ b/dom/filehandle/FileHelper.cpp
@@ -5,64 +5,64 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "FileHelper.h"
 
 #include "FileHandle.h"
 #include "FileRequest.h"
 #include "FileService.h"
 #include "js/Value.h"
-#include "LockedFile.h"
 #include "MainThreadUtils.h"
 #include "mozilla/Assertions.h"
+#include "MutableFile.h"
 #include "nsDebug.h"
 #include "nsError.h"
 #include "nsIRequest.h"
 
 namespace mozilla {
 namespace dom {
 
 namespace {
 
-LockedFile* gCurrentLockedFile = nullptr;
+FileHandle* gCurrentFileHandle = nullptr;
 
 } // anonymous namespace
 
-FileHelper::FileHelper(LockedFile* aLockedFile,
+FileHelper::FileHelper(FileHandle* aFileHandle,
                        FileRequest* aFileRequest)
-: mFileHandle(aLockedFile->mFileHandle),
-  mLockedFile(aLockedFile),
+: mMutableFile(aFileHandle->mMutableFile),
+  mFileHandle(aFileHandle),
   mFileRequest(aFileRequest),
   mResultCode(NS_OK),
   mFinished(false)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 }
 
 FileHelper::~FileHelper()
 {
-  MOZ_ASSERT(!mFileHandle && !mLockedFile && !mFileRequest && !mListener &&
+  MOZ_ASSERT(!mMutableFile && !mFileHandle && !mFileRequest && !mListener &&
              !mRequest, "Should have cleared this!");
 }
 
 NS_IMPL_ISUPPORTS(FileHelper, nsIRequestObserver)
 
 nsresult
 FileHelper::Enqueue()
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
   FileService* service = FileService::GetOrCreate();
   NS_ENSURE_TRUE(service, NS_ERROR_FAILURE);
 
-  nsresult rv = service->Enqueue(mLockedFile, this);
+  nsresult rv = service->Enqueue(mFileHandle, this);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  if (mLockedFile) {
-    mLockedFile->OnNewRequest();
+  if (mFileHandle) {
+    mFileHandle->OnNewRequest();
   }
 
   return NS_OK;
 }
 
 nsresult
 FileHelper::AsyncRun(FileHelperListener* aListener)
 {
@@ -70,21 +70,21 @@ FileHelper::AsyncRun(FileHelperListener*
 
   // Assign the listener early, so we can use it synchronously if the code
   // below fails.
   mListener = aListener;
 
   nsresult rv;
 
   nsCOMPtr<nsISupports> stream;
-  if (mLockedFile->mRequestMode == LockedFile::PARALLEL) {
-    rv = mLockedFile->CreateParallelStream(getter_AddRefs(stream));
+  if (mFileHandle->mRequestMode == FileHandle::PARALLEL) {
+    rv = mFileHandle->CreateParallelStream(getter_AddRefs(stream));
   }
   else {
-    rv = mLockedFile->GetOrCreateStream(getter_AddRefs(stream));
+    rv = mFileHandle->GetOrCreateStream(getter_AddRefs(stream));
   }
 
   if (NS_SUCCEEDED(rv)) {
     NS_ASSERTION(stream, "This should never be null!");
 
     rv = DoAsyncRun(stream);
   }
 
@@ -124,39 +124,39 @@ FileHelper::OnStopRequest(nsIRequest* aR
   return NS_OK;
 }
 
 void
 FileHelper::OnStreamProgress(uint64_t aProgress, uint64_t aProgressMax)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
-  if (mLockedFile->IsAborted()) {
+  if (mFileHandle->IsAborted()) {
     NS_ASSERTION(mRequest, "Should have a request!\n");
 
     nsresult rv = mRequest->Cancel(NS_BINDING_ABORTED);
     if (NS_FAILED(rv)) {
       NS_WARNING("Failed to cancel the request!");
     }
 
     return;
   }
 
   if (mFileRequest) {
     mFileRequest->OnProgress(aProgress, aProgressMax);
   }
 }
 
 // static
-LockedFile*
-FileHelper::GetCurrentLockedFile()
+FileHandle*
+FileHelper::GetCurrentFileHandle()
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
-  return gCurrentLockedFile;
+  return gCurrentFileHandle;
 }
 
 nsresult
 FileHelper::GetSuccessResult(JSContext* aCx,
                              JS::MutableHandle<JS::Value> aVal)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
@@ -164,60 +164,60 @@ FileHelper::GetSuccessResult(JSContext* 
   return NS_OK;
 }
 
 void
 FileHelper::ReleaseObjects()
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
+  mMutableFile = nullptr;
   mFileHandle = nullptr;
-  mLockedFile = nullptr;
   mFileRequest = nullptr;
   mListener = nullptr;
   mRequest = nullptr;
 }
 
 void
 FileHelper::Finish()
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
   if (mFinished) {
     return;
   }
 
   mFinished = true;
 
-  if (mLockedFile->IsAborted()) {
+  if (mFileHandle->IsAborted()) {
     // Always fire a "error" event with ABORT_ERR if the transaction was
     // aborted, even if the request succeeded or failed with another error.
     mResultCode = NS_ERROR_DOM_FILEHANDLE_ABORT_ERR;
   }
 
-  LockedFile* oldLockedFile = gCurrentLockedFile;
-  gCurrentLockedFile = mLockedFile;
+  FileHandle* oldFileHandle = gCurrentFileHandle;
+  gCurrentFileHandle = mFileHandle;
 
   if (mFileRequest) {
     nsresult rv = mFileRequest->NotifyHelperCompleted(this);
     if (NS_SUCCEEDED(mResultCode) && NS_FAILED(rv)) {
       mResultCode = rv;
     }
   }
 
-  NS_ASSERTION(gCurrentLockedFile == mLockedFile, "Should be unchanged!");
-  gCurrentLockedFile = oldLockedFile;
+  MOZ_ASSERT(gCurrentFileHandle == mFileHandle, "Should be unchanged!");
+  gCurrentFileHandle = oldFileHandle;
 
-  mLockedFile->OnRequestFinished();
+  mFileHandle->OnRequestFinished();
 
   mListener->OnFileHelperComplete(this);
 
   ReleaseObjects();
 
-  MOZ_ASSERT(!(mFileHandle || mLockedFile || mFileRequest || mListener ||
+  MOZ_ASSERT(!(mMutableFile || mFileHandle || mFileRequest || mListener ||
                mRequest), "Subclass didn't call FileHelper::ReleaseObjects!");
 
 }
 
 void
 FileHelper::OnStreamClose()
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
--- a/dom/filehandle/FileHelper.h
+++ b/dom/filehandle/FileHelper.h
@@ -10,21 +10,21 @@
 #include "js/TypeDecls.h"
 #include "nsAutoPtr.h"
 #include "nsCOMPtr.h"
 #include "nsIRequestObserver.h"
 
 namespace mozilla {
 namespace dom {
 
+class FileHandle;
 class FileHelper;
-class FileHandle;
 class FileRequest;
 class FileOutputStreamWrapper;
-class LockedFile;
+class MutableFile;
 
 class FileHelperListener
 {
 public:
   NS_IMETHOD_(MozExternalRefCountType)
   AddRef() = 0;
 
   NS_IMETHOD_(MozExternalRefCountType)
@@ -58,38 +58,38 @@ public:
   OnStreamProgress(uint64_t aProgress, uint64_t aProgressMax);
 
   void
   OnStreamClose();
 
   void
   OnStreamDestroy();
 
-  static LockedFile*
-  GetCurrentLockedFile();
+  static FileHandle*
+  GetCurrentFileHandle();
 
 protected:
-  FileHelper(LockedFile* aLockedFile, FileRequest* aRequest);
+  FileHelper(FileHandle* aFileHandle, FileRequest* aRequest);
 
   virtual ~FileHelper();
 
   virtual nsresult
   DoAsyncRun(nsISupports* aStream) = 0;
 
   virtual nsresult
   GetSuccessResult(JSContext* aCx, JS::MutableHandle<JS::Value> aVal);
 
   virtual void
   ReleaseObjects();
 
   void
   Finish();
 
+  nsRefPtr<MutableFile> mMutableFile;
   nsRefPtr<FileHandle> mFileHandle;
-  nsRefPtr<LockedFile> mLockedFile;
   nsRefPtr<FileRequest> mFileRequest;
 
   nsRefPtr<FileHelperListener> mListener;
   nsCOMPtr<nsIRequest> mRequest;
 
 private:
   nsresult mResultCode;
   bool mFinished;
--- a/dom/filehandle/FileRequest.cpp
+++ b/dom/filehandle/FileRequest.cpp
@@ -1,20 +1,20 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "FileRequest.h"
 
+#include "FileHandle.h"
 #include "FileHelper.h"
 #include "js/RootingAPI.h"
 #include "jsapi.h"
-#include "LockedFile.h"
 #include "MainThreadUtils.h"
 #include "mozilla/dom/FileRequestBinding.h"
 #include "mozilla/EventDispatcher.h"
 #include "nsCOMPtr.h"
 #include "nsCxPusher.h"
 #include "nsDebug.h"
 #include "nsError.h"
 #include "nsIDOMEvent.h"
@@ -33,35 +33,35 @@ FileRequest::FileRequest(nsPIDOMWindow* 
 
 FileRequest::~FileRequest()
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 }
 
 // static
 already_AddRefed<FileRequest>
-FileRequest::Create(nsPIDOMWindow* aOwner, LockedFile* aLockedFile,
+FileRequest::Create(nsPIDOMWindow* aOwner, FileHandle* aFileHandle,
                     bool aWrapAsDOMRequest)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
   nsRefPtr<FileRequest> request = new FileRequest(aOwner);
-  request->mLockedFile = aLockedFile;
+  request->mFileHandle = aFileHandle;
   request->mWrapAsDOMRequest = aWrapAsDOMRequest;
 
   return request.forget();
 }
 
 nsresult
 FileRequest::PreHandleEvent(EventChainPreVisitor& aVisitor)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
   aVisitor.mCanHandle = true;
-  aVisitor.mParentTarget = mLockedFile;
+  aVisitor.mParentTarget = mFileHandle;
   return NS_OK;
 }
 
 nsresult
 FileRequest::NotifyHelperCompleted(FileHelper* aFileHelper)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
@@ -98,17 +98,17 @@ FileRequest::NotifyHelperCompleted(FileH
   else {
     FireError(rv);
   }
 
   return NS_OK;
 }
 
 NS_IMPL_CYCLE_COLLECTION_INHERITED(FileRequest, DOMRequest,
-                                   mLockedFile)
+                                   mFileHandle)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(FileRequest)
 NS_INTERFACE_MAP_END_INHERITING(DOMRequest)
 
 NS_IMPL_ADDREF_INHERITED(FileRequest, DOMRequest)
 NS_IMPL_RELEASE_INHERITED(FileRequest, DOMRequest)
 
 // virtual
@@ -116,21 +116,21 @@ JSObject*
 FileRequest::WrapObject(JSContext* aCx)
 {
   if (mWrapAsDOMRequest) {
     return DOMRequest::WrapObject(aCx);
   }
   return FileRequestBinding::Wrap(aCx, this);
 }
 
-LockedFile*
-FileRequest::GetLockedFile() const
+FileHandle*
+FileRequest::GetFileHandle() const
 {
   MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
-  return mLockedFile;
+  return mFileHandle;
 }
 
 void
 FileRequest::FireProgressEvent(uint64_t aLoaded, uint64_t aTotal)
 {
   if (NS_FAILED(CheckInnerWindowCorrectness())) {
     return;
   }
--- a/dom/filehandle/FileRequest.h
+++ b/dom/filehandle/FileRequest.h
@@ -16,28 +16,28 @@
 class nsPIDOMWindow;
 
 namespace mozilla {
 
 class EventChainPreVisitor;
 
 namespace dom {
 
+class FileHandle;
 class FileHelper;
-class LockedFile;
 
 class FileRequest : public DOMRequest
 {
 public:
   NS_DECL_ISUPPORTS_INHERITED
 
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(FileRequest, DOMRequest)
 
   static already_AddRefed<FileRequest>
-  Create(nsPIDOMWindow* aOwner, LockedFile* aLockedFile,
+  Create(nsPIDOMWindow* aOwner, FileHandle* aFileHandle,
          bool aWrapAsDOMRequest);
 
   // nsIDOMEventTarget
   virtual nsresult
   PreHandleEvent(EventChainPreVisitor& aVisitor) MOZ_OVERRIDE;
 
   void
   OnProgress(uint64_t aProgress, uint64_t aProgressMax)
@@ -48,29 +48,35 @@ public:
   nsresult
   NotifyHelperCompleted(FileHelper* aFileHelper);
 
   // nsWrapperCache
   virtual JSObject*
   WrapObject(JSContext* aCx) MOZ_OVERRIDE;
 
   // WebIDL
-  LockedFile*
-  GetLockedFile() const;
+  FileHandle*
+  GetFileHandle() const;
+
+  FileHandle*
+  GetLockedFile() const
+  {
+    return GetFileHandle();
+  }
 
   IMPL_EVENT_HANDLER(progress)
 
 protected:
   FileRequest(nsPIDOMWindow* aWindow);
   ~FileRequest();
 
   void
   FireProgressEvent(uint64_t aLoaded, uint64_t aTotal);
 
-  nsRefPtr<LockedFile> mLockedFile;
+  nsRefPtr<FileHandle> mFileHandle;
 
   bool mWrapAsDOMRequest;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_FileRequest_h
--- a/dom/filehandle/FileService.cpp
+++ b/dom/filehandle/FileService.cpp
@@ -2,19 +2,19 @@
 /* vim: set ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "FileService.h"
 
 #include "FileHandle.h"
-#include "LockedFile.h"
 #include "MainThreadUtils.h"
 #include "mozilla/Assertions.h"
+#include "MutableFile.h"
 #include "nsError.h"
 #include "nsIEventTarget.h"
 #include "nsIObserverService.h"
 #include "nsIOfflineStorage.h"
 #include "nsNetCID.h"
 #include "nsServiceManagerUtils.h"
 #include "nsThreadUtils.h"
 
@@ -141,45 +141,45 @@ FileService::Shutdown()
 // static
 bool
 FileService::IsShuttingDown()
 {
   return gShutdown;
 }
 
 nsresult
-FileService::Enqueue(LockedFile* aLockedFile, FileHelper* aFileHelper)
+FileService::Enqueue(FileHandle* aFileHandle, FileHelper* aFileHelper)
 {
-  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
-  NS_ASSERTION(aLockedFile, "Null pointer!");
+  MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
+  MOZ_ASSERT(aFileHandle, "Null pointer!");
 
-  FileHandle* fileHandle = aLockedFile->mFileHandle;
+  MutableFile* mutableFile = aFileHandle->mMutableFile;
 
-  if (fileHandle->IsInvalid()) {
+  if (mutableFile->IsInvalid()) {
     return NS_ERROR_NOT_AVAILABLE;
   }
 
-  const nsACString& storageId = fileHandle->mStorageId;
-  const nsAString& fileName = fileHandle->mFileName;
-  bool modeIsWrite = aLockedFile->mMode == FileMode::Readwrite;
+  const nsACString& storageId = mutableFile->mStorageId;
+  const nsAString& fileName = mutableFile->mFileName;
+  bool modeIsWrite = aFileHandle->mMode == FileMode::Readwrite;
 
   StorageInfo* storageInfo;
   if (!mStorageInfos.Get(storageId, &storageInfo)) {
     nsAutoPtr<StorageInfo> newStorageInfo(new StorageInfo());
 
     mStorageInfos.Put(storageId, newStorageInfo);
 
     storageInfo = newStorageInfo.forget();
   }
 
-  LockedFileQueue* existingLockedFileQueue =
-    storageInfo->GetLockedFileQueue(aLockedFile);
+  FileHandleQueue* existingFileHandleQueue =
+    storageInfo->GetFileHandleQueue(aFileHandle);
 
-  if (existingLockedFileQueue) {
-    existingLockedFileQueue->Enqueue(aFileHelper);
+  if (existingFileHandleQueue) {
+    existingFileHandleQueue->Enqueue(aFileHelper);
     return NS_OK;
   }
 
   bool lockedForReading = storageInfo->IsFileLockedForReading(fileName);
   bool lockedForWriting = storageInfo->IsFileLockedForWriting(fileName);
 
   if (modeIsWrite) {
     if (!lockedForWriting) {
@@ -188,61 +188,61 @@ FileService::Enqueue(LockedFile* aLocked
   }
   else {
     if (!lockedForReading) {
       storageInfo->LockFileForReading(fileName);
     }
   }
 
   if (lockedForWriting || (lockedForReading && modeIsWrite)) {
-    storageInfo->CreateDelayedEnqueueInfo(aLockedFile, aFileHelper);
+    storageInfo->CreateDelayedEnqueueInfo(aFileHandle, aFileHelper);
   }
   else {
-    LockedFileQueue* lockedFileQueue =
-      storageInfo->CreateLockedFileQueue(aLockedFile);
+    FileHandleQueue* fileHandleQueue =
+      storageInfo->CreateFileHandleQueue(aFileHandle);
 
     if (aFileHelper) {
       // Enqueue() will queue the file helper if there's already something
       // running. That can't fail, so no need to eventually remove
       // storageInfo from the hash table.
       //
       // If the file helper is free to run then AsyncRun() is called on the
       // file helper. AsyncRun() is responsible for calling all necessary
       // callbacks when something fails. We're propagating the error here,
       // however there's no need to eventually remove storageInfo from
       // the hash table. Code behind AsyncRun() will take care of it. The last
-      // item in the code path is NotifyLockedFileCompleted() which removes
-      // storageInfo from the hash table if there are no locked files for
+      // item in the code path is NotifyFileHandleCompleted() which removes
+      // storageInfo from the hash table if there are no file handles for
       // the file storage.
-      nsresult rv = lockedFileQueue->Enqueue(aFileHelper);
+      nsresult rv = fileHandleQueue->Enqueue(aFileHelper);
       NS_ENSURE_SUCCESS(rv, rv);
     }
   }
 
   return NS_OK;
 }
 
 void
-FileService::NotifyLockedFileCompleted(LockedFile* aLockedFile)
+FileService::NotifyFileHandleCompleted(FileHandle* aFileHandle)
 {
-  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
-  NS_ASSERTION(aLockedFile, "Null pointer!");
+  MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
+  MOZ_ASSERT(aFileHandle, "Null pointer!");
 
-  FileHandle* fileHandle = aLockedFile->mFileHandle;
-  const nsACString& storageId = fileHandle->mStorageId;
+  MutableFile* mutableFile = aFileHandle->mMutableFile;
+  const nsACString& storageId = mutableFile->mStorageId;
 
   StorageInfo* storageInfo;
   if (!mStorageInfos.Get(storageId, &storageInfo)) {
-    NS_ERROR("We don't know anyting about this locked file?!");
+    NS_ERROR("We don't know anyting about this file handle?!");
     return;
   }
 
-  storageInfo->RemoveLockedFileQueue(aLockedFile);
+  storageInfo->RemoveFileHandleQueue(aFileHandle);
 
-  if (!storageInfo->HasRunningLockedFiles()) {
+  if (!storageInfo->HasRunningFileHandles()) {
     mStorageInfos.Remove(storageId);
 
     // See if we need to fire any complete callbacks.
     uint32_t index = 0;
     while (index < mCompleteCallbacks.Length()) {
       if (MaybeFireCallback(mCompleteCallbacks[index])) {
         mCompleteCallbacks.RemoveElementAt(index);
       }
@@ -267,47 +267,47 @@ FileService::WaitForStoragesToComplete(
   callback->mStorages.SwapElements(aStorages);
 
   if (MaybeFireCallback(*callback)) {
     mCompleteCallbacks.RemoveElementAt(mCompleteCallbacks.Length() - 1);
   }
 }
 
 void
-FileService::AbortLockedFilesForStorage(nsIOfflineStorage* aStorage)
+FileService::AbortFileHandlesForStorage(nsIOfflineStorage* aStorage)
 {
   MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
   MOZ_ASSERT(aStorage, "Null pointer!");
 
   StorageInfo* storageInfo;
   if (!mStorageInfos.Get(aStorage->Id(), &storageInfo)) {
     return;
   }
 
-  nsAutoTArray<nsRefPtr<LockedFile>, 10> lockedFiles;
-  storageInfo->CollectRunningAndDelayedLockedFiles(aStorage, lockedFiles);
+  nsAutoTArray<nsRefPtr<FileHandle>, 10> fileHandles;
+  storageInfo->CollectRunningAndDelayedFileHandles(aStorage, fileHandles);
 
-  for (uint32_t index = 0; index < lockedFiles.Length(); index++) {
+  for (uint32_t index = 0; index < fileHandles.Length(); index++) {
     ErrorResult ignored;
-    lockedFiles[index]->Abort(ignored);
+    fileHandles[index]->Abort(ignored);
   }
 }
 
 bool
-FileService::HasLockedFilesForStorage(nsIOfflineStorage* aStorage)
+FileService::HasFileHandlesForStorage(nsIOfflineStorage* aStorage)
 {
   MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
   MOZ_ASSERT(aStorage, "Null pointer!");
 
   StorageInfo* storageInfo;
   if (!mStorageInfos.Get(aStorage->Id(), &storageInfo)) {
     return false;
   }
 
-  return storageInfo->HasRunningLockedFiles(aStorage);
+  return storageInfo->HasRunningFileHandles(aStorage);
 }
 
 NS_IMPL_ISUPPORTS(FileService, nsIObserver)
 
 NS_IMETHODIMP
 FileService::Observe(nsISupports* aSubject, const char*  aTopic,
                      const char16_t* aData)
 {
@@ -329,46 +329,47 @@ FileService::MaybeFireCallback(StoragesC
       return false;
     }
   }
 
   aCallback.mCallback->Run();
   return true;
 }
 
-FileService::LockedFileQueue::LockedFileQueue(LockedFile* aLockedFile)
-: mLockedFile(aLockedFile)
+FileService::FileHandleQueue::FileHandleQueue(FileHandle* aFileHandle)
+: mFileHandle(aFileHandle)
 {
-  NS_ASSERTION(aLockedFile, "Null pointer!");
+  MOZ_ASSERT(aFileHandle, "Null pointer!");
 }
 
-NS_IMPL_ADDREF(FileService::LockedFileQueue)
-NS_IMPL_RELEASE(FileService::LockedFileQueue)
+NS_IMPL_ADDREF(FileService::FileHandleQueue)
+NS_IMPL_RELEASE(FileService::FileHandleQueue)
 
 nsresult
-FileService::LockedFileQueue::Enqueue(FileHelper* aFileHelper)
+FileService::FileHandleQueue::Enqueue(FileHelper* aFileHelper)
 {
   mQueue.AppendElement(aFileHelper);
 
   nsresult rv;
-  if (mLockedFile->mRequestMode == LockedFile::PARALLEL) {
+  if (mFileHandle->mRequestMode == FileHandle::PARALLEL) {
     rv = aFileHelper->AsyncRun(this);
   }
   else {
     rv = ProcessQueue();
   }
   NS_ENSURE_SUCCESS(rv, rv);
 
   return NS_OK;
 }
 
 void
-FileService::LockedFileQueue::OnFileHelperComplete(FileHelper* aFileHelper)
+FileService::
+FileHandleQueue::OnFileHelperComplete(FileHelper* aFileHelper)
 {
-  if (mLockedFile->mRequestMode == LockedFile::PARALLEL) {
+  if (mFileHandle->mRequestMode == FileHandle::PARALLEL) {
     int32_t index = mQueue.IndexOf(aFileHelper);
     NS_ASSERTION(index != -1, "We don't know anything about this helper!");
 
     mQueue.RemoveElementAt(index);
   }
   else {
     NS_ASSERTION(mCurrentHelper == aFileHelper, "How can this happen?!");
 
@@ -377,17 +378,17 @@ FileService::LockedFileQueue::OnFileHelp
     nsresult rv = ProcessQueue();
     if (NS_FAILED(rv)) {
       return;
     }
   }
 }
 
 nsresult
-FileService::LockedFileQueue::ProcessQueue()
+FileService::FileHandleQueue::ProcessQueue()
 {
   if (mQueue.IsEmpty() || mCurrentHelper) {
     return NS_OK;
   }
 
   mCurrentHelper = mQueue[0];
   mQueue.RemoveElementAt(0);
 
@@ -400,135 +401,135 @@ FileService::LockedFileQueue::ProcessQue
 FileService::DelayedEnqueueInfo::DelayedEnqueueInfo()
 {
 }
 
 FileService::DelayedEnqueueInfo::~DelayedEnqueueInfo()
 {
 }
 
-FileService::LockedFileQueue*
-FileService::StorageInfo::CreateLockedFileQueue(LockedFile* aLockedFile)
+FileService::FileHandleQueue*
+FileService::StorageInfo::CreateFileHandleQueue(FileHandle* aFileHandle)
 {
-  nsRefPtr<LockedFileQueue>* lockedFileQueue =
-    mLockedFileQueues.AppendElement();
-  *lockedFileQueue = new LockedFileQueue(aLockedFile);
-  return lockedFileQueue->get();
+  nsRefPtr<FileHandleQueue>* fileHandleQueue =
+    mFileHandleQueues.AppendElement();
+  *fileHandleQueue = new FileHandleQueue(aFileHandle);
+  return fileHandleQueue->get();
 }
 
-FileService::LockedFileQueue*
-FileService::StorageInfo::GetLockedFileQueue(LockedFile* aLockedFile)
+FileService::FileHandleQueue*
+FileService::StorageInfo::GetFileHandleQueue(FileHandle* aFileHandle)
 {
-  uint32_t count = mLockedFileQueues.Length();
+  uint32_t count = mFileHandleQueues.Length();
   for (uint32_t index = 0; index < count; index++) {
-    nsRefPtr<LockedFileQueue>& lockedFileQueue = mLockedFileQueues[index];
-    if (lockedFileQueue->mLockedFile == aLockedFile) {
-      return lockedFileQueue;
+    nsRefPtr<FileHandleQueue>& fileHandleQueue = mFileHandleQueues[index];
+    if (fileHandleQueue->mFileHandle == aFileHandle) {
+      return fileHandleQueue;
     }
   }
   return nullptr;
 }
 
 void
-FileService::StorageInfo::RemoveLockedFileQueue(LockedFile* aLockedFile)
+FileService::StorageInfo::RemoveFileHandleQueue(FileHandle* aFileHandle)
 {
   for (uint32_t index = 0; index < mDelayedEnqueueInfos.Length(); index++) {
-    if (mDelayedEnqueueInfos[index].mLockedFile == aLockedFile) {
-      NS_ASSERTION(!mDelayedEnqueueInfos[index].mFileHelper, "Should be null!");
+    if (mDelayedEnqueueInfos[index].mFileHandle == aFileHandle) {
+      MOZ_ASSERT(!mDelayedEnqueueInfos[index].mFileHelper, "Should be null!");
       mDelayedEnqueueInfos.RemoveElementAt(index);
       return;
     }
   }
 
-  uint32_t lockedFileCount = mLockedFileQueues.Length();
+  uint32_t fileHandleCount = mFileHandleQueues.Length();
 
   // We can't just remove entries from lock hash tables, we have to rebuild
-  // them instead. Multiple LockedFile objects may lock the same file
+  // them instead. Multiple FileHandle objects may lock the same file
   // (one entry can represent multiple locks).
 
   mFilesReading.Clear();
   mFilesWriting.Clear();
 
-  for (uint32_t index = 0, count = lockedFileCount; index < count; index++) {
-    LockedFile* lockedFile = mLockedFileQueues[index]->mLockedFile;
-    if (lockedFile == aLockedFile) {
-      NS_ASSERTION(count == lockedFileCount, "More than one match?!");
+  for (uint32_t index = 0, count = fileHandleCount; index < count; index++) {
+    FileHandle* fileHandle = mFileHandleQueues[index]->mFileHandle;
+    if (fileHandle == aFileHandle) {
+      MOZ_ASSERT(count == fileHandleCount, "More than one match?!");
 
-      mLockedFileQueues.RemoveElementAt(index);
+      mFileHandleQueues.RemoveElementAt(index);
       index--;
       count--;
 
       continue;
     }
 
-    const nsAString& fileName = lockedFile->mFileHandle->mFileName;
+    const nsAString& fileName = fileHandle->mMutableFile->mFileName;
 
-    if (lockedFile->mMode == FileMode::Readwrite) {
+    if (fileHandle->mMode == FileMode::Readwrite) {
       if (!IsFileLockedForWriting(fileName)) {
         LockFileForWriting(fileName);
       }
     }
     else {
       if (!IsFileLockedForReading(fileName)) {
         LockFileForReading(fileName);
       }
     }
   }
 
-  NS_ASSERTION(mLockedFileQueues.Length() == lockedFileCount - 1,
-               "Didn't find the locked file we were looking for!");
+  MOZ_ASSERT(mFileHandleQueues.Length() == fileHandleCount - 1,
+             "Didn't find the file handle we were looking for!");
 
   nsTArray<DelayedEnqueueInfo> delayedEnqueueInfos;
   delayedEnqueueInfos.SwapElements(mDelayedEnqueueInfos);
 
   for (uint32_t index = 0; index < delayedEnqueueInfos.Length(); index++) {
     DelayedEnqueueInfo& delayedEnqueueInfo = delayedEnqueueInfos[index];
-    if (NS_FAILED(gInstance->Enqueue(delayedEnqueueInfo.mLockedFile,
+    if (NS_FAILED(gInstance->Enqueue(delayedEnqueueInfo.mFileHandle,
                                      delayedEnqueueInfo.mFileHelper))) {
       NS_WARNING("Enqueue failed!");
     }
   }
 }
 
 bool
-FileService::StorageInfo::HasRunningLockedFiles(nsIOfflineStorage* aStorage)
+FileService::StorageInfo::HasRunningFileHandles(nsIOfflineStorage* aStorage)
 {
-  for (uint32_t index = 0; index < mLockedFileQueues.Length(); index++) {
-    LockedFile* lockedFile = mLockedFileQueues[index]->mLockedFile;
-    if (lockedFile->mFileHandle->Storage() == aStorage) {
+  for (uint32_t index = 0; index < mFileHandleQueues.Length(); index++) {
+    FileHandle* fileHandle = mFileHandleQueues[index]->mFileHandle;
+    if (fileHandle->mMutableFile->Storage() == aStorage) {
       return true;
     }
   }
   return false;
 }
 
 FileService::DelayedEnqueueInfo*
-FileService::StorageInfo::CreateDelayedEnqueueInfo(LockedFile* aLockedFile,
+FileService::StorageInfo::CreateDelayedEnqueueInfo(FileHandle* aFileHandle,
                                                    FileHelper* aFileHelper)
 {
   DelayedEnqueueInfo* info = mDelayedEnqueueInfos.AppendElement();
-  info->mLockedFile = aLockedFile;
+  info->mFileHandle = aFileHandle;
   info->mFileHelper = aFileHelper;
   return info;
 }
 
 void
-FileService::StorageInfo::CollectRunningAndDelayedLockedFiles(
+FileService::StorageInfo::CollectRunningAndDelayedFileHandles(
                                  nsIOfflineStorage* aStorage,
-                                 nsTArray<nsRefPtr<LockedFile> >& aLockedFiles)
+                                 nsTArray<nsRefPtr<FileHandle>>& aFileHandles)
 {
-  for (uint32_t index = 0; index < mLockedFileQueues.Length(); index++) {
-    LockedFile* lockedFile = mLockedFileQueues[index]->mLockedFile;
-    if (lockedFile->mFileHandle->Storage() == aStorage) {
-      aLockedFiles.AppendElement(lockedFile);
+  for (uint32_t index = 0; index < mFileHandleQueues.Length(); index++) {
+    FileHandle* fileHandle = mFileHandleQueues[index]->mFileHandle;
+    if (fileHandle->mMutableFile->Storage() == aStorage) {
+      aFileHandles.AppendElement(fileHandle);
     }
   }
 
   for (uint32_t index = 0; index < mDelayedEnqueueInfos.Length(); index++) {
-    LockedFile* lockedFile = mDelayedEnqueueInfos[index].mLockedFile;
-    if (lockedFile->mFileHandle->Storage() == aStorage) {
-      aLockedFiles.AppendElement(lockedFile);
+    FileHandle* fileHandle = mDelayedEnqueueInfos[index].mFileHandle;
+    if (fileHandle->mMutableFile->Storage() == aStorage) {
+      aFileHandles.AppendElement(fileHandle);
     }
   }
 }
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/filehandle/FileService.h
+++ b/dom/filehandle/FileService.h
@@ -21,17 +21,17 @@
 class nsAString;
 class nsIEventTarget;
 class nsIOfflineStorage;
 class nsIRunnable;
 
 namespace mozilla {
 namespace dom {
 
-class LockedFile;
+class FileHandle;
 
 class FileService MOZ_FINAL : public nsIObserver
 {
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSIOBSERVER
 
   // Returns a non-owning reference!
@@ -45,40 +45,40 @@ public:
   static void
   Shutdown();
 
   // Returns true if we've begun the shutdown process.
   static bool
   IsShuttingDown();
 
   nsresult
-  Enqueue(LockedFile* aLockedFile, FileHelper* aFileHelper);
+  Enqueue(FileHandle* aFileHandle, FileHelper* aFileHelper);
 
   void
-  NotifyLockedFileCompleted(LockedFile* aLockedFile);
+  NotifyFileHandleCompleted(FileHandle* aFileHandle);
 
   void
   WaitForStoragesToComplete(nsTArray<nsCOMPtr<nsIOfflineStorage> >& aStorages,
                             nsIRunnable* aCallback);
 
   void
-  AbortLockedFilesForStorage(nsIOfflineStorage* aStorage);
+  AbortFileHandlesForStorage(nsIOfflineStorage* aStorage);
 
   bool
-  HasLockedFilesForStorage(nsIOfflineStorage* aStorage);
+  HasFileHandlesForStorage(nsIOfflineStorage* aStorage);
 
   nsIEventTarget*
   StreamTransportTarget()
   {
     NS_ASSERTION(mStreamTransportTarget, "This should never be null!");
     return mStreamTransportTarget;
   }
 
 private:
-  class LockedFileQueue MOZ_FINAL : public FileHelperListener
+  class FileHandleQueue MOZ_FINAL : public FileHelperListener
   {
     friend class FileService;
 
   public:
     NS_IMETHOD_(MozExternalRefCountType)
     AddRef() MOZ_OVERRIDE;
 
     NS_IMETHOD_(MozExternalRefCountType)
@@ -87,67 +87,67 @@ private:
     inline nsresult
     Enqueue(FileHelper* aFileHelper);
 
     virtual void
     OnFileHelperComplete(FileHelper* aFileHelper) MOZ_OVERRIDE;
 
   private:
     inline
-    LockedFileQueue(LockedFile* aLockedFile);
+    FileHandleQueue(FileHandle* aFileHandle);
 
     nsresult
     ProcessQueue();
 
     ThreadSafeAutoRefCnt mRefCnt;
     NS_DECL_OWNINGTHREAD
-    nsRefPtr<LockedFile> mLockedFile;
+    nsRefPtr<FileHandle> mFileHandle;
     nsTArray<nsRefPtr<FileHelper> > mQueue;
     nsRefPtr<FileHelper> mCurrentHelper;
   };
 
   struct DelayedEnqueueInfo
   {
     DelayedEnqueueInfo();
     ~DelayedEnqueueInfo();
 
-    nsRefPtr<LockedFile> mLockedFile;
+    nsRefPtr<FileHandle> mFileHandle;
     nsRefPtr<FileHelper> mFileHelper;
   };
 
   class StorageInfo
   {
     friend class FileService;
 
   public:
-    inline LockedFileQueue*
-    CreateLockedFileQueue(LockedFile* aLockedFile);
+    inline FileHandleQueue*
+    CreateFileHandleQueue(FileHandle* aFileHandle);
 
-    inline LockedFileQueue*
-    GetLockedFileQueue(LockedFile* aLockedFile);
+    inline FileHandleQueue*
+    GetFileHandleQueue(FileHandle* aFileHandle);
 
     void
-    RemoveLockedFileQueue(LockedFile* aLockedFile);
+    RemoveFileHandleQueue(FileHandle* aFileHandle);
 
     bool
-    HasRunningLockedFiles()
+    HasRunningFileHandles()
     {
-      return !mLockedFileQueues.IsEmpty();
+      return !mFileHandleQueues.IsEmpty();
     }
 
     inline bool
-    HasRunningLockedFiles(nsIOfflineStorage* aStorage);
+    HasRunningFileHandles(nsIOfflineStorage* aStorage);
 
     inline DelayedEnqueueInfo*
-    CreateDelayedEnqueueInfo(LockedFile* aLockedFile, FileHelper* aFileHelper);
+    CreateDelayedEnqueueInfo(FileHandle* aFileHandle, FileHelper* aFileHelper);
 
     inline void
-    CollectRunningAndDelayedLockedFiles(
+    CollectRunningAndDelayedFileHandles(
                                  nsIOfflineStorage* aStorage,
-                                 nsTArray<nsRefPtr<LockedFile> >& aLockedFiles);
+                                 nsTArray<nsRefPtr<FileHandle>>& aFileHandles);
 
     void
     LockFileForReading(const nsAString& aFileName)
     {
       mFilesReading.PutEntry(aFileName);
     }
 
     void
@@ -168,17 +168,17 @@ private:
       return mFilesWriting.Contains(aFileName);
     }
 
   private:
     StorageInfo()
     {
     }
 
-    nsTArray<nsRefPtr<LockedFileQueue> > mLockedFileQueues;
+    nsTArray<nsRefPtr<FileHandleQueue>> mFileHandleQueues;
     nsTArray<DelayedEnqueueInfo> mDelayedEnqueueInfos;
     nsTHashtable<nsStringHashKey> mFilesReading;
     nsTHashtable<nsStringHashKey> mFilesWriting;
   };
 
   struct StoragesCompleteCallback
   {
     nsTArray<nsCOMPtr<nsIOfflineStorage> > mStorages;
--- a/dom/filehandle/FileStreamWrappers.cpp
+++ b/dom/filehandle/FileStreamWrappers.cpp
@@ -1,20 +1,20 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "FileStreamWrappers.h"
 
-#include "FileHandle.h"
 #include "FileHelper.h"
 #include "MainThreadUtils.h"
 #include "mozilla/Attributes.h"
+#include "MutableFile.h"
 #include "nsDebug.h"
 #include "nsError.h"
 #include "nsIRunnable.h"
 #include "nsISeekableStream.h"
 #include "nsThreadUtils.h"
 
 namespace mozilla {
 namespace dom {
@@ -247,17 +247,17 @@ FileOutputStreamWrapper::Close()
 {
   NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
 
   nsresult rv = NS_OK;
 
   if (!mFirstTime) {
     NS_ASSERTION(PR_GetCurrentThread() == mWriteThread,
                  "Unsetting thread locals on wrong thread!");
-    mFileHelper->mFileHandle->UnsetThreadLocals();
+    mFileHelper->mMutableFile->UnsetThreadLocals();
   }
 
   if (mFlags & NOTIFY_CLOSE) {
     nsCOMPtr<nsIRunnable> runnable = new CloseRunnable(mFileHelper);
 
     if (NS_FAILED(NS_DispatchToMainThread(runnable))) {
       NS_WARNING("Failed to dispatch to the main thread!");
     }
@@ -278,17 +278,17 @@ FileOutputStreamWrapper::Write(const cha
   nsresult rv;
 
   if (mFirstTime) {
     mFirstTime = false;
 
 #ifdef DEBUG
     mWriteThread = PR_GetCurrentThread();
 #endif
-    mFileHelper->mFileHandle->SetThreadLocals();
+    mFileHelper->mMutableFile->SetThreadLocals();
 
     nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(mOutputStream);
     if (seekable) {
       if (mOffset == UINT64_MAX) {
         rv = seekable->Seek(nsISeekableStream::NS_SEEK_END, 0);
       }
       else {
         rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET, mOffset);
deleted file mode 100644
--- a/dom/filehandle/LockedFile.cpp
+++ /dev/null
@@ -1,1062 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim: set ts=2 et sw=2 tw=80: */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this file,
- * You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#include "LockedFile.h"
-
-#include "AsyncHelper.h"
-#include "FileHandle.h"
-#include "FileHelper.h"
-#include "FileRequest.h"
-#include "FileService.h"
-#include "FileStreamWrappers.h"
-#include "MemoryStreams.h"
-#include "MetadataHelper.h"
-#include "mozilla/dom/EncodingUtils.h"
-#include "mozilla/dom/LockedFileBinding.h"
-#include "mozilla/EventDispatcher.h"
-#include "nsContentUtils.h"
-#include "nsDebug.h"
-#include "nsError.h"
-#include "nsIAppShell.h"
-#include "nsIDOMEvent.h"
-#include "nsIDOMFile.h"
-#include "nsIEventTarget.h"
-#include "nsISeekableStream.h"
-#include "nsNetUtil.h"
-#include "nsServiceManagerUtils.h"
-#include "nsString.h"
-#include "nsStringStream.h"
-#include "nsThreadUtils.h"
-#include "nsWidgetsCID.h"
-
-#define STREAM_COPY_BLOCK_SIZE 32768
-
-namespace mozilla {
-namespace dom {
-
-namespace {
-
-NS_DEFINE_CID(kAppShellCID, NS_APPSHELL_CID);
-
-class ReadHelper : public FileHelper
-{
-public:
-  ReadHelper(LockedFile* aLockedFile,
-             FileRequest* aFileRequest,
-             uint64_t aLocation,
-             uint64_t aSize)
-  : FileHelper(aLockedFile, aFileRequest),
-    mLocation(aLocation), mSize(aSize)
-  {
-    NS_ASSERTION(mSize, "Passed zero size!");
-  }
-
-  nsresult
-  Init();
-
-  nsresult
-  DoAsyncRun(nsISupports* aStream) MOZ_OVERRIDE;
-
-  nsresult
-  GetSuccessResult(JSContext* aCx,
-                   JS::MutableHandle<JS::Value> aVal) MOZ_OVERRIDE;
-
-protected:
-  uint64_t mLocation;
-  uint64_t mSize;
-
-  nsRefPtr<MemoryOutputStream> mStream;
-};
-
-class ReadTextHelper : public ReadHelper
-{
-public:
-  ReadTextHelper(LockedFile* aLockedFile,
-                 FileRequest* aFileRequest,
-                 uint64_t aLocation,
-                 uint64_t aSize,
-                 const nsAString& aEncoding)
-  : ReadHelper(aLockedFile, aFileRequest, aLocation, aSize),
-    mEncoding(aEncoding)
-  { }
-
-  nsresult
-  GetSuccessResult(JSContext* aCx,
-                   JS::MutableHandle<JS::Value> aVal) MOZ_OVERRIDE;
-
-private:
-  nsString mEncoding;
-};
-
-class WriteHelper : public FileHelper
-{
-public:
-  WriteHelper(LockedFile* aLockedFile,
-              FileRequest* aFileRequest,
-              uint64_t aLocation,
-              nsIInputStream* aStream,
-              uint64_t aLength)
-  : FileHelper(aLockedFile, aFileRequest),
-    mLocation(aLocation), mStream(aStream), mLength(aLength)
-  {
-    NS_ASSERTION(mLength, "Passed zero length!");
-  }
-
-  nsresult
-  DoAsyncRun(nsISupports* aStream);
-
-private:
-  uint64_t mLocation;
-  nsCOMPtr<nsIInputStream> mStream;
-  uint64_t mLength;
-};
-
-class TruncateHelper : public FileHelper
-{
-public:
-  TruncateHelper(LockedFile* aLockedFile,
-                 FileRequest* aFileRequest,
-                 uint64_t aOffset)
-  : FileHelper(aLockedFile, aFileRequest),
-    mOffset(aOffset)
-  { }
-
-  nsresult
-  DoAsyncRun(nsISupports* aStream);
-
-private:
-  class AsyncTruncator : public AsyncHelper
-  {
-  public:
-    AsyncTruncator(nsISupports* aStream, int64_t aOffset)
-    : AsyncHelper(aStream),
-      mOffset(aOffset)
-    { }
-  protected:
-    nsresult
-    DoStreamWork(nsISupports* aStream);
-
-    uint64_t mOffset;
-  };
-
-  uint64_t mOffset;
-};
-
-class FlushHelper : public FileHelper
-{
-public:
-  FlushHelper(LockedFile* aLockedFile,
-              FileRequest* aFileRequest)
-  : FileHelper(aLockedFile, aFileRequest)
-  { }
-
-  nsresult
-  DoAsyncRun(nsISupports* aStream);
-
-private:
-  class AsyncFlusher : public AsyncHelper
-  {
-  public:
-    AsyncFlusher(nsISupports* aStream)
-    : AsyncHelper(aStream)
-    { }
-  protected:
-    nsresult
-    DoStreamWork(nsISupports* aStream);
-  };
-};
-
-class OpenStreamHelper : public FileHelper
-{
-public:
-  OpenStreamHelper(LockedFile* aLockedFile,
-                   bool aWholeFile,
-                   uint64_t aStart,
-                   uint64_t aLength)
-  : FileHelper(aLockedFile, nullptr),
-    mWholeFile(aWholeFile), mStart(aStart), mLength(aLength)
-  { }
-
-  nsresult
-  DoAsyncRun(nsISupports* aStream);
-
-  nsCOMPtr<nsIInputStream>&
-  Result()
-  {
-    return mStream;
-  }
-
-private:
-  bool mWholeFile;
-  uint64_t mStart;
-  uint64_t mLength;
-
-  nsCOMPtr<nsIInputStream> mStream;
-};
-
-already_AddRefed<nsIDOMEvent>
-CreateGenericEvent(EventTarget* aEventOwner,
-                   const nsAString& aType, bool aBubbles, bool aCancelable)
-{
-  nsCOMPtr<nsIDOMEvent> event;
-  NS_NewDOMEvent(getter_AddRefs(event), aEventOwner, nullptr, nullptr);
-  nsresult rv = event->InitEvent(aType, aBubbles, aCancelable);
-  NS_ENSURE_SUCCESS(rv, nullptr);
-
-  event->SetTrusted(true);
-
-  return event.forget();
-}
-
-} // anonymous namespace
-
-// static
-already_AddRefed<LockedFile>
-LockedFile::Create(FileHandle* aFileHandle,
-                   FileMode aMode,
-                   RequestMode aRequestMode)
-{
-  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
-
-  nsRefPtr<LockedFile> lockedFile = new LockedFile();
-
-  lockedFile->BindToOwner(aFileHandle);
-
-  lockedFile->mFileHandle = aFileHandle;
-  lockedFile->mMode = aMode;
-  lockedFile->mRequestMode = aRequestMode;
-
-  nsCOMPtr<nsIAppShell> appShell = do_GetService(kAppShellCID);
-  NS_ENSURE_TRUE(appShell, nullptr);
-
-  nsresult rv = appShell->RunBeforeNextEvent(lockedFile);
-  NS_ENSURE_SUCCESS(rv, nullptr);
-
-  lockedFile->mCreating = true;
-
-  FileService* service = FileService::GetOrCreate();
-  NS_ENSURE_TRUE(service, nullptr);
-
-  rv = service->Enqueue(lockedFile, nullptr);
-  NS_ENSURE_SUCCESS(rv, nullptr);
-
-  return lockedFile.forget();
-}
-
-LockedFile::LockedFile()
-: mReadyState(INITIAL),
-  mMode(FileMode::Readonly),
-  mRequestMode(NORMAL),
-  mLocation(0),
-  mPendingRequests(0),
-  mAborted(false),
-  mCreating(false)
-{
-  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
-  SetIsDOMBinding();
-}
-
-LockedFile::~LockedFile()
-{
-  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
-}
-
-NS_IMPL_CYCLE_COLLECTION_INHERITED(LockedFile, DOMEventTargetHelper,
-                                   mFileHandle)
-
-NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(LockedFile)
-  NS_INTERFACE_MAP_ENTRY(nsIRunnable)
-NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
-
-NS_IMPL_ADDREF_INHERITED(LockedFile, DOMEventTargetHelper)
-NS_IMPL_RELEASE_INHERITED(LockedFile, DOMEventTargetHelper)
-
-nsresult
-LockedFile::PreHandleEvent(EventChainPreVisitor& aVisitor)
-{
-  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
-
-  aVisitor.mCanHandle = true;
-  aVisitor.mParentTarget = mFileHandle;
-  return NS_OK;
-}
-
-void
-LockedFile::OnNewRequest()
-{
-  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
-  if (!mPendingRequests) {
-    NS_ASSERTION(mReadyState == INITIAL,
-                 "Reusing a locked file!");
-    mReadyState = LOADING;
-  }
-  ++mPendingRequests;
-}
-
-void
-LockedFile::OnRequestFinished()
-{
-  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
-  NS_ASSERTION(mPendingRequests, "Mismatched calls!");
-  --mPendingRequests;
-  if (!mPendingRequests) {
-    NS_ASSERTION(mAborted || mReadyState == LOADING,
-                 "Bad state!");
-    mReadyState = LockedFile::FINISHING;
-    Finish();
-  }
-}
-
-nsresult
-LockedFile::CreateParallelStream(nsISupports** aStream)
-{
-  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
-
-  if (mFileHandle->IsInvalid()) {
-    return NS_ERROR_NOT_AVAILABLE;
-  }
-
-  nsCOMPtr<nsISupports> stream =
-    mFileHandle->CreateStream(mFileHandle->mFile, mMode == FileMode::Readonly);
-  NS_ENSURE_TRUE(stream, NS_ERROR_FAILURE);
-
-  mParallelStreams.AppendElement(stream);
-
-  stream.forget(aStream);
-  return NS_OK;
-}
-
-nsresult
-LockedFile::GetOrCreateStream(nsISupports** aStream)
-{
-  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
-
-  if (mFileHandle->IsInvalid()) {
-    return NS_ERROR_NOT_AVAILABLE;
-  }
-
-  if (!mStream) {
-    nsCOMPtr<nsISupports> stream =
-      mFileHandle->CreateStream(mFileHandle->mFile, mMode == FileMode::Readonly);
-    NS_ENSURE_TRUE(stream, NS_ERROR_FAILURE);
-
-    stream.swap(mStream);
-  }
-
-  nsCOMPtr<nsISupports> stream(mStream);
-  stream.forget(aStream);
-
-  return NS_OK;
-}
-
-already_AddRefed<FileRequest>
-LockedFile::GenerateFileRequest()
-{
-  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
-  return FileRequest::Create(GetOwner(), this, /* aWrapAsDOMRequest */ false);
-}
-
-bool
-LockedFile::IsOpen() const
-{
-  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
-
-  // If we haven't started anything then we're open.
-  if (mReadyState == INITIAL) {
-    NS_ASSERTION(FileHelper::GetCurrentLockedFile() != this,
-                 "This should be some other locked file (or null)!");
-    return true;
-  }
-
-  // If we've already started then we need to check to see if we still have the
-  // mCreating flag set. If we do (i.e. we haven't returned to the event loop
-  // from the time we were created) then we are open. Otherwise check the
-  // currently running locked files to see if it's the same. We only allow other
-  // requests to be made if this locked file is currently running.
-  if (mReadyState == LOADING) {
-    if (mCreating) {
-      return true;
-    }
-
-    if (FileHelper::GetCurrentLockedFile() == this) {
-      return true;
-    }
-  }
-
-  return false;
-}
-
-// virtual
-JSObject*
-LockedFile::WrapObject(JSContext* aCx)
-{
-  return LockedFileBinding::Wrap(aCx, this);
-}
-
-already_AddRefed<FileRequest>
-LockedFile::GetMetadata(const DOMFileMetadataParameters& aParameters,
-                        ErrorResult& aRv)
-{
-  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
-
-  // Common state checking
-  if (!CheckState(aRv)) {
-    return nullptr;
-  }
-
-  // Do nothing if the window is closed
-  if (!GetOwner()) {
-    return nullptr;
-  }
-
-  nsRefPtr<MetadataParameters> params =
-    new MetadataParameters(aParameters.mSize, aParameters.mLastModified);
-  if (!params->IsConfigured()) {
-    aRv.ThrowTypeError(MSG_METADATA_NOT_CONFIGURED);
-    return nullptr;
-  }
-
-  nsRefPtr<FileRequest> fileRequest = GenerateFileRequest();
-
-  nsRefPtr<MetadataHelper> helper =
-    new MetadataHelper(this, fileRequest, params);
-
-  if (NS_WARN_IF(NS_FAILED(helper->Enqueue()))) {
-    aRv.Throw(NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR);
-    return nullptr;
-  }
-
-  return fileRequest.forget();
-}
-
-already_AddRefed<FileRequest>
-LockedFile::ReadAsArrayBuffer(uint64_t aSize, ErrorResult& aRv)
-{
-  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
-
-  // State and argument checking for read
-  if (!CheckStateAndArgumentsForRead(aSize, aRv)) {
-    return nullptr;
-  }
-
-  // Do nothing if the window is closed
-  if (!GetOwner()) {
-    return nullptr;
-  }
-
-  nsRefPtr<FileRequest> fileRequest = GenerateFileRequest();
-
-  nsRefPtr<ReadHelper> helper =
-    new ReadHelper(this, fileRequest, mLocation, aSize);
-
-  if (NS_WARN_IF(NS_FAILED(helper->Init())) ||
-      NS_WARN_IF(NS_FAILED(helper->Enqueue()))) {
-    aRv.Throw(NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR);
-    return nullptr;
-  }
-
-  mLocation += aSize;
-
-  return fileRequest.forget();
-}
-
-already_AddRefed<FileRequest>
-LockedFile::ReadAsText(uint64_t aSize, const nsAString& aEncoding,
-                       ErrorResult& aRv)
-{
-  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
-
-  // State and argument checking for read
-  if (!CheckStateAndArgumentsForRead(aSize, aRv)) {
-    return nullptr;
-  }
-
-  // Do nothing if the window is closed
-  if (!GetOwner()) {
-    return nullptr;
-  }
-
-  nsRefPtr<FileRequest> fileRequest = GenerateFileRequest();
-
-  nsRefPtr<ReadTextHelper> helper =
-    new ReadTextHelper(this, fileRequest, mLocation, aSize, aEncoding);
-
-  if (NS_WARN_IF(NS_FAILED(helper->Init())) ||
-      NS_WARN_IF(NS_FAILED(helper->Enqueue()))) {
-    aRv.Throw(NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR);
-    return nullptr;
-  }
-
-  mLocation += aSize;
-
-  return fileRequest.forget();
-}
-
-already_AddRefed<FileRequest>
-LockedFile::Truncate(const Optional<uint64_t>& aSize, ErrorResult& aRv)
-{
-  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
-
-  // State checking for write
-  if (!CheckStateForWrite(aRv)) {
-    return nullptr;
-  }
-
-  // Getting location and additional state checking for truncate
-  uint64_t location;
-  if (aSize.WasPassed()) {
-    // Just in case someone calls us from C++
-    NS_ASSERTION(aSize.Value() != UINT64_MAX, "Passed wrong size!");
-    location = aSize.Value();
-  } else {
-    if (mLocation == UINT64_MAX) {
-      aRv.Throw(NS_ERROR_DOM_FILEHANDLE_NOT_ALLOWED_ERR);
-      return nullptr;
-    }
-    location = mLocation;
-  }
-
-  // Do nothing if the window is closed
-  if (!GetOwner()) {
-    return nullptr;
-  }
-
-  nsRefPtr<FileRequest> fileRequest = GenerateFileRequest();
-
-  nsRefPtr<TruncateHelper> helper =
-    new TruncateHelper(this, fileRequest, location);
-
-  if (NS_WARN_IF(NS_FAILED(helper->Enqueue()))) {
-    aRv.Throw(NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR);
-    return nullptr;
-  }
-
-  if (aSize.WasPassed()) {
-    mLocation = aSize.Value();
-  }
-
-  return fileRequest.forget();
-}
-
-already_AddRefed<FileRequest>
-LockedFile::Flush(ErrorResult& aRv)
-{
-  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
-
-  // State checking for write
-  if (!CheckStateForWrite(aRv)) {
-    return nullptr;
-  }
-
-  // Do nothing if the window is closed
-  if (!GetOwner()) {
-    return nullptr;
-  }
-
-  nsRefPtr<FileRequest> fileRequest = GenerateFileRequest();
-
-  nsRefPtr<FlushHelper> helper = new FlushHelper(this, fileRequest);
-
-  if (NS_WARN_IF(NS_FAILED(helper->Enqueue()))) {
-    aRv.Throw(NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR);
-    return nullptr;
-  }
-
-  return fileRequest.forget();
-}
-
-void
-LockedFile::Abort(ErrorResult& aRv)
-{
-  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
-
-  // This method is special enough for not using generic state checking methods.
-
-  // We can't use IsOpen here since we need it to be possible to call Abort()
-  // even from outside of transaction callbacks.
-  if (mReadyState != LockedFile::INITIAL &&
-      mReadyState != LockedFile::LOADING) {
-    aRv.Throw(NS_ERROR_DOM_FILEHANDLE_NOT_ALLOWED_ERR);
-    return;
-  }
-
-  bool needToFinish = mReadyState == INITIAL;
-
-  mAborted = true;
-  mReadyState = DONE;
-
-  // Fire the abort event if there are no outstanding requests. Otherwise the
-  // abort event will be fired when all outstanding requests finish.
-  if (needToFinish) {
-    aRv = Finish();
-  }
-}
-
-NS_IMETHODIMP
-LockedFile::Run()
-{
-  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
-
-  // We're back at the event loop, no longer newborn.
-  mCreating = false;
-
-  // Maybe set the readyState to DONE if there were no requests generated.
-  if (mReadyState == INITIAL) {
-    mReadyState = DONE;
-
-    if (NS_FAILED(Finish())) {
-      NS_WARNING("Failed to finish!");
-    }
-  }
-
-  return NS_OK;
-}
-
-nsresult
-LockedFile::OpenInputStream(bool aWholeFile, uint64_t aStart, uint64_t aLength,
-                            nsIInputStream** aResult)
-{
-  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
-  NS_ASSERTION(mRequestMode == PARALLEL,
-               "Don't call me in other than parallel mode!");
-
-  // Common state checking
-  ErrorResult error;
-  if (!CheckState(error)) {
-    return error.ErrorCode();
-  }
-
-  // Do nothing if the window is closed
-  if (!GetOwner()) {
-    return NS_OK;
-  }
-
-  nsRefPtr<OpenStreamHelper> helper =
-    new OpenStreamHelper(this, aWholeFile, aStart, aLength);
-
-  nsresult rv = helper->Enqueue();
-  NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR);
-
-  nsCOMPtr<nsIInputStream>& result = helper->Result();
-  NS_ENSURE_TRUE(result, NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR);
-
-  result.forget(aResult);
-  return NS_OK;
-}
-
-bool
-LockedFile::CheckState(ErrorResult& aRv)
-{
-  if (!IsOpen()) {
-    aRv.Throw(NS_ERROR_DOM_FILEHANDLE_LOCKEDFILE_INACTIVE_ERR);
-    return false;
-  }
-
-  return true;
-}
-
-bool
-LockedFile::CheckStateAndArgumentsForRead(uint64_t aSize, ErrorResult& aRv)
-{
-  // Common state checking
-  if (!CheckState(aRv)) {
-    return false;
-  }
-
-  // Additional state checking for read
-  if (mLocation == UINT64_MAX) {
-    aRv.Throw(NS_ERROR_DOM_FILEHANDLE_NOT_ALLOWED_ERR);
-    return false;
-  }
-
-  // Argument checking for read
-  if (!aSize) {
-    aRv.ThrowTypeError(MSG_INVALID_READ_SIZE);
-    return false;
-  }
-
-  return true;
-}
-
-bool
-LockedFile::CheckStateForWrite(ErrorResult& aRv)
-{
-  // Common state checking
-  if (!CheckState(aRv)) {
-    return false;
-  }
-
-  // Additional state checking for write
-  if (mMode != FileMode::Readwrite) {
-    aRv.Throw(NS_ERROR_DOM_FILEHANDLE_READ_ONLY_ERR);
-    return false;
-  }
-
-  return true;
-}
-
-already_AddRefed<FileRequest>
-LockedFile::WriteInternal(nsIInputStream* aInputStream, uint64_t aInputLength,
-                          bool aAppend, ErrorResult& aRv)
-{
-  MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
-
-  DebugOnly<ErrorResult> error;
-  MOZ_ASSERT(CheckStateForWrite(error));
-  MOZ_ASSERT_IF(!aAppend, mLocation != UINT64_MAX);
-  MOZ_ASSERT(aInputStream);
-  MOZ_ASSERT(aInputLength);
-  MOZ_ASSERT(GetOwner());
-
-  nsRefPtr<FileRequest> fileRequest = GenerateFileRequest();
-
-  uint64_t location = aAppend ? UINT64_MAX : mLocation;
-
-  nsRefPtr<WriteHelper> helper =
-    new WriteHelper(this, fileRequest, location, aInputStream, aInputLength);
-
-  if (NS_WARN_IF(NS_FAILED(helper->Enqueue()))) {
-    aRv.Throw(NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR);
-    return nullptr;
-  }
-
-  if (aAppend) {
-    mLocation = UINT64_MAX;
-  }
-  else {
-    mLocation += aInputLength;
-  }
-
-  return fileRequest.forget();
-}
-
-nsresult
-LockedFile::Finish()
-{
-  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
-
-  nsRefPtr<FinishHelper> helper(new FinishHelper(this));
-
-  FileService* service = FileService::Get();
-  NS_ASSERTION(service, "This should never be null");
-
-  nsIEventTarget* target = service->StreamTransportTarget();
-
-  nsresult rv = target->Dispatch(helper, NS_DISPATCH_NORMAL);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  return NS_OK;
-}
-
-// static
-already_AddRefed<nsIInputStream>
-LockedFile::GetInputStream(const ArrayBuffer& aValue, uint64_t* aInputLength,
-                           ErrorResult& aRv)
-{
-  aValue.ComputeLengthAndData();
-  const char* data = reinterpret_cast<const char*>(aValue.Data());
-  uint32_t length = aValue.Length();
-
-  nsCOMPtr<nsIInputStream> stream;
-  aRv = NS_NewByteInputStream(getter_AddRefs(stream), data, length,
-                              NS_ASSIGNMENT_COPY);
-  if (aRv.Failed()) {
-    return nullptr;
-  }
-
-  *aInputLength = length;
-  return stream.forget();
-}
-
-// static
-already_AddRefed<nsIInputStream>
-LockedFile::GetInputStream(nsIDOMBlob* aValue, uint64_t* aInputLength,
-                           ErrorResult& aRv)
-{
-  uint64_t length;
-  aRv = aValue->GetSize(&length);
-  if (aRv.Failed()) {
-    return nullptr;
-  }
-
-  nsCOMPtr<nsIInputStream> stream;
-  aRv = aValue->GetInternalStream(getter_AddRefs(stream));
-  if (aRv.Failed()) {
-    return nullptr;
-  }
-
-  *aInputLength = length;
-  return stream.forget();
-}
-
-// static
-already_AddRefed<nsIInputStream>
-LockedFile::GetInputStream(const nsAString& aValue, uint64_t* aInputLength,
-                           ErrorResult& aRv)
-{
-  NS_ConvertUTF16toUTF8 cstr(aValue);
-
-  nsCOMPtr<nsIInputStream> stream;
-  aRv = NS_NewCStringInputStream(getter_AddRefs(stream), cstr);
-  if (aRv.Failed()) {
-    return nullptr;
-  }
-
-  *aInputLength = cstr.Length();
-  return stream.forget();
-}
-
-FinishHelper::FinishHelper(LockedFile* aLockedFile)
-: mLockedFile(aLockedFile),
-  mAborted(aLockedFile->mAborted)
-{
-  mParallelStreams.SwapElements(aLockedFile->mParallelStreams);
-  mStream.swap(aLockedFile->mStream);
-}
-
-NS_IMPL_ISUPPORTS(FinishHelper, nsIRunnable)
-
-NS_IMETHODIMP
-FinishHelper::Run()
-{
-  if (NS_IsMainThread()) {
-    mLockedFile->mReadyState = LockedFile::DONE;
-
-    FileService* service = FileService::Get();
-    if (service) {
-      service->NotifyLockedFileCompleted(mLockedFile);
-    }
-
-    nsCOMPtr<nsIDOMEvent> event;
-    if (mAborted) {
-      event = CreateGenericEvent(mLockedFile, NS_LITERAL_STRING("abort"),
-                                 true, false);
-    }
-    else {
-      event = CreateGenericEvent(mLockedFile, NS_LITERAL_STRING("complete"),
-                                 false, false);
-    }
-    NS_ENSURE_TRUE(event, NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR);
-
-    bool dummy;
-    if (NS_FAILED(mLockedFile->DispatchEvent(event, &dummy))) {
-      NS_WARNING("Dispatch failed!");
-    }
-
-    mLockedFile = nullptr;
-
-    return NS_OK;
-  }
-
-  if (mLockedFile->mFileHandle->IsInvalid()) {
-    mAborted = true;
-  }
-
-  for (uint32_t index = 0; index < mParallelStreams.Length(); index++) {
-    nsCOMPtr<nsIInputStream> stream =
-      do_QueryInterface(mParallelStreams[index]);
-
-    if (NS_FAILED(stream->Close())) {
-      NS_WARNING("Failed to close stream!");
-    }
-
-    mParallelStreams[index] = nullptr;
-  }
-
-  if (mStream) {
-    nsCOMPtr<nsIInputStream> stream = do_QueryInterface(mStream);
-
-    if (NS_FAILED(stream->Close())) {
-      NS_WARNING("Failed to close stream!");
-    }
-
-    mStream = nullptr;
-  }
-
-  return NS_DispatchToMainThread(this);
-}
-
-nsresult
-ReadHelper::Init()
-{
-  mStream = MemoryOutputStream::Create(mSize);
-  NS_ENSURE_TRUE(mStream, NS_ERROR_FAILURE);
-
-  return NS_OK;
-}
-
-nsresult
-ReadHelper::DoAsyncRun(nsISupports* aStream)
-{
-  NS_ASSERTION(aStream, "Passed a null stream!");
-
-  uint32_t flags = FileStreamWrapper::NOTIFY_PROGRESS;
-
-  nsCOMPtr<nsIInputStream> istream =
-    new FileInputStreamWrapper(aStream, this, mLocation, mSize, flags);
-
-  FileService* service = FileService::Get();
-  NS_ASSERTION(service, "This should never be null");
-
-  nsIEventTarget* target = service->StreamTransportTarget();
-
-  nsCOMPtr<nsIAsyncStreamCopier> copier;
-  nsresult rv =
-    NS_NewAsyncStreamCopier(getter_AddRefs(copier), istream, mStream, target,
-                            false, true, STREAM_COPY_BLOCK_SIZE);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  rv = copier->AsyncCopy(this, nullptr);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  mRequest = do_QueryInterface(copier);
-
-  return NS_OK;
-}
-
-nsresult
-ReadHelper::GetSuccessResult(JSContext* aCx,
-                             JS::MutableHandle<JS::Value> aVal)
-{
-  JS::Rooted<JSObject*> arrayBuffer(aCx);
-  nsresult rv =
-    nsContentUtils::CreateArrayBuffer(aCx, mStream->Data(), arrayBuffer.address());
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  aVal.setObject(*arrayBuffer);
-  return NS_OK;
-}
-
-nsresult
-ReadTextHelper::GetSuccessResult(JSContext* aCx,
-                                 JS::MutableHandle<JS::Value> aVal)
-{
-  nsAutoCString encoding;
-  const nsCString& data = mStream->Data();
-  // The BOM sniffing is baked into the "decode" part of the Encoding
-  // Standard, which the File API references.
-  if (!nsContentUtils::CheckForBOM(
-        reinterpret_cast<const unsigned char *>(data.get()),
-        data.Length(),
-        encoding)) {
-    // BOM sniffing failed. Try the API argument.
-    if (!EncodingUtils::FindEncodingForLabel(mEncoding, encoding)) {
-      // API argument failed. Since we are dealing with a file system file,
-      // we don't have a meaningful type attribute for the blob available,
-      // so proceeding to the next step, which is defaulting to UTF-8.
-      encoding.AssignLiteral("UTF-8");
-    }
-  }
-
-  nsString tmpString;
-  nsresult rv = nsContentUtils::ConvertStringFromEncoding(encoding, data,
-                                                          tmpString);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  if (!xpc::StringToJsval(aCx, tmpString, aVal)) {
-    NS_WARNING("Failed to convert string!");
-    return NS_ERROR_FAILURE;
-  }
-  return NS_OK;
-}
-
-nsresult
-WriteHelper::DoAsyncRun(nsISupports* aStream)
-{
-  NS_ASSERTION(aStream, "Passed a null stream!");
-
-  uint32_t flags = FileStreamWrapper::NOTIFY_PROGRESS;
-
-  nsCOMPtr<nsIOutputStream> ostream =
-    new FileOutputStreamWrapper(aStream, this, mLocation, mLength, flags);
-
-  FileService* service = FileService::Get();
-  NS_ASSERTION(service, "This should never be null");
-
-  nsIEventTarget* target = service->StreamTransportTarget();
-
-  nsCOMPtr<nsIAsyncStreamCopier> copier;
-  nsresult rv =
-    NS_NewAsyncStreamCopier(getter_AddRefs(copier), mStream, ostream, target,
-                            true, false, STREAM_COPY_BLOCK_SIZE);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  rv = copier->AsyncCopy(this, nullptr);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  mRequest = do_QueryInterface(copier);
-
-  return NS_OK;
-}
-
-nsresult
-TruncateHelper::DoAsyncRun(nsISupports* aStream)
-{
-  NS_ASSERTION(aStream, "Passed a null stream!");
-
-  nsRefPtr<AsyncTruncator> truncator = new AsyncTruncator(aStream, mOffset);
-
-  nsresult rv = truncator->AsyncWork(this, nullptr);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  return NS_OK;
-}
-
-nsresult
-TruncateHelper::AsyncTruncator::DoStreamWork(nsISupports* aStream)
-{
-  nsCOMPtr<nsISeekableStream> sstream = do_QueryInterface(aStream);
-
-  nsresult rv = sstream->Seek(nsISeekableStream::NS_SEEK_SET, mOffset);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  rv = sstream->SetEOF();
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  return NS_OK;
-}
-
-nsresult
-FlushHelper::DoAsyncRun(nsISupports* aStream)
-{
-  NS_ASSERTION(aStream, "Passed a null stream!");
-
-  nsRefPtr<AsyncFlusher> flusher = new AsyncFlusher(aStream);
-
-  nsresult rv = flusher->AsyncWork(this, nullptr);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  return NS_OK;
-}
-
-nsresult
-FlushHelper::AsyncFlusher::DoStreamWork(nsISupports* aStream)
-{
-  nsCOMPtr<nsIOutputStream> ostream = do_QueryInterface(aStream);
-
-  nsresult rv = ostream->Flush();
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  return NS_OK;
-}
-
-nsresult
-OpenStreamHelper::DoAsyncRun(nsISupports* aStream)
-{
-  NS_ASSERTION(aStream, "Passed a null stream!");
-
-  uint32_t flags = FileStreamWrapper::NOTIFY_CLOSE |
-                   FileStreamWrapper::NOTIFY_DESTROY;
-
-  mStream = mWholeFile ?
-    new FileInputStreamWrapper(aStream, this, 0, mLength, flags) :
-    new FileInputStreamWrapper(aStream, this, mStart, mLength, flags);
-
-  return NS_OK;
-}
-
-} // namespace dom
-} // namespace mozilla
deleted file mode 100644
--- a/dom/filehandle/LockedFile.h
+++ /dev/null
@@ -1,319 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim: set ts=2 et sw=2 tw=80: */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this file,
- * You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#ifndef mozilla_dom_LockedFile_h
-#define mozilla_dom_LockedFile_h
-
-#include "js/TypeDecls.h"
-#include "MainThreadUtils.h"
-#include "mozilla/Assertions.h"
-#include "mozilla/Attributes.h"
-#include "mozilla/dom/BindingDeclarations.h"
-#include "mozilla/dom/FileModeBinding.h"
-#include "mozilla/dom/Nullable.h"
-#include "mozilla/dom/TypedArray.h"
-#include "mozilla/DOMEventTargetHelper.h"
-#include "mozilla/ErrorResult.h"
-#include "nsAutoPtr.h"
-#include "nsCOMPtr.h"
-#include "nsCycleCollectionParticipant.h"
-#include "nsIInputStream.h"
-#include "nsIRunnable.h"
-#include "nsTArray.h"
-
-class nsAString;
-class nsIDOMBlob;
-class nsPIDOMWindow;
-
-namespace mozilla {
-
-class EventChainPreVisitor;
-
-namespace dom {
-
-class DOMFileMetadataParameters;
-class FileHandle;
-class FileHelper;
-class FileRequest;
-class FileService;
-class FinishHelper;
-class MetadataHelper;
-
-class LockedFile : public DOMEventTargetHelper,
-                   public nsIRunnable
-{
-  friend class FileHelper;
-  friend class FileService;
-  friend class FinishHelper;
-  friend class MetadataHelper;
-
-public:
-  NS_DECL_ISUPPORTS_INHERITED
-  NS_DECL_NSIRUNNABLE
-
-  NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(LockedFile, DOMEventTargetHelper)
-
-  enum RequestMode
-  {
-    NORMAL = 0, // Sequential
-    PARALLEL
-  };
-
-  enum ReadyState
-  {
-    INITIAL = 0,
-    LOADING,
-    FINISHING,
-    DONE
-  };
-
-  static already_AddRefed<LockedFile>
-  Create(FileHandle* aFileHandle,
-         FileMode aMode,
-         RequestMode aRequestMode = NORMAL);
-
-  // nsIDOMEventTarget
-  virtual nsresult
-  PreHandleEvent(EventChainPreVisitor& aVisitor) MOZ_OVERRIDE;
-
-  nsresult
-  CreateParallelStream(nsISupports** aStream);
-
-  nsresult
-  GetOrCreateStream(nsISupports** aStream);
-
-  bool
-  IsOpen() const;
-
-  bool
-  IsAborted() const
-  {
-    return mAborted;
-  }
-
-  FileHandle*
-  Handle() const
-  {
-    return mFileHandle;
-  }
-
-  nsresult
-  OpenInputStream(bool aWholeFile, uint64_t aStart, uint64_t aLength,
-                  nsIInputStream** aResult);
-
-  // WrapperCache
-  virtual JSObject*
-  WrapObject(JSContext* aCx) MOZ_OVERRIDE;
-
-  // WebIDL
-  nsPIDOMWindow*
-  GetParentObject() const
-  {
-    return GetOwner();
-  }
-
-  FileHandle*
-  GetFileHandle() const
-  {
-    MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
-
-    return Handle();
-  }
-
-  FileMode
-  Mode() const
-  {
-    MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
-
-    return mMode;
-  }
-
-  bool
-  Active() const
-  {
-    MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
-
-    return IsOpen();
-  }
-
-  Nullable<uint64_t>
-  GetLocation() const
-  {
-    MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
-
-    if (mLocation == UINT64_MAX) {
-      return Nullable<uint64_t>();
-    }
-
-    return Nullable<uint64_t>(mLocation);
-  }
-
-  void
-  SetLocation(const Nullable<uint64_t>& aLocation)
-  {
-    MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
-
-    // Null means the end-of-file.
-    if (aLocation.IsNull()) {
-      mLocation = UINT64_MAX;
-    } else {
-      mLocation = aLocation.Value();
-    }
-  }
-
-  already_AddRefed<FileRequest>
-  GetMetadata(const DOMFileMetadataParameters& aParameters, ErrorResult& aRv);
-
-  already_AddRefed<FileRequest>
-  ReadAsArrayBuffer(uint64_t aSize, ErrorResult& aRv);
-
-  already_AddRefed<FileRequest>
-  ReadAsText(uint64_t aSize, const nsAString& aEncoding, ErrorResult& aRv);
-
-  template<class T>
-  already_AddRefed<FileRequest>
-  Write(const T& aValue, ErrorResult& aRv)
-  {
-    MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
-
-    return WriteOrAppend(aValue, false, aRv);
-  }
-
-  template<class T>
-  already_AddRefed<FileRequest>
-  Append(const T& aValue, ErrorResult& aRv)
-  {
-    MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
-
-    return WriteOrAppend(aValue, true, aRv);
-  }
-
-  already_AddRefed<FileRequest>
-  Truncate(const Optional<uint64_t>& aSize, ErrorResult& aRv);
-
-  already_AddRefed<FileRequest>
-  Flush(ErrorResult& aRv);
-
-  void
-  Abort(ErrorResult& aRv);
-
-  IMPL_EVENT_HANDLER(complete)
-  IMPL_EVENT_HANDLER(abort)
-  IMPL_EVENT_HANDLER(error)
-
-private:
-  LockedFile();
-  ~LockedFile();
-
-  void
-  OnNewRequest();
-
-  void
-  OnRequestFinished();
-
-  bool
-  CheckState(ErrorResult& aRv);
-
-  bool
-  CheckStateAndArgumentsForRead(uint64_t aSize, ErrorResult& aRv);
-
-  bool
-  CheckStateForWrite(ErrorResult& aRv);
-
-  already_AddRefed<FileRequest>
-  GenerateFileRequest();
-
-  template<class T>
-  already_AddRefed<FileRequest>
-  WriteOrAppend(const T& aValue, bool aAppend, ErrorResult& aRv)
-  {
-    MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
-
-    // State checking for write
-    if (!CheckStateForWrite(aRv)) {
-      return nullptr;
-    }
-
-    // Additional state checking for write
-    if (!aAppend && mLocation == UINT64_MAX) {
-      aRv.Throw(NS_ERROR_DOM_FILEHANDLE_NOT_ALLOWED_ERR);
-      return nullptr;
-    }
-
-    uint64_t length;
-    nsCOMPtr<nsIInputStream> stream = GetInputStream(aValue, &length, aRv);
-    if (aRv.Failed()) {
-      return nullptr;
-    }
-
-    if (!length) {
-      return nullptr;
-    }
-
-    // Do nothing if the window is closed
-    if (!GetOwner()) {
-      return nullptr;
-    }
-
-    return WriteInternal(stream, length, aAppend, aRv);
-  }
-
-  already_AddRefed<FileRequest>
-  WriteInternal(nsIInputStream* aInputStream, uint64_t aInputLength,
-                bool aAppend, ErrorResult& aRv);
-
-  nsresult
-  Finish();
-
-  static already_AddRefed<nsIInputStream>
-  GetInputStream(const ArrayBuffer& aValue, uint64_t* aInputLength,
-                 ErrorResult& aRv);
-
-  static already_AddRefed<nsIInputStream>
-  GetInputStream(nsIDOMBlob* aValue, uint64_t* aInputLength, ErrorResult& aRv);
-
-  static already_AddRefed<nsIInputStream>
-  GetInputStream(const nsAString& aValue, uint64_t* aInputLength,
-                 ErrorResult& aRv);
-
-  nsRefPtr<FileHandle> mFileHandle;
-  ReadyState mReadyState;
-  FileMode mMode;
-  RequestMode mRequestMode;
-  uint64_t mLocation;
-  uint32_t mPendingRequests;
-
-  nsTArray<nsCOMPtr<nsISupports> > mParallelStreams;
-  nsCOMPtr<nsISupports> mStream;
-
-  bool mAborted;
-  bool mCreating;
-};
-
-class FinishHelper MOZ_FINAL : public nsIRunnable
-{
-  friend class LockedFile;
-
-public:
-  NS_DECL_THREADSAFE_ISUPPORTS
-  NS_DECL_NSIRUNNABLE
-
-private:
-  FinishHelper(LockedFile* aLockedFile);
-  ~FinishHelper()
-  { }
-
-  nsRefPtr<LockedFile> mLockedFile;
-  nsTArray<nsCOMPtr<nsISupports> > mParallelStreams;
-  nsCOMPtr<nsISupports> mStream;
-
-  bool mAborted;
-};
-
-} // namespace dom
-} // namespace mozilla
-
-#endif // mozilla_dom_LockedFile_h
--- a/dom/filehandle/MetadataHelper.cpp
+++ b/dom/filehandle/MetadataHelper.cpp
@@ -1,32 +1,32 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "MetadataHelper.h"
 
+#include "FileHandle.h"
 #include "js/Value.h"
 #include "js/RootingAPI.h"
 #include "jsapi.h"
-#include "LockedFile.h"
 #include "mozilla/dom/FileModeBinding.h"
 #include "nsDebug.h"
 #include "nsIFileStreams.h"
 #include "nsIOutputStream.h"
 
 namespace mozilla {
 namespace dom {
 
 nsresult
 MetadataHelper::DoAsyncRun(nsISupports* aStream)
 {
-  bool readWrite = mLockedFile->mMode == FileMode::Readwrite;
+  bool readWrite = mFileHandle->mMode == FileMode::Readwrite;
 
   nsRefPtr<AsyncMetadataGetter> getter =
     new AsyncMetadataGetter(aStream, mParams, readWrite);
 
   nsresult rv = getter->AsyncWork(this, nullptr);
   NS_ENSURE_SUCCESS(rv, rv);
 
   return NS_OK;
--- a/dom/filehandle/MetadataHelper.h
+++ b/dom/filehandle/MetadataHelper.h
@@ -71,20 +71,20 @@ private:
   int64_t mLastModified;
   bool mSizeRequested;
   bool mLastModifiedRequested;
 };
 
 class MetadataHelper : public FileHelper
 {
 public:
-  MetadataHelper(LockedFile* aLockedFile,
+  MetadataHelper(FileHandle* aFileHandle,
                  FileRequest* aFileRequest,
                  MetadataParameters* aParams)
-  : FileHelper(aLockedFile, aFileRequest),
+  : FileHelper(aFileHandle, aFileRequest),
     mParams(aParams)
   { }
 
   nsresult
   DoAsyncRun(nsISupports* aStream) MOZ_OVERRIDE;
 
   nsresult
   GetSuccessResult(JSContext* aCx,
copy from dom/filehandle/FileHandle.cpp
copy to dom/filehandle/MutableFile.cpp
--- a/dom/filehandle/FileHandle.cpp
+++ b/dom/filehandle/MutableFile.cpp
@@ -1,23 +1,23 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#include "FileHandle.h"
+#include "MutableFile.h"
 
 #include "File.h"
+#include "FileHandle.h"
 #include "FileRequest.h"
 #include "FileService.h"
-#include "LockedFile.h"
 #include "MetadataHelper.h"
 #include "mozilla/Assertions.h"
-#include "mozilla/dom/FileHandleBinding.h"
+#include "mozilla/dom/MutableFileBinding.h"
 #include "mozilla/ErrorResult.h"
 #include "nsAutoPtr.h"
 #include "nsContentUtils.h"
 #include "nsDebug.h"
 #include "nsError.h"
 #include "nsIDOMFile.h"
 #include "nsIFile.h"
 #include "nsNetUtil.h"
@@ -25,65 +25,65 @@
 namespace mozilla {
 namespace dom {
 
 namespace {
 
 class GetFileHelper : public MetadataHelper
 {
 public:
-  GetFileHelper(LockedFile* aLockedFile,
+  GetFileHelper(FileHandle* aFileHandle,
                 FileRequest* aFileRequest,
                 MetadataParameters* aParams,
-                FileHandle* aFileHandle)
-  : MetadataHelper(aLockedFile, aFileRequest, aParams),
-    mFileHandle(aFileHandle)
+                MutableFile* aMutableFile)
+  : MetadataHelper(aFileHandle, aFileRequest, aParams),
+    mMutableFile(aMutableFile)
   { }
 
   virtual nsresult
   GetSuccessResult(JSContext* aCx,
                    JS::MutableHandle<JS::Value> aVal) MOZ_OVERRIDE;
 
   virtual void
   ReleaseObjects() MOZ_OVERRIDE
   {
-    mFileHandle = nullptr;
+    mMutableFile = nullptr;
 
     MetadataHelper::ReleaseObjects();
   }
 
 private:
-  nsRefPtr<FileHandle> mFileHandle;
+  nsRefPtr<MutableFile> mMutableFile;
 };
 
 } // anonymous namespace
 
-FileHandle::FileHandle(nsPIDOMWindow* aWindow)
+MutableFile::MutableFile(nsPIDOMWindow* aWindow)
   : DOMEventTargetHelper(aWindow)
 {
 }
 
-FileHandle::FileHandle(DOMEventTargetHelper* aOwner)
+MutableFile::MutableFile(DOMEventTargetHelper* aOwner)
   : DOMEventTargetHelper(aOwner)
 {
 }
 
-FileHandle::~FileHandle()
+MutableFile::~MutableFile()
 {
 }
 
 bool
-FileHandle::IsShuttingDown()
+MutableFile::IsShuttingDown()
 {
   return FileService::IsShuttingDown();
 }
 
 // virtual
 already_AddRefed<nsISupports>
-FileHandle::CreateStream(nsIFile* aFile, bool aReadOnly)
+MutableFile::CreateStream(nsIFile* aFile, bool aReadOnly)
 {
   nsresult rv;
 
   if (aReadOnly) {
     nsCOMPtr<nsIInputStream> stream;
     rv = NS_NewLocalFileInputStream(getter_AddRefs(stream), aFile, -1, -1,
                                     nsIFileInputStream::DEFER_OPEN);
     if (NS_WARN_IF(NS_FAILED(rv))) {
@@ -98,90 +98,90 @@ FileHandle::CreateStream(nsIFile* aFile,
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return nullptr;
   }
   return stream.forget();
 }
 
 // virtual
 already_AddRefed<nsIDOMFile>
-FileHandle::CreateFileObject(LockedFile* aLockedFile, uint32_t aFileSize)
+MutableFile::CreateFileObject(FileHandle* aFileHandle, uint32_t aFileSize)
 {
   nsCOMPtr<nsIDOMFile> file =
-    new File(mName, mType, aFileSize, mFile, aLockedFile);
+    new File(mName, mType, aFileSize, mFile, aFileHandle);
 
   return file.forget();
 }
 
 // virtual
 JSObject*
-FileHandle::WrapObject(JSContext* aCx)
+MutableFile::WrapObject(JSContext* aCx)
 {
-  return FileHandleBinding::Wrap(aCx, this);
+  return MutableFileBinding::Wrap(aCx, this);
 }
 
-already_AddRefed<LockedFile>
-FileHandle::Open(FileMode aMode, ErrorResult& aError)
+already_AddRefed<FileHandle>
+MutableFile::Open(FileMode aMode, ErrorResult& aError)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   if (IsShuttingDown()) {
     aError.Throw(NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR);
     return nullptr;
   }
 
-  nsRefPtr<LockedFile> lockedFile = LockedFile::Create(this, aMode);
-  if (!lockedFile) {
+  nsRefPtr<FileHandle> fileHandle = FileHandle::Create(this, aMode);
+  if (!fileHandle) {
     aError.Throw(NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR);
     return nullptr;
   }
 
-  return lockedFile.forget();
+  return fileHandle.forget();
 }
 
 already_AddRefed<DOMRequest>
-FileHandle::GetFile(ErrorResult& aError)
+MutableFile::GetFile(ErrorResult& aError)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   // Do nothing if the window is closed
   if (!GetOwner()) {
     return nullptr;
   }
 
-  nsRefPtr<LockedFile> lockedFile =
-    LockedFile::Create(this, FileMode::Readonly, LockedFile::PARALLEL);
-  if (!lockedFile) {
+  nsRefPtr<FileHandle> fileHandle =
+    FileHandle::Create(this, FileMode::Readonly, FileHandle::PARALLEL);
+  if (!fileHandle) {
     aError.Throw(NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR);
     return nullptr;
   }
 
   nsRefPtr<FileRequest> request =
-    FileRequest::Create(GetOwner(), lockedFile, /* aWrapAsDOMRequest */ true);
+    FileRequest::Create(GetOwner(), fileHandle, /* aWrapAsDOMRequest */ true);
 
   nsRefPtr<MetadataParameters> params = new MetadataParameters(true, false);
 
   nsRefPtr<GetFileHelper> helper =
-    new GetFileHelper(lockedFile, request, params, this);
+    new GetFileHelper(fileHandle, request, params, this);
 
   nsresult rv = helper->Enqueue();
   if (NS_FAILED(rv)) {
     aError.Throw(NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR);
     return nullptr;
   }
 
   return request.forget();
 }
 
 nsresult
 GetFileHelper::GetSuccessResult(JSContext* aCx,
                                 JS::MutableHandle<JS::Value> aVal)
 {
   nsCOMPtr<nsIDOMFile> domFile =
-    mFileHandle->CreateFileObject(mLockedFile, mParams->Size());
+    mMutableFile->CreateFileObject(mFileHandle, mParams->Size());
 
   nsresult rv =
     nsContentUtils::WrapNative(aCx, domFile, &NS_GET_IID(nsIDOMFile), aVal);
   NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR);
   return NS_OK;
 }
 
 } // namespace dom
copy from dom/filehandle/FileHandle.h
copy to dom/filehandle/MutableFile.h
--- a/dom/filehandle/FileHandle.h
+++ b/dom/filehandle/MutableFile.h
@@ -1,16 +1,16 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#ifndef mozilla_dom_FileHandle_h
-#define mozilla_dom_FileHandle_h
+#ifndef mozilla_dom_MutableFile_h
+#define mozilla_dom_MutableFile_h
 
 #include "js/TypeDecls.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/dom/FileModeBinding.h"
 #include "mozilla/DOMEventTargetHelper.h"
 #include "nsCOMPtr.h"
 #include "nsString.h"
 
@@ -21,36 +21,36 @@ class nsPIDOMWindow;
 
 namespace mozilla {
 
 class ErrorResult;
 
 namespace dom {
 
 class DOMRequest;
+class FileHandle;
 class FileService;
-class LockedFile;
 class FinishHelper;
 class FileHelper;
 
 namespace indexedDB {
 class FileInfo;
 } // namespace indexedDB
 
 /**
- * This class provides a default FileHandle implementation, but it can be also
+ * This class provides a default MutableFile implementation, but it can be also
  * subclassed. The subclass can override implementation of GetFileId,
  * GetFileInfo, IsShuttingDown, IsInvalid, CreateStream, SetThreadLocals,
  * UnsetThreadLocals and CreateFileObject.
- * (for example IDBFileHandle provides IndexedDB specific implementation).
+ * (for example IDBMutableFile provides IndexedDB specific implementation).
  */
-class FileHandle : public DOMEventTargetHelper
+class MutableFile : public DOMEventTargetHelper
 {
+  friend class FileHandle;
   friend class FileService;
-  friend class LockedFile;
   friend class FinishHelper;
   friend class FileHelper;
 
 public:
   const nsAString&
   Name() const
   {
     return mName;
@@ -97,17 +97,17 @@ public:
   }
 
   virtual void
   UnsetThreadLocals()
   {
   }
 
   virtual already_AddRefed<nsIDOMFile>
-  CreateFileObject(LockedFile* aLockedFile, uint32_t aFileSize);
+  CreateFileObject(FileHandle* aFileHandle, uint32_t aFileSize);
 
   // nsWrapperCache
   virtual JSObject*
   WrapObject(JSContext* aCx) MOZ_OVERRIDE;
 
   // WebIDL
   nsPIDOMWindow*
   GetParentObject() const
@@ -122,37 +122,37 @@ public:
   }
 
   void
   GetType(nsString& aType) const
   {
     aType = mType;
   }
 
-  already_AddRefed<LockedFile>
+  already_AddRefed<FileHandle>
   Open(FileMode aMode, ErrorResult& aError);
 
   already_AddRefed<DOMRequest>
   GetFile(ErrorResult& aError);
 
   IMPL_EVENT_HANDLER(abort)
   IMPL_EVENT_HANDLER(error)
 
 protected:
-  FileHandle(nsPIDOMWindow* aWindow);
+  MutableFile(nsPIDOMWindow* aWindow);
 
-  FileHandle(DOMEventTargetHelper* aOwner);
+  MutableFile(DOMEventTargetHelper* aOwner);
 
-  virtual ~FileHandle();
+  virtual ~MutableFile();
 
   nsString mName;
   nsString mType;
 
   nsCOMPtr<nsIFile> mFile;
 
   nsCString mStorageId;
   nsString mFileName;
 };
 
 } // namespace dom
 } // namespace mozilla
 
-#endif // mozilla_dom_FileHandle_h
+#endif // mozilla_dom_MutableFile_h
--- a/dom/filehandle/moz.build
+++ b/dom/filehandle/moz.build
@@ -7,30 +7,30 @@
 TEST_DIRS += ['test']
 
 EXPORTS.mozilla.dom += [
     'File.h',
     'FileHandle.h',
     'FileHelper.h',
     'FileRequest.h',
     'FileService.h',
-    'LockedFile.h',
+    'MutableFile.h',
 ]
 
 UNIFIED_SOURCES += [
     'AsyncHelper.cpp',
     'File.cpp',
     'FileHandle.cpp',
     'FileHelper.cpp',
     'FileRequest.cpp',
     'FileService.cpp',
     'FileStreamWrappers.cpp',
-    'LockedFile.cpp',
     'MemoryStreams.cpp',
     'MetadataHelper.cpp',
+    'MutableFile.cpp',
 ]
 
 FAIL_ON_WARNINGS = True
 
 LOCAL_INCLUDES += [
     '../base',
 ]
 
--- a/dom/filehandle/test/helpers.js
+++ b/dom/filehandle/test/helpers.js
@@ -93,16 +93,48 @@ function allowUnlimitedQuota(url)
   addPermission("indexedDB-unlimited", true, url);
 }
 
 function resetUnlimitedQuota(url)
 {
   removePermission("indexedDB-unlimited", url);
 }
 
+function getMutableFile(fileStorageKey, name)
+{
+  var requestService = SpecialPowers.getDOMRequestService();
+  var request = requestService.createRequest(window);
+
+  switch (fileStorageKey) {
+    case IndexedDatabaseKey:
+      var dbname = window.location.pathname;
+      indexedDB.open(dbname, 1).onsuccess = function(event) {
+        var db = event.target.result;
+        db.createMutableFile(name).onsuccess = function(event) {
+          var fileHandle = event.target.result;
+          requestService.fireSuccess(request, fileHandle);
+        }
+      }
+      break;
+
+    case DeviceStorageKey:
+      var dbname = window.location.pathname;
+      indexedDB.open(dbname, 1).onsuccess = function(event) {
+        var db = event.target.result;
+        db.createMutableFile(name).onsuccess = function(event) {
+          var fileHandle = event.target.result;
+          requestService.fireSuccess(request, fileHandle);
+        }
+      }
+      break;
+  }
+
+  return request;
+}
+
 function getFileHandle(fileStorageKey, name)
 {
   var requestService = SpecialPowers.getDOMRequestService();
   var request = requestService.createRequest(window);
 
   switch (fileStorageKey) {
     case IndexedDatabaseKey:
       var dbname = window.location.pathname;
--- a/dom/filehandle/test/mochitest.ini
+++ b/dom/filehandle/test/mochitest.ini
@@ -1,32 +1,34 @@
 [DEFAULT]
 skip-if = e10s
 support-files =
   dummy_worker.js
   helpers.js
 
 [test_append_read_data.html]
 skip-if = buildapp == 'b2g'
+[test_compat.html]
+skip-if = buildapp == 'b2g'
+[test_filehandle_lifetimes.html]
+skip-if = buildapp == 'b2g'
+[test_filehandle_lifetimes_nested.html]
+skip-if = buildapp == 'b2g'
+[test_filehandle_ordering.html]
+skip-if = buildapp == 'b2g'
 [test_getFile.html]
 skip-if = buildapp == 'b2g'
 [test_getFileId.html]
 [test_location.html]
 skip-if = buildapp == 'b2g'
-[test_lockedfile_lifetimes.html]
-skip-if = buildapp == 'b2g'
-[test_lockedfile_lifetimes_nested.html]
-skip-if = buildapp == 'b2g'
-[test_lockedfile_ordering.html]
-skip-if = buildapp == 'b2g'
-[test_overlapping_lockedfiles.html]
+[test_overlapping_filehandles.html]
 skip-if = buildapp == 'b2g'
 [test_progress_events.html]
 skip-if = buildapp == 'b2g' # b2g(All of these fail fairly regularly with: UnknownError: The operation failed for reasons unrelated to the database itself and not covered by any other error code. at http://mochi.test:8888/tests/dom/file/test/helpers.js:109) b2g-debug(All of these fail fairly regularly with: UnknownError: The operation failed for reasons unrelated to the database itself and not covered by any other error code. at http://mochi.test:8888/tests/dom/file/test/helpers.js:109) b2g-desktop(All of these fail fairly regularly with: UnknownError: The operation failed for reasons unrelated to the database itself and not covered by any other error code. at http://mochi.test:8888/tests/dom/file/test/helpers.js:109)
-[test_readonly_lockedfiles.html]
+[test_readonly_filehandles.html]
 skip-if = buildapp == 'b2g'
 [test_request_readyState.html]
 skip-if = buildapp == 'b2g'
 [test_stream_tracking.html]
 skip-if = buildapp == 'b2g'
 [test_success_events_after_abort.html]
 skip-if = buildapp == 'b2g'
 [test_truncate.html]
--- a/dom/filehandle/test/test_append_read_data.html
+++ b/dom/filehandle/test/test_append_read_data.html
@@ -17,75 +17,75 @@
       testString += testString;
     }
 
     var testBuffer = getRandomBuffer(100000);
 
     var testBlob = getBlob("binary/random", testBuffer);
 
     for each (let fileStorage in fileStorages) {
-      let request = getFileHandle(fileStorage.key, "test.txt");
+      let request = getMutableFile(fileStorage.key, "test.txt");
       request.onerror = errorHandler;
       request.onsuccess = grabEventAndContinueHandler;
       let event = yield undefined;
 
-      let fileHandle = event.target.result;
-      fileHandle.onerror = errorHandler;
+      let mutableFile = event.target.result;
+      mutableFile.onerror = errorHandler;
 
       let location = 0;
 
-      let lockedFile = fileHandle.open("readwrite");
-      is(lockedFile.location, location, "Correct location");
+      let fileHandle = mutableFile.open("readwrite");
+      is(fileHandle.location, location, "Correct location");
 
-      request = lockedFile.append(testString);
-      ok(lockedFile.location === null, "Correct location");
+      request = fileHandle.append(testString);
+      ok(fileHandle.location === null, "Correct location");
       request.onsuccess = grabEventAndContinueHandler;
       event = yield undefined;
 
-      lockedFile.location = 0;
-      request = lockedFile.readAsText(testString.length);
+      fileHandle.location = 0;
+      request = fileHandle.readAsText(testString.length);
       location += testString.length
-      is(lockedFile.location, location, "Correct location");
+      is(fileHandle.location, location, "Correct location");
       request.onsuccess = grabEventAndContinueHandler;
       event = yield undefined;
 
       let resultString = event.target.result;
       ok(resultString == testString, "Correct string data");
 
-      request = lockedFile.append(testBuffer);
-      ok(lockedFile.location === null, "Correct location");
+      request = fileHandle.append(testBuffer);
+      ok(fileHandle.location === null, "Correct location");
       request.onsuccess = grabEventAndContinueHandler;
       event = yield undefined;
 
-      lockedFile.location = location;
-      request = lockedFile.readAsArrayBuffer(testBuffer.byteLength);
+      fileHandle.location = location;
+      request = fileHandle.readAsArrayBuffer(testBuffer.byteLength);
       location += testBuffer.byteLength;
-      is(lockedFile.location, location, "Correct location");
+      is(fileHandle.location, location, "Correct location");
       request.onsuccess = grabEventAndContinueHandler;
       event = yield undefined;
 
       let resultBuffer = event.target.result;
       ok(compareBuffers(resultBuffer, testBuffer), "Correct array buffer data");
 
-      request = lockedFile.append(testBlob);
-      ok(lockedFile.location === null, "Correct location");
+      request = fileHandle.append(testBlob);
+      ok(fileHandle.location === null, "Correct location");
       request.onsuccess = grabEventAndContinueHandler;
       event = yield undefined;
 
-      lockedFile.location = location;
-      request = lockedFile.readAsArrayBuffer(testBlob.size);
+      fileHandle.location = location;
+      request = fileHandle.readAsArrayBuffer(testBlob.size);
       location += testBlob.size;
-      is(lockedFile.location, location, "Correct location");
+      is(fileHandle.location, location, "Correct location");
       request.onsuccess = grabEventAndContinueHandler;
       event = yield undefined;
 
       resultBuffer = event.target.result;
       ok(compareBuffers(resultBuffer, testBuffer), "Correct blob data");
 
-      request = lockedFile.getMetadata({ size: true });
+      request = fileHandle.getMetadata({ size: true });
       request.onsuccess = grabEventAndContinueHandler;
       event = yield undefined;
 
       let result = event.target.result;
       is(result.size, location, "Correct size");
     }
 
     finishTest();
new file mode 100644
--- /dev/null
+++ b/dom/filehandle/test/test_compat.html
@@ -0,0 +1,41 @@
+<!--
+  Any copyright is dedicated to the Public Domain.
+  http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<html>
+<head>
+  <title>File Handle Test</title>
+
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+
+  <script type="text/javascript;version=1.7">
+  function testSteps()
+  {
+    for each (let fileStorage in fileStorages) {
+      let request = getFileHandle(fileStorage.key, "test.txt");
+      request.onerror = errorHandler;
+      request.onsuccess = grabEventAndContinueHandler;
+      let event = yield undefined;
+
+      let fileHandle = event.target.result;
+      fileHandle.onerror = errorHandler;
+
+      let lockedFile = fileHandle.open();
+      ok(lockedFile.fileHandle === fileHandle, "Correct property");
+
+      request = lockedFile.getMetadata({ size: true });
+      ok(request.lockedFile === lockedFile, "Correct property");
+    }
+
+    finishTest();
+    yield undefined;
+  }
+  </script>
+  <script type="text/javascript;version=1.7" src="helpers.js"></script>
+
+</head>
+
+<body onload="runTest();"></body>
+
+</html>
rename from dom/filehandle/test/test_lockedfile_lifetimes.html
rename to dom/filehandle/test/test_filehandle_lifetimes.html
--- a/dom/filehandle/test/test_lockedfile_lifetimes.html
+++ b/dom/filehandle/test/test_filehandle_lifetimes.html
@@ -8,35 +8,35 @@
 
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
 
   <script type="text/javascript;version=1.7">
   function testSteps()
   {
     for each (let fileStorage in fileStorages) {
-      let request = getFileHandle(fileStorage.key, "test.txt");
+      let request = getMutableFile(fileStorage.key, "test.txt");
       request.onerror = errorHandler;
       request.onsuccess = grabEventAndContinueHandler;
       let event = yield undefined;
 
-      let fileHandle = event.target.result;
-      fileHandle.onerror = errorHandler;
+      let mutableFile = event.target.result;
+      mutableFile.onerror = errorHandler;
 
-      let lockedFile = fileHandle.open();
+      let fileHandle = mutableFile.open();
       continueToNextStep();
       yield undefined;
 
       try {
-        lockedFile.getMetadata({ size: true });
+        fileHandle.getMetadata({ size: true });
         ok(false, "Should have thrown!");
       }
       catch (e) {
         ok(e instanceof DOMException, "Got exception.");
-        is(e.name, "LockedFileInactiveError", "Good error.");
+        is(e.name, "FileHandleInactiveError", "Good error.");
         is(e.code, 0, "Good error code.");
       }
     }
 
     finishTest();
     yield undefined;
   }
   </script>
rename from dom/filehandle/test/test_lockedfile_lifetimes_nested.html
rename to dom/filehandle/test/test_filehandle_lifetimes_nested.html
--- a/dom/filehandle/test/test_lockedfile_lifetimes_nested.html
+++ b/dom/filehandle/test/test_filehandle_lifetimes_nested.html
@@ -8,46 +8,46 @@
 
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
 
   <script type="text/javascript;version=1.7">
   function testSteps()
   {
     for each (let fileStorage in fileStorages) {
-      let request = getFileHandle(fileStorage.key, "test.txt");
+      let request = getMutableFile(fileStorage.key, "test.txt");
       request.onerror = errorHandler;
       request.onsuccess = grabEventAndContinueHandler;
       let event = yield undefined;
 
-      let fileHandle = event.target.result;
-      fileHandle.onerror = errorHandler;
+      let mutableFile = event.target.result;
+      mutableFile.onerror = errorHandler;
 
-      let lockedFile = fileHandle.open();
+      let fileHandle = mutableFile.open();
 
-      let lockedFile2;
+      let fileHandle2;
 
       let comp = SpecialPowers.wrap(SpecialPowers.Components);
       let thread = comp.classes["@mozilla.org/thread-manager;1"]
                        .getService(comp.interfaces.nsIThreadManager)
                        .currentThread;
 
       let eventHasRun;
 
       thread.dispatch(function() {
         eventHasRun = true;
 
-        lockedFile2 = fileHandle.open();
+        fileHandle2 = mutableFile.open();
       }, SpecialPowers.Ci.nsIThread.DISPATCH_NORMAL);
 
       while (!eventHasRun) {
         thread.processNextEvent(false);
       }
 
-      ok(lockedFile2, "Non-null lockedFile2");
+      ok(fileHandle2, "Non-null fileHandle2");
 
       continueToNextStep();
       yield undefined;
     }
 
     finishTest();
     yield undefined;
   }
rename from dom/filehandle/test/test_lockedfile_ordering.html
rename to dom/filehandle/test/test_filehandle_ordering.html
--- a/dom/filehandle/test/test_lockedfile_ordering.html
+++ b/dom/filehandle/test/test_filehandle_ordering.html
@@ -8,42 +8,42 @@
 
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
 
   <script type="text/javascript;version=1.7">
   function testSteps()
   {
     for each (let fileStorage in fileStorages) {
-      let request = getFileHandle(fileStorage.key, "test.txt");
+      let request = getMutableFile(fileStorage.key, "test.txt");
       request.onerror = errorHandler;
       request.onsuccess = grabEventAndContinueHandler;
       let event = yield undefined;
 
-      let fileHandle = event.target.result;
-      fileHandle.onerror = errorHandler;
+      let mutableFile = event.target.result;
+      mutableFile.onerror = errorHandler;
 
-      let lockedFile1 = fileHandle.open("readwrite");
-      let lockedFile2 = fileHandle.open("readwrite");
+      let fileHandle1 = mutableFile.open("readwrite");
+      let fileHandle2 = mutableFile.open("readwrite");
 
-      let request1 = lockedFile2.write("2");
-      let request2 = lockedFile1.write("1");
+      let request1 = fileHandle2.write("2");
+      let request2 = fileHandle1.write("1");
 
-      lockedFile1.oncomplete = grabEventAndContinueHandler;
-      lockedFile2.oncomplete = grabEventAndContinueHandler;
+      fileHandle1.oncomplete = grabEventAndContinueHandler;
+      fileHandle2.oncomplete = grabEventAndContinueHandler;
 
       yield undefined;
       yield undefined;
 
-      let lockedFile3 = fileHandle.open("readonly");
-      let request3 = lockedFile3.readAsText(1);
+      let fileHandle3 = mutableFile.open("readonly");
+      let request3 = fileHandle3.readAsText(1);
       request3.onsuccess = grabEventAndContinueHandler;
 
       event = yield undefined;
-      is(event.target.result, "2", "Locked files were ordered properly.");
+      is(event.target.result, "2", "File handles were ordered properly.");
     }
 
     finishTest();
     yield undefined;
   }
   </script>
   <script type="text/javascript;version=1.7" src="helpers.js"></script>
 
--- a/dom/filehandle/test/test_getFile.html
+++ b/dom/filehandle/test/test_getFile.html
@@ -8,27 +8,29 @@
 
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 
   <script type="text/javascript;version=1.7">
   function testSteps()
   {
     for each (let fileStorage in fileStorages) {
-      let request = getFileHandle(fileStorage.key, "test.txt");
+      let request = getMutableFile(fileStorage.key, "test.txt");
       request.onerror = errorHandler;
       request.onsuccess = grabEventAndContinueHandler;
       let event = yield undefined;
 
-      let fileHandle = event.target.result;
-      fileHandle.onerror = errorHandler;
+      let mutableFile = event.target.result;
+      mutableFile.onerror = errorHandler;
 
-      request = fileHandle.getFile();
+      request = mutableFile.getFile();
       ok(request instanceof DOMRequest, "Correct interface");
       ok(!(request instanceof FileRequest), "Correct interface");
+      ok(!('fileHandle' in request), "Property should not exist");
+      ok(request.fileHandle === undefined, "Property should not exist");
       ok(!('lockedFile' in request), "Property should not exist");
       ok(request.lockedFile === undefined, "Property should not exist");
       ok(!('onprogress' in request), "Property should not exist");
       ok(request.onprogress === undefined, "Property should not exist");
     }
 
     finishTest();
     yield undefined;
--- a/dom/filehandle/test/test_location.html
+++ b/dom/filehandle/test/test_location.html
@@ -8,84 +8,84 @@
 
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
 
   <script type="text/javascript;version=1.7">
   function testSteps()
   {
     for each (let fileStorage in fileStorages) {
-      let request = getFileHandle(fileStorage.key, "test.txt");
+      let request = getMutableFile(fileStorage.key, "test.txt");
       request.onerror = errorHandler;
       request.onsuccess = grabEventAndContinueHandler;
       let event = yield undefined;
 
-      let fileHandle = event.target.result;
-      fileHandle.onerror = errorHandler;
+      let mutableFile = event.target.result;
+      mutableFile.onerror = errorHandler;
 
-      let lockedFile = fileHandle.open("readwrite");
-      is(lockedFile.location, 0, "Correct location");
+      let fileHandle = mutableFile.open("readwrite");
+      is(fileHandle.location, 0, "Correct location");
 
-      lockedFile.location = 100000;
-      is(lockedFile.location, 100000, "Correct location");
+      fileHandle.location = 100000;
+      is(fileHandle.location, 100000, "Correct location");
 
-      lockedFile.location = null;
-      ok(lockedFile.location === null, "Correct location");
+      fileHandle.location = null;
+      ok(fileHandle.location === null, "Correct location");
 
       try {
-        lockedFile.readAsArrayBuffer(1);
+        fileHandle.readAsArrayBuffer(1);
         ok(false, "Should have thrown!");
       }
       catch (e) {
         ok(e instanceof DOMException, "Got exception.");
         is(e.name, "InvalidStateError", "Good error.");
         is(e.code, DOMException.INVALID_STATE_ERR, "Good error code.");
       }
 
       try {
-        lockedFile.readAsText(1);
+        fileHandle.readAsText(1);
         ok(false, "Should have thrown!");
       }
       catch (e) {
         ok(e instanceof DOMException, "Got exception.");
         is(e.name, "InvalidStateError", "Good error.");
         is(e.code, DOMException.INVALID_STATE_ERR, "Good error code.");
       }
 
       try {
-        lockedFile.write({});
+        fileHandle.write({});
         ok(false, "Should have thrown!");
       }
       catch (e) {
         ok(e instanceof DOMException, "Got exception.");
         is(e.name, "InvalidStateError", "Good error.");
         is(e.code, DOMException.INVALID_STATE_ERR, "Good error code.");
       }
 
-      request = lockedFile.append("foo");
+      request = fileHandle.append("foo");
       request.onsuccess = grabEventAndContinueHandler;
       event = yield undefined;
 
-      ok(lockedFile.location === null, "Correct location");
+      ok(fileHandle.location === null, "Correct location");
 
       try {
-        lockedFile.truncate();
+        fileHandle.truncate();
         ok(false, "Should have thrown!");
       }
       catch (e) {
         ok(e instanceof DOMException, "Got exception.");
         is(e.name, "InvalidStateError", "Good error.");
         is(e.code, DOMException.INVALID_STATE_ERR, "Good error code.");
       }
 
-      request = lockedFile.truncate(0);
+      request = fileHandle.truncate(0);
       request.onsuccess = grabEventAndContinueHandler;
       event = yield undefined;
 
-      is(lockedFile.location, 0, "Correct location");
+      is(fileHandle.location, 0, "Correct location");
     }
 
     finishTest();
     yield undefined;
   }
   </script>
   <script type="text/javascript;version=1.7" src="helpers.js"></script>
 
rename from dom/filehandle/test/test_overlapping_lockedfiles.html
rename to dom/filehandle/test/test_overlapping_filehandles.html
--- a/dom/filehandle/test/test_overlapping_lockedfiles.html
+++ b/dom/filehandle/test/test_overlapping_filehandles.html
@@ -8,46 +8,46 @@
 
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
 
   <script type="text/javascript;version=1.7">
   function testSteps()
   {
     for each (let fileStorage in fileStorages) {
-      let request = getFileHandle(fileStorage.key, "test.txt");
+      let request = getMutableFile(fileStorage.key, "test.txt");
       request.onerror = errorHandler;
       request.onsuccess = grabEventAndContinueHandler;
       let event = yield undefined;
 
-      let fileHandle = event.target.result;
-      fileHandle.onerror = errorHandler;
+      let mutableFile = event.target.result;
+      mutableFile.onerror = errorHandler;
 
       for (let i = 0; i < 50; i++) {
         let stepNumber = 0;
 
-        request = fileHandle.open("readwrite").append("string1");
+        request = mutableFile.open("readwrite").append("string1");
         request.onsuccess = function(event) {
           is(stepNumber, 1, "This callback came first");
           stepNumber++;
-          event.target.lockedFile.oncomplete = grabEventAndContinueHandler;
+          event.target.fileHandle.oncomplete = grabEventAndContinueHandler;
         }
 
-        request = fileHandle.open("readwrite").append("string2");
+        request = mutableFile.open("readwrite").append("string2");
         request.onsuccess = function(event) {
           is(stepNumber, 2, "This callback came second");
           stepNumber++;
-          event.target.lockedFile.oncomplete = grabEventAndContinueHandler;
+          event.target.fileHandle.oncomplete = grabEventAndContinueHandler;
         }
 
-        request = fileHandle.open("readwrite").append("string3");
+        request = mutableFile.open("readwrite").append("string3");
         request.onsuccess = function(event) {
           is(stepNumber, 3, "This callback came third");
           stepNumber++;
-          event.target.lockedFile.oncomplete = grabEventAndContinueHandler;
+          event.target.fileHandle.oncomplete = grabEventAndContinueHandler;
         }
 
         stepNumber++;
         yield undefined; yield undefined; yield undefined;;
 
         is(stepNumber, 4, "All callbacks received");
       }
     }
--- a/dom/filehandle/test/test_progress_events.html
+++ b/dom/filehandle/test/test_progress_events.html
@@ -10,45 +10,45 @@
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
 
   <script type="text/javascript;version=1.7">
   function testSteps()
   {
     var testBuffer = getRandomBuffer(100000);
 
     for each (let fileStorage in fileStorages) {
-      let request = getFileHandle(fileStorage.key, "test.txt");
+      let request = getMutableFile(fileStorage.key, "test.txt");
       request.onerror = errorHandler;
       request.onsuccess = grabEventAndContinueHandler;
       let event = yield undefined;
 
-      let fileHandle = event.target.result;
-      fileHandle.onerror = errorHandler;
+      let mutableFile = event.target.result;
+      mutableFile.onerror = errorHandler;
 
-      let lockedFile = fileHandle.open("readwrite");
+      let fileHandle = mutableFile.open("readwrite");
 
       let sum = 0;
 
-      request = lockedFile.write(testBuffer);
+      request = fileHandle.write(testBuffer);
       request.onprogress = function(event) {
         let loaded = event.loaded;
         let total = event.total;
         ok(loaded >= 0 && loaded <= total, "Correct loaded progress");
         is(total, testBuffer.byteLength, "Correct total progress");
         sum += event.loaded - sum;
       }
       request.onsuccess = grabEventAndContinueHandler;
       event = yield undefined;
 
       is(sum, testBuffer.byteLength, "Correct loaded progress sum");
 
       sum = 0;
 
-      lockedFile.location = 0;
-      request = lockedFile.readAsArrayBuffer(testBuffer.byteLength);
+      fileHandle.location = 0;
+      request = fileHandle.readAsArrayBuffer(testBuffer.byteLength);
       request.onprogress = function(event) {
         let loaded = event.loaded;
         let total = event.total;
         ok(loaded >= 0 && loaded <= total, "Correct loaded progress");
         is(total, testBuffer.byteLength, "Correct total progress");
         sum += event.loaded - sum;
       }
       request.onsuccess = grabEventAndContinueHandler;
rename from dom/filehandle/test/test_readonly_lockedfiles.html
rename to dom/filehandle/test/test_readonly_filehandles.html
--- a/dom/filehandle/test/test_readonly_lockedfiles.html
+++ b/dom/filehandle/test/test_readonly_filehandles.html
@@ -8,60 +8,60 @@
 
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
 
   <script type="text/javascript;version=1.7">
   function testSteps()
   {
     for each (let fileStorage in fileStorages) {
-      let request = getFileHandle(fileStorage.key, "test.txt");
+      let request = getMutableFile(fileStorage.key, "test.txt");
       request.onerror = errorHandler;
       request.onsuccess = grabEventAndContinueHandler;
       let event = yield undefined;
 
-      let fileHandle = event.target.result;
-      fileHandle.onerror = errorHandler;
+      let mutableFile = event.target.result;
+      mutableFile.onerror = errorHandler;
 
-      request = fileHandle.open("readwrite").write({});
+      request = mutableFile.open("readwrite").write({});
       request.onsuccess = grabEventAndContinueHandler;
       event = yield undefined;
 
-      is(event.target.lockedFile.mode, "readwrite", "Correct mode");
+      is(event.target.fileHandle.mode, "readwrite", "Correct mode");
 
       try {
-        fileHandle.open().write({});
-        ok(false, "Writing to a readonly locked file should fail!");
+        mutableFile.open().write({});
+        ok(false, "Writing to a readonly file handle should fail!");
       }
       catch (e) {
-        ok(true, "Writing to a readonly locked file failed");
+        ok(true, "Writing to a readonly file handle failed");
       }
 
       try {
-        fileHandle.open().append({});
-        ok(false, "Appending to a readonly locked file should fail!");
+        mutableFile.open().append({});
+        ok(false, "Appending to a readonly file handle should fail!");
       }
       catch (e) {
-        ok(true, "Appending to a readonly locked file failed");
+        ok(true, "Appending to a readonly file handle failed");
       }
 
       try {
-        fileHandle.open().truncate({});
-        ok(false, "Truncating a readonly locked file should fail!");
+        mutableFile.open().truncate({});
+        ok(false, "Truncating a readonly file handle should fail!");
       }
       catch (e) {
-        ok(true, "Truncating a readonly locked file failed");
+        ok(true, "Truncating a readonly file handle failed");
       }
 
       try {
-        fileHandle.open().flush({});
-        ok(false, "Flushing a readonly locked file should fail!");
+        mutableFile.open().flush({});
+        ok(false, "Flushing a readonly file handle should fail!");
       }
       catch (e) {
-        ok(true, "Flushing a readonly locked file failed");
+        ok(true, "Flushing a readonly file handle failed");
       }
     }
 
     finishTest();
     yield undefined;
   }
   </script>
   <script type="text/javascript;version=1.7" src="helpers.js"></script>
--- a/dom/filehandle/test/test_request_readyState.html
+++ b/dom/filehandle/test/test_request_readyState.html
@@ -8,39 +8,39 @@
 
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
 
   <script type="text/javascript;version=1.7">
   function testSteps()
   {
     for each (let fileStorage in fileStorages) {
-      let request = getFileHandle(fileStorage.key, "test.txt");
+      let request = getMutableFile(fileStorage.key, "test.txt");
       is(request.readyState, "pending", "Correct readyState");
 
       request.onerror = errorHandler;
       request.onsuccess = grabEventAndContinueHandler;
       let event = yield undefined;
 
       is(request.readyState, "done", "Correct readyState");
 
-      let fileHandle = event.target.result;
-      fileHandle.onerror = errorHandler;
+      let mutableFile = event.target.result;
+      mutableFile.onerror = errorHandler;
 
-      let lockedFile = fileHandle.open("readwrite");
-      request = lockedFile.write("string");
+      let fileHandle = mutableFile.open("readwrite");
+      request = fileHandle.write("string");
       is(request.readyState, "pending", "Correct readyState");
 
       request.onsuccess = grabEventAndContinueHandler;
       event = yield undefined;
 
       is(request.readyState, "done", "Correct readyState");
 
-      lockedFile.location = 0;
-      request = lockedFile.readAsText(6);
+      fileHandle.location = 0;
+      request = fileHandle.readAsText(6);
       request.onsuccess = grabEventAndContinueHandler;
       is(request.readyState, "pending", "Correct readyState");
       event = yield undefined;
 
       ok(event.target.result, "Got something");
       is(request.readyState, "done", "Correct readyState");
     }
 
--- a/dom/filehandle/test/test_stream_tracking.html
+++ b/dom/filehandle/test/test_stream_tracking.html
@@ -10,31 +10,31 @@
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
 
   <script type="text/javascript;version=1.7">
   function testSteps()
   {
     var testBuffer = getRandomBuffer(100000);
 
     for each (let fileStorage in fileStorages) {
-      let request = getFileHandle(fileStorage.key, "test.txt");
+      let request = getMutableFile(fileStorage.key, "test.txt");
       request.onerror = errorHandler;
       request.onsuccess = grabEventAndContinueHandler;
       let event = yield undefined;
 
-      let fileHandle = event.target.result;
-      fileHandle.onerror = errorHandler;
+      let mutableFile = event.target.result;
+      mutableFile.onerror = errorHandler;
 
-      let lockedFile = fileHandle.open("readwrite");
+      let fileHandle = mutableFile.open("readwrite");
 
-      request = lockedFile.write(testBuffer);
+      request = fileHandle.write(testBuffer);
       request.onsuccess = grabEventAndContinueHandler;
       event = yield undefined;
 
-      request = fileHandle.getFile();
+      request = mutableFile.getFile();
       request.onsuccess = grabEventAndContinueHandler;
       event = yield undefined;
 
       let file = event.target.result;
 
       let resultBuffer1;
       let resultBuffer2;
 
@@ -47,17 +47,17 @@
 
         let reader = new FileReader();
         try {
           reader.readAsArrayBuffer(file);
           ok(false, "Should have thrown!");
         }
         catch (e) {
           ok(e instanceof DOMException, "Got exception.");
-          is(e.name, "LockedFileInactiveError", "Good error.");
+          is(e.name, "FileHandleInactiveError", "Good error.");
           is(e.code, 0, "Good error code.");
         }
 
         if (resultBuffer2) {
           testGenerator.next();
         }
       }
 
@@ -70,17 +70,17 @@
 
         let reader = new FileReader();
         try {
           reader.readAsArrayBuffer(file);
           ok(false, "Should have thrown!");
         }
         catch (e) {
           ok(e instanceof DOMException, "Got exception.");
-          is(e.name, "LockedFileInactiveError", "Good error.");
+          is(e.name, "FileHandleInactiveError", "Good error.");
           is(e.code, 0, "Good error code.");
         }
 
         if (resultBuffer1) {
           testGenerator.next();
         }
       }
 
--- a/dom/filehandle/test/test_success_events_after_abort.html
+++ b/dom/filehandle/test/test_success_events_after_abort.html
@@ -8,40 +8,40 @@
 
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
 
   <script type="text/javascript;version=1.7">
   function testSteps()
   {
     for each (let fileStorage in fileStorages) {
-      let request = getFileHandle(fileStorage.key, "test.txt");
+      let request = getMutableFile(fileStorage.key, "test.txt");
       request.onerror = errorHandler;
       request.onsuccess = grabEventAndContinueHandler;
       let event = yield undefined;
 
-      let fileHandle = event.target.result;
-      fileHandle.onerror = errorHandler;
+      let mutableFile = event.target.result;
+      mutableFile.onerror = errorHandler;
 
-      let lockedFile = fileHandle.open();
+      let fileHandle = mutableFile.open();
 
-      lockedFile.oncomplete = unexpectedSuccessHandler;
-      lockedFile.onabort = grabEventAndContinueHandler;
+      fileHandle.oncomplete = unexpectedSuccessHandler;
+      fileHandle.onabort = grabEventAndContinueHandler;
 
       let sawError = false;
 
-      request = lockedFile.getMetadata({ size: true });
+      request = fileHandle.getMetadata({ size: true });
       request.onsuccess = unexpectedSuccessHandler;
       request.onerror = function(event) {
         is(event.target.error.name, "AbortError", "Good error");
         sawError = true;
         event.stopPropagation();
       }
 
-      lockedFile.abort();
+      fileHandle.abort();
 
       event = yield undefined;
 
       is(event.type, "abort", "Got abort event");
       is(sawError, true, "Saw getMetadata() error");
 
       // Make sure the success event isn't queued somehow.
       let comp = SpecialPowers.wrap(SpecialPowers.Components);
--- a/dom/filehandle/test/test_truncate.html
+++ b/dom/filehandle/test/test_truncate.html
@@ -10,63 +10,63 @@
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
 
   <script type="text/javascript;version=1.7">
   function testSteps()
   {
     var testBuffer = getRandomBuffer(100000);
 
     for each (let fileStorage in fileStorages) {
-      let request = getFileHandle(fileStorage.key, "test.bin");
+      let request = getMutableFile(fileStorage.key, "test.bin");
       request.onerror = errorHandler;
       request.onsuccess = grabEventAndContinueHandler;
       let event = yield undefined;
 
-      let fileHandle = event.target.result;
-      fileHandle.onerror = errorHandler;
+      let mutableFile = event.target.result;
+      mutableFile.onerror = errorHandler;
 
-      let lockedFile = fileHandle.open("readwrite");
-      request = lockedFile.write(testBuffer);
+      let fileHandle = mutableFile.open("readwrite");
+      request = fileHandle.write(testBuffer);
       request.onsuccess = grabEventAndContinueHandler;
       event = yield undefined;
 
-      is(lockedFile.location, 100000, "Correct location");
+      is(fileHandle.location, 100000, "Correct location");
 
       for (let i = 0; i < 10; i++) {
-        let location = lockedFile.location - 10000;
-        lockedFile.location = location;
+        let location = fileHandle.location - 10000;
+        fileHandle.location = location;
 
-        request = lockedFile.truncate();
+        request = fileHandle.truncate();
         request.onsuccess = grabEventAndContinueHandler;
         event = yield undefined;
 
-        is(lockedFile.location, location, "Correct location");
+        is(fileHandle.location, location, "Correct location");
 
-        request = lockedFile.getMetadata({ size: true });
+        request = fileHandle.getMetadata({ size: true });
         request.onsuccess = grabEventAndContinueHandler;
         event = yield undefined;
 
         is(event.target.result.size, location, "Correct size");
       }
 
-      request = lockedFile.write(testBuffer);
+      request = fileHandle.write(testBuffer);
       request.onsuccess = grabEventAndContinueHandler;
       event = yield undefined;
 
-      let location = lockedFile.location;
+      let location = fileHandle.location;
       for (let i = 0; i < 10; i++) {
         location -= 10000;
 
-        request = lockedFile.truncate(location);
+        request = fileHandle.truncate(location);
         request.onsuccess = grabEventAndContinueHandler;
         event = yield undefined;
 
-        is(lockedFile.location, location, "Correct location");
+        is(fileHandle.location, location, "Correct location");
 
-        request = lockedFile.getMetadata({ size: true });
+        request = fileHandle.getMetadata({ size: true });
         request.onsuccess = grabEventAndContinueHandler;
         event = yield undefined;
 
         is(event.target.result.size, location, "Correct size");
       }
     }
 
     finishTest();
--- a/dom/filehandle/test/test_workers.html
+++ b/dom/filehandle/test/test_workers.html
@@ -10,31 +10,31 @@
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 
   <script type="text/javascript;version=1.7">
   function testSteps()
   {
     var testBuffer = getRandomBuffer(100000);
 
     for each (let fileStorage in fileStorages) {
-      let request = getFileHandle(fileStorage.key, "test.txt");
+      let request = getMutableFile(fileStorage.key, "test.txt");
       request.onerror = errorHandler;
       request.onsuccess = grabEventAndContinueHandler;
       let event = yield undefined;
 
-      let fileHandle = event.target.result;
-      fileHandle.onerror = errorHandler;
+      let mutableFile = event.target.result;
+      mutableFile.onerror = errorHandler;
 
-      let lockedFile = fileHandle.open("readwrite");
+      let fileHandle = mutableFile.open("readwrite");
 
-      request = lockedFile.write(testBuffer);
+      request = fileHandle.write(testBuffer);
       request.onsuccess = grabEventAndContinueHandler;
       event = yield undefined;
 
-      request = fileHandle.getFile();
+      request = mutableFile.getFile();
       request.onsuccess = grabEventAndContinueHandler;
       event = yield undefined;
 
       let file = event.target.result;
 
       var worker = new Worker("dummy_worker.js");
       try {
         worker.postMessage(file);
--- a/dom/filehandle/test/test_write_read_data.html
+++ b/dom/filehandle/test/test_write_read_data.html
@@ -17,75 +17,75 @@
       testString += testString;
     }
 
     var testBuffer = getRandomBuffer(100000);
 
     var testBlob = getBlob("binary/random", testBuffer);
 
     for each (let fileStorage in fileStorages) {
-      let request = getFileHandle(fileStorage.key, "test.txt");
+      let request = getMutableFile(fileStorage.key, "test.txt");
       request.onerror = errorHandler;
       request.onsuccess = grabEventAndContinueHandler;
       let event = yield undefined;
 
-      let fileHandle = event.target.result;
-      fileHandle.onerror = errorHandler;
+      let mutableFile = event.target.result;
+      mutableFile.onerror = errorHandler;
 
       let location = 0;
 
-      let lockedFile = fileHandle.open("readwrite");
-      is(lockedFile.location, location, "Correct location");
+      let fileHandle = mutableFile.open("readwrite");
+      is(fileHandle.location, location, "Correct location");
 
-      request = lockedFile.write(testString);
+      request = fileHandle.write(testString);
       location += testString.length;
-      is(lockedFile.location, location, "Correct location");
+      is(fileHandle.location, location, "Correct location");
       request.onsuccess = grabEventAndContinueHandler;
       event = yield undefined;
 
-      lockedFile.location = 0;
-      request = lockedFile.readAsText(testString.length);
-      is(lockedFile.location, location, "Correct location");
+      fileHandle.location = 0;
+      request = fileHandle.readAsText(testString.length);
+      is(fileHandle.location, location, "Correct location");
       request.onsuccess = grabEventAndContinueHandler;
       event = yield undefined;
 
       let resultString = event.target.result;
       ok(resultString == testString, "Correct string data");
 
-      request = lockedFile.write(testBuffer);
+      request = fileHandle.write(testBuffer);
       location += testBuffer.byteLength;
-      is(lockedFile.location, location, "Correct location");
+      is(fileHandle.location, location, "Correct location");
       request.onsuccess = grabEventAndContinueHandler;
       event = yield undefined;
 
-      lockedFile.location -= testBuffer.byteLength;
-      request = lockedFile.readAsArrayBuffer(testBuffer.byteLength);
-      is(lockedFile.location, location, "Correct location");
+      fileHandle.location -= testBuffer.byteLength;
+      request = fileHandle.readAsArrayBuffer(testBuffer.byteLength);
+      is(fileHandle.location, location, "Correct location");
       request.onsuccess = grabEventAndContinueHandler;
       event = yield undefined;
 
       let resultBuffer = event.target.result;
       ok(compareBuffers(resultBuffer, testBuffer), "Correct array buffer data");
 
-      request = lockedFile.write(testBlob);
+      request = fileHandle.write(testBlob);
       location += testBlob.size;
-      is(lockedFile.location, location, "Correct location");
+      is(fileHandle.location, location, "Correct location");
       request.onsuccess = grabEventAndContinueHandler;
       event = yield undefined;
 
-      lockedFile.location -= testBlob.size;
-      request = lockedFile.readAsArrayBuffer(testBlob.size);
-      is(lockedFile.location, location, "Correct location");
+      fileHandle.location -= testBlob.size;
+      request = fileHandle.readAsArrayBuffer(testBlob.size);
+      is(fileHandle.location, location, "Correct location");
       request.onsuccess = grabEventAndContinueHandler;
       event = yield undefined;
 
       resultBuffer = event.target.result;
       ok(compareBuffers(resultBuffer, testBuffer), "Correct blob data");
 
-      request = lockedFile.getMetadata({ size: true });
+      request = fileHandle.getMetadata({ size: true });
       request.onsuccess = grabEventAndContinueHandler;
       event = yield undefined;
 
       let result = event.target.result;
       is(result.size, location, "Correct size");
     }
 
     finishTest();
--- a/dom/indexedDB/IDBDatabase.cpp
+++ b/dom/indexedDB/IDBDatabase.cpp
@@ -19,18 +19,18 @@
 #include "nsJSUtils.h"
 #include "nsProxyRelease.h"
 #include "nsThreadUtils.h"
 
 #include "AsyncConnectionHelper.h"
 #include "DatabaseInfo.h"
 #include "IDBEvents.h"
 #include "IDBFactory.h"
-#include "IDBFileHandle.h"
 #include "IDBIndex.h"
+#include "IDBMutableFile.h"
 #include "IDBObjectStore.h"
 #include "IDBTransaction.h"
 #include "IDBFactory.h"
 #include "ProfilerHelpers.h"
 #include "ReportInternalError.h"
 #include "TransactionThreadPool.h"
 
 #include "ipc/IndexedDBChild.h"
@@ -679,19 +679,19 @@ IDBDatabase::Transaction(const Sequence<
                     "IDBTransaction[%llu] MT Started",
                     transaction->GetSerialNumber(), IDB_PROFILER_STRING(this),
                     IDB_PROFILER_STRING(transaction));
 
   return transaction.forget();
 }
 
 already_AddRefed<IDBRequest>
-IDBDatabase::MozCreateFileHandle(const nsAString& aName,
-                                 const Optional<nsAString>& aType,
-                                 ErrorResult& aRv)
+IDBDatabase::CreateMutableFile(const nsAString& aName,
+                               const Optional<nsAString>& aType,
+                               ErrorResult& aRv)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
   if (!IndexedDatabaseManager::IsMainProcess()) {
     IDB_WARNING("Not supported yet!");
     aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
     return nullptr;
   }
@@ -925,14 +925,14 @@ CreateFileHelper::DoDatabaseWork(mozISto
 
   return NS_OK;
 }
 
 nsresult
 CreateFileHelper::GetSuccessResult(JSContext* aCx,
                                    JS::MutableHandle<JS::Value> aVal)
 {
-  nsRefPtr<IDBFileHandle> fileHandle =
-    IDBFileHandle::Create(mName, mType, mDatabase, mFileInfo.forget());
-  IDB_ENSURE_TRUE(fileHandle, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
+  nsRefPtr<IDBMutableFile> mutableFile =
+    IDBMutableFile::Create(mName, mType, mDatabase, mFileInfo.forget());
+  IDB_ENSURE_TRUE(mutableFile, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
-  return WrapNative(aCx, NS_ISUPPORTS_CAST(EventTarget*, fileHandle), aVal);
+  return WrapNative(aCx, NS_ISUPPORTS_CAST(EventTarget*, mutableFile), aVal);
 }
--- a/dom/indexedDB/IDBDatabase.h
+++ b/dom/indexedDB/IDBDatabase.h
@@ -221,18 +221,25 @@ public:
 
   mozilla::dom::StorageType
   Storage() const
   {
     return PersistenceTypeToStorage(mPersistenceType);
   }
 
   already_AddRefed<IDBRequest>
+  CreateMutableFile(const nsAString& aName, const Optional<nsAString>& aType,
+                    ErrorResult& aRv);
+
+  already_AddRefed<IDBRequest>
   MozCreateFileHandle(const nsAString& aName, const Optional<nsAString>& aType,
-                      ErrorResult& aRv);
+                      ErrorResult& aRv)
+  {
+    return CreateMutableFile(aName, aType, aRv);
+  }
 
   virtual void LastRelease() MOZ_OVERRIDE;
 
 private:
   IDBDatabase(IDBWrapperCache* aOwnerCache);
   ~IDBDatabase();
 
   void OnUnlink();
rename from dom/indexedDB/IDBFileHandle.cpp
rename to dom/indexedDB/IDBMutableFile.cpp
--- a/dom/indexedDB/IDBFileHandle.cpp
+++ b/dom/indexedDB/IDBMutableFile.cpp
@@ -1,18 +1,18 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#include "IDBFileHandle.h"
+#include "IDBMutableFile.h"
 
 #include "mozilla/dom/File.h"
-#include "mozilla/dom/IDBFileHandleBinding.h"
+#include "mozilla/dom/IDBMutableFileBinding.h"
 #include "mozilla/dom/quota/FileStreams.h"
 #include "mozilla/dom/quota/QuotaManager.h"
 
 #include "IDBDatabase.h"
 
 USING_INDEXEDDB_NAMESPACE
 USING_QUOTA_NAMESPACE
 
@@ -31,42 +31,42 @@ GetFileFor(FileInfo* aFileInfo)
                                                      aFileInfo->Id());
   NS_ENSURE_TRUE(file, nullptr);
 
   return file.forget();
 }
 
 } // anonymous namespace
 
-IDBFileHandle::IDBFileHandle(IDBDatabase* aOwner)
-  : FileHandle(aOwner)
+IDBMutableFile::IDBMutableFile(IDBDatabase* aOwner)
+  : MutableFile(aOwner)
 {
 }
 
-NS_IMPL_CYCLE_COLLECTION_INHERITED(IDBFileHandle, FileHandle, mDatabase)
+NS_IMPL_CYCLE_COLLECTION_INHERITED(IDBMutableFile, MutableFile, mDatabase)
 
-NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(IDBFileHandle)
-NS_INTERFACE_MAP_END_INHERITING(FileHandle)
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(IDBMutableFile)
+NS_INTERFACE_MAP_END_INHERITING(MutableFile)
 
-NS_IMPL_ADDREF_INHERITED(IDBFileHandle, FileHandle)
-NS_IMPL_RELEASE_INHERITED(IDBFileHandle, FileHandle)
+NS_IMPL_ADDREF_INHERITED(IDBMutableFile, MutableFile)
+NS_IMPL_RELEASE_INHERITED(IDBMutableFile, MutableFile)
 
 // static
-already_AddRefed<IDBFileHandle>
-IDBFileHandle::Create(const nsAString& aName,
-                      const nsAString& aType,
-                      IDBDatabase* aDatabase,
-                      already_AddRefed<FileInfo> aFileInfo)
+already_AddRefed<IDBMutableFile>
+IDBMutableFile::Create(const nsAString& aName,
+                       const nsAString& aType,
+                       IDBDatabase* aDatabase,
+                       already_AddRefed<FileInfo> aFileInfo)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
   nsRefPtr<FileInfo> fileInfo(aFileInfo);
   NS_ASSERTION(fileInfo, "Null pointer!");
 
-  nsRefPtr<IDBFileHandle> newFile = new IDBFileHandle(aDatabase);
+  nsRefPtr<IDBMutableFile> newFile = new IDBMutableFile(aDatabase);
 
   newFile->mName = aName;
   newFile->mType = aType;
 
   newFile->mFile = GetFileFor(fileInfo);
   NS_ENSURE_TRUE(newFile->mFile, nullptr);
 
   newFile->mStorageId = aDatabase->Id();
@@ -74,35 +74,35 @@ IDBFileHandle::Create(const nsAString& a
 
   newFile->mDatabase = aDatabase;
   fileInfo.swap(newFile->mFileInfo);
 
   return newFile.forget();
 }
 
 bool
-IDBFileHandle::IsShuttingDown()
+IDBMutableFile::IsShuttingDown()
 {
-  return QuotaManager::IsShuttingDown() || FileHandle::IsShuttingDown();
+  return QuotaManager::IsShuttingDown() || MutableFile::IsShuttingDown();
 }
 
 bool
-IDBFileHandle::IsInvalid()
+IDBMutableFile::IsInvalid()
 {
   return mDatabase->IsInvalidated();
 }
 
 nsIOfflineStorage*
-IDBFileHandle::Storage()
+IDBMutableFile::Storage()
 {
   return mDatabase;
 }
 
 already_AddRefed<nsISupports>
-IDBFileHandle::CreateStream(nsIFile* aFile, bool aReadOnly)
+IDBMutableFile::CreateStream(nsIFile* aFile, bool aReadOnly)
 {
   PersistenceType persistenceType = mDatabase->Type();
   const nsACString& group = mDatabase->Group();
   const nsACString& origin = mDatabase->Origin();
 
   nsCOMPtr<nsISupports> result;
 
   if (aReadOnly) {
@@ -118,36 +118,36 @@ IDBFileHandle::CreateStream(nsIFile* aFi
     result = NS_ISUPPORTS_CAST(nsIFileStream*, stream);
   }
   NS_ENSURE_TRUE(result, nullptr);
 
   return result.forget();
 }
 
 void
-IDBFileHandle::SetThreadLocals()
+IDBMutableFile::SetThreadLocals()
 {
   MOZ_ASSERT(mDatabase->GetOwner(), "Should have owner!");
   QuotaManager::SetCurrentWindow(mDatabase->GetOwner());
 }
 
 void
-IDBFileHandle::UnsetThreadLocals()
+IDBMutableFile::UnsetThreadLocals()
 {
   QuotaManager::SetCurrentWindow(nullptr);
 }
 
 already_AddRefed<nsIDOMFile>
-IDBFileHandle::CreateFileObject(mozilla::dom::LockedFile* aLockedFile,
+IDBMutableFile::CreateFileObject(mozilla::dom::FileHandle* aFileHandle,
                                 uint32_t aFileSize)
 {
   nsCOMPtr<nsIDOMFile> file =
-    new File(mName, mType, aFileSize, mFile, aLockedFile, mFileInfo);
+    new File(mName, mType, aFileSize, mFile, aFileHandle, mFileInfo);
 
   return file.forget();
 }
 
 // virtual
 JSObject*
-IDBFileHandle::WrapObject(JSContext* aCx)
+IDBMutableFile::WrapObject(JSContext* aCx)
 {
-  return IDBFileHandleBinding::Wrap(aCx, this);
+  return IDBMutableFileBinding::Wrap(aCx, this);
 }
rename from dom/indexedDB/IDBFileHandle.h
rename to dom/indexedDB/IDBMutableFile.h
--- a/dom/indexedDB/IDBFileHandle.h
+++ b/dom/indexedDB/IDBMutableFile.h
@@ -1,40 +1,40 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#ifndef mozilla_dom_indexeddb_idbfilehandle_h__
-#define mozilla_dom_indexeddb_idbfilehandle_h__
+#ifndef mozilla_dom_indexeddb_idbmutablefile_h__
+#define mozilla_dom_indexeddb_idbmutablefile_h__
 
 #include "IndexedDatabase.h"
 
 #include "MainThreadUtils.h"
 #include "mozilla/Assertions.h"
 #include "mozilla/Attributes.h"
-#include "mozilla/dom/FileHandle.h"
 #include "mozilla/dom/indexedDB/FileInfo.h"
+#include "mozilla/dom/MutableFile.h"
 #include "nsCycleCollectionParticipant.h"
 
 BEGIN_INDEXEDDB_NAMESPACE
 
 class IDBDatabase;
 
-class IDBFileHandle : public FileHandle
+class IDBMutableFile : public MutableFile
 {
-  typedef mozilla::dom::LockedFile LockedFile;
+  typedef mozilla::dom::FileHandle FileHandle;
 
 public:
   NS_DECL_ISUPPORTS_INHERITED
 
-  NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(IDBFileHandle, FileHandle)
+  NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(IDBMutableFile, MutableFile)
 
-  static already_AddRefed<IDBFileHandle>
+  static already_AddRefed<IDBMutableFile>
   Create(const nsAString& aName, const nsAString& aType,
          IDBDatabase* aDatabase, already_AddRefed<FileInfo> aFileInfo);
 
 
   virtual int64_t
   GetFileId() MOZ_OVERRIDE
   {
     return mFileInfo->Id();
@@ -60,37 +60,37 @@ public:
 
   virtual void
   SetThreadLocals() MOZ_OVERRIDE;
 
   virtual void
   UnsetThreadLocals() MOZ_OVERRIDE;
 
   virtual already_AddRefed<nsIDOMFile>
-  CreateFileObject(LockedFile* aLockedFile, uint32_t aFileSize) MOZ_OVERRIDE;
+  CreateFileObject(FileHandle* aFileHandle, uint32_t aFileSize) MOZ_OVERRIDE;
 
   // nsWrapperCache
   virtual JSObject*
   WrapObject(JSContext* aCx) MOZ_OVERRIDE;
 
   // WebIDL
   IDBDatabase*
   Database()
   {
     MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
 
     return mDatabase;
   }
 
 private:
-  IDBFileHandle(IDBDatabase* aOwner);
+  IDBMutableFile(IDBDatabase* aOwner);
 
-  ~IDBFileHandle()
+  ~IDBMutableFile()
   {
   }
 
   nsRefPtr<IDBDatabase> mDatabase;
   nsRefPtr<FileInfo> mFileInfo;
 };
 
 END_INDEXEDDB_NAMESPACE
 
-#endif // mozilla_dom_indexeddb_idbfilehandle_h__
+#endif // mozilla_dom_indexeddb_idbmutablefile_h__
--- a/dom/indexedDB/IDBObjectStore.cpp
+++ b/dom/indexedDB/IDBObjectStore.cpp
@@ -10,17 +10,17 @@
 
 #include "mozilla/dom/ipc/nsIRemoteBlob.h"
 #include "nsIOutputStream.h"
 
 #include <algorithm>
 #include "jsfriendapi.h"
 #include "mozilla/dom/ContentChild.h"
 #include "mozilla/dom/nsIContentParent.h"
-#include "mozilla/dom/FileHandleBinding.h"
+#include "mozilla/dom/MutableFileBinding.h"
 #include "mozilla/dom/StructuredCloneTags.h"
 #include "mozilla/dom/TabChild.h"
 #include "mozilla/dom/ipc/Blob.h"
 #include "mozilla/dom/quota/FileStreams.h"
 #include "mozilla/Endian.h"
 #include "mozilla/storage.h"
 #include "nsContentUtils.h"
 #include "nsDOMClassInfo.h"
@@ -29,19 +29,19 @@
 #include "nsJSUtils.h"
 #include "nsServiceManagerUtils.h"
 #include "nsThreadUtils.h"
 #include "snappy/snappy.h"
 
 #include "AsyncConnectionHelper.h"
 #include "IDBCursor.h"
 #include "IDBEvents.h"
-#include "IDBFileHandle.h"
 #include "IDBIndex.h"
 #include "IDBKeyRange.h"
+#include "IDBMutableFile.h"
 #include "IDBTransaction.h"
 #include "DatabaseInfo.h"
 #include "KeyPath.h"
 #include "ProfilerHelpers.h"
 #include "ReportInternalError.h"
 
 #include "ipc/IndexedDBChild.h"
 #include "ipc/IndexedDBParent.h"
@@ -58,17 +58,17 @@ using mozilla::dom::quota::FileOutputStr
 using mozilla::ErrorResult;
 using mozilla::fallible_t;
 using mozilla::LittleEndian;
 using mozilla::Move;
 using mozilla::NativeEndian;
 
 BEGIN_INDEXEDDB_NAMESPACE
 
-struct FileHandleData
+struct MutableFileData
 {
   nsString type;
   nsString name;
 };
 
 struct BlobOrFileData
 {
   BlobOrFileData()
@@ -774,29 +774,29 @@ ResolveMysteryBlob(nsIDOMBlob* aBlob, co
     return actor->SetMysteryBlobInfo(aContentType, aSize);
   }
   return true;
 }
 
 class MainThreadDeserializationTraits
 {
 public:
-  static JSObject* CreateAndWrapFileHandle(JSContext* aCx,
-                                           IDBDatabase* aDatabase,
-                                           StructuredCloneFile& aFile,
-                                           const FileHandleData& aData)
+  static JSObject* CreateAndWrapMutableFile(JSContext* aCx,
+                                            IDBDatabase* aDatabase,
+                                            StructuredCloneFile& aFile,
+                                            const MutableFileData& aData)
   {
     MOZ_ASSERT(NS_IsMainThread());
 
     nsRefPtr<FileInfo>& fileInfo = aFile.mFileInfo;
 
-    nsRefPtr<IDBFileHandle> fileHandle = IDBFileHandle::Create(aData.name,
+    nsRefPtr<IDBMutableFile> mutableFile = IDBMutableFile::Create(aData.name,
       aData.type, aDatabase, fileInfo.forget());
 
-    return fileHandle->WrapObject(aCx);
+    return mutableFile->WrapObject(aCx);
   }
 
   static JSObject* CreateAndWrapBlobOrFile(JSContext* aCx,
                                            IDBDatabase* aDatabase,
                                            StructuredCloneFile& aFile,
                                            const BlobOrFileData& aData)
   {
     MOZ_ASSERT(NS_IsMainThread());
@@ -876,22 +876,22 @@ public:
     return wrappedFile.toObjectOrNull();
   }
 };
 
 
 class CreateIndexDeserializationTraits
 {
 public:
-  static JSObject* CreateAndWrapFileHandle(JSContext* aCx,
-                                           IDBDatabase* aDatabase,
-                                           StructuredCloneFile& aFile,
-                                           const FileHandleData& aData)
+  static JSObject* CreateAndWrapMutableFile(JSContext* aCx,
+                                            IDBDatabase* aDatabase,
+                                            StructuredCloneFile& aFile,
+                                            const MutableFileData& aData)
   {
-    // FileHandle can't be used in index creation, so just make a dummy object.
+    // MutableFile can't be used in index creation, so just make a dummy object.
     return JS_NewObject(aCx, nullptr, JS::NullPtr(), JS::NullPtr());
   }
 
   static JSObject* CreateAndWrapBlobOrFile(JSContext* aCx,
                                            IDBDatabase* aDatabase,
                                            StructuredCloneFile& aFile,
                                            const BlobOrFileData& aData)
   {
@@ -1399,20 +1399,20 @@ StructuredCloneReadString(JSStructuredCl
     return false;
   }
 
   return true;
 }
 
 // static
 bool
-IDBObjectStore::ReadFileHandle(JSStructuredCloneReader* aReader,
-                               FileHandleData* aRetval)
+IDBObjectStore::ReadMutableFile(JSStructuredCloneReader* aReader,
+                                MutableFileData* aRetval)
 {
-  static_assert(SCTAG_DOM_FILEHANDLE == 0xFFFF8004,
+  static_assert(SCTAG_DOM_MUTABLEFILE == 0xFFFF8004,
                 "Update me!");
   MOZ_ASSERT(aReader && aRetval);
 
   nsCString type;
   if (!StructuredCloneReadString(aReader, type)) {
     return false;
   }
   CopyUTF8toUTF16(type, aRetval->type);
@@ -1438,17 +1438,17 @@ IDBObjectStore::ReadBlobOrFile(JSStructu
                 "Update me!");
   MOZ_ASSERT(aReader && aRetval);
   MOZ_ASSERT(aTag == SCTAG_DOM_FILE ||
              aTag == SCTAG_DOM_FILE_WITHOUT_LASTMODIFIEDDATE ||
              aTag == SCTAG_DOM_BLOB);
 
   aRetval->tag = aTag;
 
-  // If it's not a FileHandle, it's a Blob or a File.
+  // If it's not a MutableFile, it's a Blob or a File.
   uint64_t size;
   if (!JS_ReadBytes(aReader, &size, sizeof(uint64_t))) {
     NS_WARNING("Failed to read size!");
     return false;
   }
   aRetval->size = NativeEndian::swapFromLittleEndian(size);
 
   nsCString type;
@@ -1495,44 +1495,44 @@ IDBObjectStore::StructuredCloneReadCallb
                                             uint32_t aTag,
                                             uint32_t aData,
                                             void* aClosure)
 {
   // We need to statically assert that our tag values are what we expect
   // so that if people accidentally change them they notice.
   static_assert(SCTAG_DOM_BLOB == 0xFFFF8001 &&
                 SCTAG_DOM_FILE_WITHOUT_LASTMODIFIEDDATE == 0xFFFF8002 &&
-                SCTAG_DOM_FILEHANDLE == 0xFFFF8004 &&
+                SCTAG_DOM_MUTABLEFILE == 0xFFFF8004 &&
                 SCTAG_DOM_FILE == 0xFFFF8005,
                 "You changed our structured clone tag values and just ate "
                 "everyone's IndexedDB data.  I hope you are happy.");
 
   if (aTag == SCTAG_DOM_FILE_WITHOUT_LASTMODIFIEDDATE ||
-      aTag == SCTAG_DOM_FILEHANDLE ||
+      aTag == SCTAG_DOM_MUTABLEFILE ||
       aTag == SCTAG_DOM_BLOB ||
       aTag == SCTAG_DOM_FILE) {
     StructuredCloneReadInfo* cloneReadInfo =
       reinterpret_cast<StructuredCloneReadInfo*>(aClosure);
 
     if (aData >= cloneReadInfo->mFiles.Length()) {
       NS_ERROR("Bad blob index!");
       return nullptr;
     }
 
     StructuredCloneFile& file = cloneReadInfo->mFiles[aData];
     IDBDatabase* database = cloneReadInfo->mDatabase;
 
-    if (aTag == SCTAG_DOM_FILEHANDLE) {
-      FileHandleData data;
-      if (!ReadFileHandle(aReader, &data)) {
+    if (aTag == SCTAG_DOM_MUTABLEFILE) {
+      MutableFileData data;
+      if (!ReadMutableFile(aReader, &data)) {
         return nullptr;
       }
 
-      return DeserializationTraits::CreateAndWrapFileHandle(aCx, database,
-                                                            file, data);
+      return DeserializationTraits::CreateAndWrapMutableFile(aCx, database,
+                                                             file, data);
     }
 
     BlobOrFileData data;
     if (!ReadBlobOrFile(aReader, aTag, &data)) {
       return nullptr;
     }
 
     return DeserializationTraits::CreateAndWrapBlobOrFile(aCx, database,
@@ -1567,35 +1567,35 @@ IDBObjectStore::StructuredCloneWriteCall
     uint64_t value = 0;
     // Omit endian swap
     return JS_WriteBytes(aWriter, &value, sizeof(value));
   }
 
   IDBTransaction* transaction = cloneWriteInfo->mTransaction;
   FileManager* fileManager = transaction->Database()->Manager();
 
-  FileHandle* fileHandle = nullptr;
-  if (NS_SUCCEEDED(UNWRAP_OBJECT(FileHandle, aObj, fileHandle))) {
-    nsRefPtr<FileInfo> fileInfo = fileHandle->GetFileInfo();
-
-    // Throw when trying to store non IDB file handles or IDB file handles
+  MutableFile* mutableFile = nullptr;
+  if (NS_SUCCEEDED(UNWRAP_OBJECT(MutableFile, aObj, mutableFile))) {
+    nsRefPtr<FileInfo> fileInfo = mutableFile->GetFileInfo();
+
+    // Throw when trying to store non IDB mutable files or IDB mutable files
     // across databases.
     if (!fileInfo || fileInfo->Manager() != fileManager) {
       return false;
     }
 
-    NS_ConvertUTF16toUTF8 convType(fileHandle->Type());
+    NS_ConvertUTF16toUTF8 convType(mutableFile->Type());
     uint32_t convTypeLength =
       NativeEndian::swapToLittleEndian(convType.Length());
 
-    NS_ConvertUTF16toUTF8 convName(fileHandle->Name());
+    NS_ConvertUTF16toUTF8 convName(mutableFile->Name());
     uint32_t convNameLength =
       NativeEndian::swapToLittleEndian(convName.Length());
 
-    if (!JS_WriteUint32Pair(aWriter, SCTAG_DOM_FILEHANDLE,
+    if (!JS_WriteUint32Pair(aWriter, SCTAG_DOM_MUTABLEFILE,
                             cloneWriteInfo->mFiles.Length()) ||
         !JS_WriteBytes(aWriter, &convTypeLength, sizeof(uint32_t)) ||
         !JS_WriteBytes(aWriter, convType.get(), convType.Length()) ||
         !JS_WriteBytes(aWriter, &convNameLength, sizeof(uint32_t)) ||
         !JS_WriteBytes(aWriter, convName.get(), convName.Length())) {
       return false;
     }
 
--- a/dom/indexedDB/IDBObjectStore.h
+++ b/dom/indexedDB/IDBObjectStore.h
@@ -42,17 +42,17 @@ class IDBRequest;
 class IndexedDBObjectStoreChild;
 class IndexedDBObjectStoreParent;
 class Key;
 
 struct IndexInfo;
 struct IndexUpdateInfo;
 struct ObjectStoreInfo;
 
-struct FileHandleData;
+struct MutableFileData;
 struct BlobOrFileData;
 
 class IDBObjectStore MOZ_FINAL : public nsISupports,
                                  public nsWrapperCache
 {
 public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(IDBObjectStore)
@@ -382,18 +382,18 @@ protected:
   already_AddRefed<IDBIndex>
   CreateIndex(JSContext* aCx, const nsAString& aName, KeyPath& aKeyPath,
               const IDBIndexParameters& aOptionalParameters, ErrorResult& aRv);
 
   static void
   ClearStructuredCloneBuffer(JSAutoStructuredCloneBuffer& aBuffer);
 
   static bool
-  ReadFileHandle(JSStructuredCloneReader* aReader,
-                 FileHandleData* aRetval);
+  ReadMutableFile(JSStructuredCloneReader* aReader,
+                  MutableFileData* aRetval);
 
   static bool
   ReadBlobOrFile(JSStructuredCloneReader* aReader,
                  uint32_t aTag,
                  BlobOrFileData* aRetval);
 private:
   nsRefPtr<IDBTransaction> mTransaction;
 
--- a/dom/indexedDB/IndexedDatabaseManager.cpp
+++ b/dom/indexedDB/IndexedDatabaseManager.cpp
@@ -32,19 +32,19 @@
 #include "IDBFactory.h"
 #include "IDBKeyRange.h"
 #include "IDBRequest.h"
 
 // Bindings for ResolveConstructors
 #include "mozilla/dom/IDBCursorBinding.h"
 #include "mozilla/dom/IDBDatabaseBinding.h"
 #include "mozilla/dom/IDBFactoryBinding.h"
-#include "mozilla/dom/IDBFileHandleBinding.h"
+#include "mozilla/dom/IDBIndexBinding.h"
 #include "mozilla/dom/IDBKeyRangeBinding.h"
-#include "mozilla/dom/IDBIndexBinding.h"
+#include "mozilla/dom/IDBMutableFileBinding.h"
 #include "mozilla/dom/IDBObjectStoreBinding.h"
 #include "mozilla/dom/IDBOpenDBRequestBinding.h"
 #include "mozilla/dom/IDBRequestBinding.h"
 #include "mozilla/dom/IDBTransactionBinding.h"
 #include "mozilla/dom/IDBVersionChangeEventBinding.h"
 
 #define IDB_STR "indexedDB"
 
@@ -414,19 +414,19 @@ IndexedDatabaseManager::DefineIndexedDB(
   MOZ_ASSERT(nsContentUtils::IsCallerChrome(), "Only for chrome!");
   MOZ_ASSERT(js::GetObjectClass(aGlobal)->flags & JSCLASS_DOM_GLOBAL,
              "Passed object is not a global object!");
 
   if (!IDBCursorBinding::GetConstructorObject(aCx, aGlobal) ||
       !IDBCursorWithValueBinding::GetConstructorObject(aCx, aGlobal) ||
       !IDBDatabaseBinding::GetConstructorObject(aCx, aGlobal) ||
       !IDBFactoryBinding::GetConstructorObject(aCx, aGlobal) ||
-      !IDBFileHandleBinding::GetConstructorObject(aCx, aGlobal) ||
       !IDBIndexBinding::GetConstructorObject(aCx, aGlobal) ||
       !IDBKeyRangeBinding::GetConstructorObject(aCx, aGlobal) ||
+      !IDBMutableFileBinding::GetConstructorObject(aCx, aGlobal) ||
       !IDBObjectStoreBinding::GetConstructorObject(aCx, aGlobal) ||
       !IDBOpenDBRequestBinding::GetConstructorObject(aCx, aGlobal) ||
       !IDBRequestBinding::GetConstructorObject(aCx, aGlobal) ||
       !IDBTransactionBinding::GetConstructorObject(aCx, aGlobal) ||
       !IDBVersionChangeEventBinding::GetConstructorObject(aCx, aGlobal))
   {
     return false;
   }
--- a/dom/indexedDB/moz.build
+++ b/dom/indexedDB/moz.build
@@ -17,19 +17,19 @@ EXPORTS.mozilla.dom.indexedDB += [
     'Client.h',
     'DatabaseInfo.h',
     'FileInfo.h',
     'FileManager.h',
     'IDBCursor.h',
     'IDBDatabase.h',
     'IDBEvents.h',
     'IDBFactory.h',
-    'IDBFileHandle.h',
     'IDBIndex.h',
     'IDBKeyRange.h',
+    'IDBMutableFile.h',
     'IDBObjectStore.h',
     'IDBRequest.h',
     'IDBTransaction.h',
     'IDBWrapperCache.h',
     'IndexedDatabase.h',
     'IndexedDatabaseManager.h',
     'Key.h',
     'KeyPath.h',
@@ -40,18 +40,18 @@ UNIFIED_SOURCES += [
     'CheckPermissionsHelper.cpp',
     'Client.cpp',
     'DatabaseInfo.cpp',
     'FileInfo.cpp',
     'FileManager.cpp',
     'IDBDatabase.cpp',
     'IDBEvents.cpp',
     'IDBFactory.cpp',
-    'IDBFileHandle.cpp',
     'IDBKeyRange.cpp',
+    'IDBMutableFile.cpp',
     'IDBRequest.cpp',
     'IDBTransaction.cpp',
     'IDBWrapperCache.cpp',
     'IndexedDatabaseManager.cpp',
     'Key.cpp',
     'KeyPath.cpp',
     'OpenDatabaseHelper.cpp',
     'TransactionThreadPool.cpp',
--- a/dom/indexedDB/test/test_filehandle_quota.html
+++ b/dom/indexedDB/test/test_filehandle_quota.html
@@ -24,34 +24,34 @@
 
     is(event.type, "upgradeneeded", "Got correct event type");
 
     let db = event.target.result;
     db.onerror = errorHandler;
 
     event = yield undefined;
 
-    request = db.mozCreateFileHandle("test.bin", "binary");
+    request = db.createMutableFile("test.bin", "binary");
     request.onsuccess = grabEventAndContinueHandler;
     event = yield undefined;
 
-    let fileHandle = event.target.result;
-    fileHandle.onerror = errorHandler;
+    let mutableFile = event.target.result;
+    mutableFile.onerror = errorHandler;
 
-    let lockedFile = fileHandle.open("readwrite");
+    let fileHandle = mutableFile.open("readwrite");
 
     let blob = getNullBlob(DEFAULT_QUOTA);
 
-    request = lockedFile.write(blob);
+    request = fileHandle.write(blob);
     request.addEventListener("error",
                              new ExpectError("QuotaExceededError", true));
     request.onsuccess = unexpectedSuccessHandler;
     event = yield undefined;
 
-    lockedFile.oncomplete = grabEventAndContinueHandler;
+    fileHandle.oncomplete = grabEventAndContinueHandler;
     event = yield undefined;
 
     is(event.type, "complete", "Got correct event type");
 
     finishTest();
     yield undefined;
   }
   </script>
--- a/dom/indexedDB/test/test_filehandle_serialization.html
+++ b/dom/indexedDB/test/test_filehandle_serialization.html
@@ -42,48 +42,48 @@
 
       is(event.type, "success", "Got correct event type");
 
       databases.push(db);
     }
 
     let db1 = databases[0];
 
-    let request = db1.mozCreateFileHandle("random.bin", "binary/random");
+    let request = db1.createMutableFile("random.bin", "binary/random");
     request.onsuccess = grabEventAndContinueHandler;
     event = yield undefined;
 
-    let fileHandle = event.target.result;
-    is(getFileId(fileHandle), 1, "Correct file id");
-    is(fileHandle.name, "random.bin", "Correct name");
-    is(fileHandle.type, "binary/random", "Correct type");
+    let mutableFile = event.target.result;
+    is(getFileId(mutableFile), 1, "Correct file id");
+    is(mutableFile.name, "random.bin", "Correct name");
+    is(mutableFile.type, "binary/random", "Correct type");
 
     let trans = db1.transaction([objectStoreName], READ_WRITE);
     let objectStore = trans.objectStore(objectStoreName);
 
-    request = objectStore.add(fileHandle, 42);
+    request = objectStore.add(mutableFile, 42);
     request.onsuccess = grabEventAndContinueHandler;
     event = yield undefined;
 
     request = objectStore.get(42);
     request.onsuccess = grabEventAndContinueHandler;
     event = yield undefined;
 
     let result = event.target.result;
     is(getFileId(result), 1, "Correct file id");
-    is(result.name, fileHandle.name, "Correct name");
-    is(result.type, fileHandle.type, "Correct type");
+    is(result.name, mutableFile.name, "Correct name");
+    is(result.type, mutableFile.type, "Correct type");
 
     let db2 = databases[1];
 
     trans = db2.transaction([objectStoreName], READ_WRITE);
     objectStore = trans.objectStore(objectStoreName);
 
     try {
-      objectStore.add(fileHandle, 42);
+      objectStore.add(mutableFile, 42);
       ok(false, "Should have thrown!");
     }
     catch (e) {
       ok(e instanceof DOMException, "Got exception.");
       is(e.name, "DataCloneError", "Good error.");
       is(e.code, DOMException.DATA_CLONE_ERR, "Good error code.");
     }
 
--- a/dom/indexedDB/test/test_filehandle_store_snapshot.html
+++ b/dom/indexedDB/test/test_filehandle_store_snapshot.html
@@ -32,32 +32,32 @@
     db.onerror = errorHandler;
 
     let objectStore = db.createObjectStore(objectStoreName, { });
 
     event = yield undefined;
 
     is(event.type, "success", "Got correct event type");
 
-    request = db.mozCreateFileHandle("random.bin", "binary/random");
+    request = db.createMutableFile("random.bin", "binary/random");
     request.onsuccess = grabEventAndContinueHandler;
     event = yield undefined;
 
-    let fileHandle = event.target.result;
-    fileHandle.onerror = errorHandler;
+    let mutableFile = event.target.result;
+    mutableFile.onerror = errorHandler;
 
-    let lockedFile = fileHandle.open("readwrite");
+    let fileHandle = mutableFile.open("readwrite");
 
-    is(getFileId(fileHandle), 1, "Correct file id");
+    is(getFileId(mutableFile), 1, "Correct file id");
 
-    request = lockedFile.write(testFile);
+    request = fileHandle.write(testFile);
     request.onsuccess = grabEventAndContinueHandler;
     event = yield undefined;
 
-    request = fileHandle.getFile();
+    request = mutableFile.getFile();
     request.onsuccess = grabEventAndContinueHandler;
     event = yield undefined;
 
     let file = event.target.result;
 
     let trans = db.transaction([objectStoreName], READ_WRITE);
     let objectStore = trans.objectStore(objectStoreName);
 
@@ -68,17 +68,17 @@
     // At this moment, the file should not be readable anymore.
     let reader = new FileReader();
     try {
       reader.readAsArrayBuffer(file);
       ok(false, "Should have thrown!");
     }
     catch (e) {
       ok(e instanceof DOMException, "Got exception.");
-      is(e.name, "LockedFileInactiveError", "Good error.");
+      is(e.name, "FileHandleInactiveError", "Good error.");
       is(e.code, 0, "Good error code.");
     }
 
     request = objectStore.get(42);
     request.onsuccess = grabEventAndContinueHandler;
     event = yield undefined;
 
     verifyBlob(event.target.result, testFile, 2);
--- a/dom/indexedDB/test/test_get_filehandle.html
+++ b/dom/indexedDB/test/test_get_filehandle.html
@@ -18,30 +18,30 @@
     request.onerror = errorHandler;
     request.onsuccess = grabEventAndContinueHandler;
     let event = yield undefined;
 
     let db = event.target.result;
     db.onerror = errorHandler;
 
     if (SpecialPowers.isMainProcess()) {
-      request = db.mozCreateFileHandle("random.bin", "binary/random");
+      request = db.createMutableFile("random.bin", "binary/random");
       request.onsuccess = grabEventAndContinueHandler;
       event = yield undefined;
 
-      let fileHandle = event.target.result;
-      ok(fileHandle, "Got filehandle");
+      let mutableFile = event.target.result;
+      ok(mutableFile, "Got mutablefile");
     }
     else {
       try {
-        db.mozCreateFileHandle("random.bin", "binary/random");
+        db.createMutableFile("random.bin", "binary/random");
         ok(false, "Should have thrown!");
       }
       catch (ex) {
-        ok(true, "MozCreateFileHandle threw");
+        ok(true, "CreateMutableFile threw");
       }
     }
 
     finishTest();
     yield undefined;
   }
   </script>
   <script type="text/javascript;version=1.7" src="helpers.js"></script>
--- a/dom/interfaces/base/nsIDOMWindowUtils.idl
+++ b/dom/interfaces/base/nsIDOMWindowUtils.idl
@@ -43,17 +43,17 @@ interface nsIDOMFile;
 interface nsIFile;
 interface nsIDOMClientRect;
 interface nsIURI;
 interface nsIDOMEventTarget;
 interface nsIRunnable;
 interface nsICompositionStringSynthesizer;
 interface nsITranslationNodeList;
 
-[scriptable, uuid(8435ca79-517f-40ba-acd9-55201a2a119d)]
+[scriptable, uuid(75b7674b-3e6e-4fac-931b-8fd3c4e4e8d2)]
 interface nsIDOMWindowUtils : nsISupports {
 
   /**
    * Image animation mode of the window. When this attribute's value
    * is changed, the implementation should set all images in the window
    * to the given value. That is, when set to kDontAnimMode, all images
    * will stop animating. The attribute's value must be one of the
    * animationMode values from imgIContainer.
@@ -1649,24 +1649,16 @@ interface nsIDOMWindowUtils : nsISupport
 
    /*
     * Returns the value of a given property animated on the compositor thread.
     * If the property is NOT currently being animated on the compositor thread,
     * returns an empty string.
     */
    AString getOMTAStyle(in nsIDOMElement aElement, in AString aProperty);
 
-   /*
-    * Returns the value of a given property.  If the property is animated off
-    * the main thread, this function will fetch the correct value from the
-    * compositor.
-    */
-   AString getOMTAOrComputedStyle(in nsIDOMElement aElement,
-                                  in AString aProperty);
-
    /**
     * Get the content- and compositor-side APZ test data instances.
     * The return values are of type APZTestData (see APZTestData.webidl).
     */
    [implicit_jscontext] jsval getContentAPZTestData();
    [implicit_jscontext] jsval getCompositorAPZTestData();
 
    /**
--- a/dom/quota/QuotaManager.cpp
+++ b/dom/quota/QuotaManager.cpp
@@ -517,20 +517,20 @@ public:
   }
 
 private:
   // The QuotaManager holds this alive.
   SynchronizedOp* mOp;
   uint32_t mCountdown;
 };
 
-class WaitForLockedFilesToFinishRunnable MOZ_FINAL : public nsRunnable
+class WaitForFileHandlesToFinishRunnable MOZ_FINAL : public nsRunnable
 {
 public:
-  WaitForLockedFilesToFinishRunnable()
+  WaitForFileHandlesToFinishRunnable()
   : mBusy(true)
   { }
 
   NS_IMETHOD
   Run();
 
   bool
   IsBusy() const
@@ -1436,17 +1436,17 @@ QuotaManager::AbortCloseStoragesForWindo
       nsIOfflineStorage*& storage = array[j];
 
       if (storage->IsOwned(aWindow)) {
         if (NS_FAILED(storage->Close())) {
           NS_WARNING("Failed to close storage for dying window!");
         }
 
         if (utilized) {
-          service->AbortLockedFilesForStorage(storage);
+          service->AbortFileHandlesForStorage(storage);
         }
 
         if (activated) {
           client->AbortTransactionsForStorage(storage);
         }
       }
     }
   }
@@ -1473,17 +1473,17 @@ QuotaManager::HasOpenTransactions(nsPIDO
         liveStorages->Find(mLiveStorages);
       }
 
       nsTArray<nsIOfflineStorage*>& storages = liveStorages->ArrayAt(i);
       for (uint32_t j = 0; j < storages.Length(); j++) {
         nsIOfflineStorage*& storage = storages[j];
 
         if (storage->IsOwned(aWindow) &&
-            ((utilized && service->HasLockedFilesForStorage(storage)) ||
+            ((utilized && service->HasFileHandlesForStorage(storage)) ||
              (activated && client->HasTransactionsForStorage(storage)))) {
           return true;
         }
       }
     }
   }
 
   return false;
@@ -2345,35 +2345,35 @@ QuotaManager::Observe(nsISupports* aSubj
     if (gShutdown.exchange(true)) {
       NS_ERROR("Shutdown more than once?!");
     }
 
     if (IsMainProcess()) {
       FileService* service = FileService::Get();
       if (service) {
         // This should only wait for storages registered in this manager
-        // to complete. Other storages may still have running locked files.
+        // to complete. Other storages may still have running file handles.
         // If the necko service (thread pool) gets the shutdown notification
         // first then the sync loop won't be processed at all, otherwise it will
         // lock the main thread until all storages registered in this manager
         // are finished.
 
         nsTArray<uint32_t> indexes;
         for (uint32_t index = 0; index < Client::TYPE_MAX; index++) {
           if (mClients[index]->IsFileServiceUtilized()) {
             indexes.AppendElement(index);
           }
         }
 
         StorageMatcher<nsTArray<nsCOMPtr<nsIOfflineStorage>>> liveStorages;
         liveStorages.Find(mLiveStorages, &indexes);
 
         if (!liveStorages.IsEmpty()) {
-          nsRefPtr<WaitForLockedFilesToFinishRunnable> runnable =
-            new WaitForLockedFilesToFinishRunnable();
+          nsRefPtr<WaitForFileHandlesToFinishRunnable> runnable =
+            new WaitForFileHandlesToFinishRunnable();
 
           service->WaitForStoragesToComplete(liveStorages, runnable);
 
           nsIThread* thread = NS_GetCurrentThread();
           while (runnable->IsBusy()) {
             if (!NS_ProcessNextEvent(thread)) {
               NS_ERROR("Failed to process next event!");
               break;
@@ -3995,17 +3995,17 @@ WaitForTransactionsToFinishRunnable::Run
   NS_ENSURE_SUCCESS(rv, rv);
 
   // The listener is responsible for calling
   // QuotaManager::AllowNextSynchronizedOp.
   return NS_OK;
 }
 
 NS_IMETHODIMP
-WaitForLockedFilesToFinishRunnable::Run()
+WaitForFileHandlesToFinishRunnable::Run()
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
   mBusy = false;
 
   return NS_OK;
 }
 
--- a/dom/tests/mochitest/general/test_interfaces.html
+++ b/dom/tests/mochitest/general/test_interfaces.html
@@ -546,22 +546,22 @@ var interfaceNamesInGlobalScope =
     "IDBCursor",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "IDBCursorWithValue",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "IDBDatabase",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "IDBFactory",
 // IMPORTANT: Do not change this list without review from a DOM peer!
-    "IDBFileHandle",
-// IMPORTANT: Do not change this list without review from a DOM peer!
     "IDBIndex",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "IDBKeyRange",
 // IMPORTANT: Do not change this list without review from a DOM peer!
+    "IDBMutableFile",
+// IMPORTANT: Do not change this list without review from a DOM peer!
     "IDBObjectStore",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "IDBOpenDBRequest",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "IDBRequest",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "IDBTransaction",
 // IMPORTANT: Do not change this list without review from a DOM peer!
@@ -582,18 +582,16 @@ var interfaceNamesInGlobalScope =
     {name: "KeyPair", pref: "dom.webcrypto.enabled"},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "KeyboardEvent",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "LocalMediaStream",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "Location",
 // IMPORTANT: Do not change this list without review from a DOM peer!
-    "LockedFile",
-// IMPORTANT: Do not change this list without review from a DOM peer!
     "MediaElementAudioSourceNode",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "MediaError",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "MediaList",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "MediaQueryList",
 // IMPORTANT: Do not change this list without review from a DOM peer!
@@ -722,16 +720,18 @@ var interfaceNamesInGlobalScope =
     {name: "MozWifiStatusChangeEvent", b2g: true},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "MozWifiP2pGroupOwner", b2g: true, permission: "wifi-manage"},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "MozWifiP2pManager", b2g: true, permission: "wifi-manage"},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "MozWifiP2pStatusChangeEvent", b2g: true, permission: "wifi-manage"},
 // IMPORTANT: Do not change this list without review from a DOM peer!
+    "MutableFile",
+// IMPORTANT: Do not change this list without review from a DOM peer!
     "MutationEvent",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "MutationObserver",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "MutationRecord",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "Navigator",
 // IMPORTANT: Do not change this list without review from a DOM peer!
--- a/dom/webidl/FileHandle.webidl
+++ b/dom/webidl/FileHandle.webidl
@@ -1,18 +1,50 @@
-/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtaone at http://mozilla.org/MPL/2.0/. */
+
+dictionary DOMFileMetadataParameters
+{
+  boolean size = true;
+  boolean lastModified = true;
+};
 
-interface FileHandle : EventTarget {
-  readonly attribute DOMString name;
-  readonly attribute DOMString type;
+interface FileHandle : EventTarget
+{
+  readonly attribute MutableFile? mutableFile;
+  // this is deprecated due to renaming in the spec
+  readonly attribute MutableFile? fileHandle; // now mutableFile
+  readonly attribute FileMode mode;
+  readonly attribute boolean active;
+  attribute unsigned long long? location;
 
   [Throws]
-  LockedFile open(optional FileMode mode = "readonly");
+  FileRequest? getMetadata(optional DOMFileMetadataParameters parameters);
+  [Throws]
+  FileRequest? readAsArrayBuffer(unsigned long long size);
+  [Throws]
+  FileRequest? readAsText(unsigned long long size,
+                         optional DOMString? encoding = null);
 
   [Throws]
-  DOMRequest getFile();
+  FileRequest? write(ArrayBuffer value);
+  [Throws]
+  FileRequest? write(Blob value);
+  [Throws]
+  FileRequest? write(DOMString value);
+  [Throws]
+  FileRequest? append(ArrayBuffer value);
+  [Throws]
+  FileRequest? append(Blob value);
+  [Throws]
+  FileRequest? append(DOMString value);
+  [Throws]
+  FileRequest? truncate(optional unsigned long long size);
+  [Throws]
+  FileRequest? flush();
+  [Throws]
+  void abort();
 
+  attribute EventHandler oncomplete;
   attribute EventHandler onabort;
   attribute EventHandler onerror;
 };
--- a/dom/webidl/FileRequest.webidl
+++ b/dom/webidl/FileRequest.webidl
@@ -1,11 +1,14 @@
 /* -*- Mode: IDL; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 interface FileRequest : DOMRequest {
-  readonly attribute LockedFile? lockedFile;
+  readonly attribute FileHandle? fileHandle;
+  // this is deprecated due to renaming in the spec
+  readonly attribute FileHandle? lockedFile; // now fileHandle
 
   attribute EventHandler onprogress;
 };
+
--- a/dom/webidl/IDBDatabase.webidl
+++ b/dom/webidl/IDBDatabase.webidl
@@ -40,10 +40,14 @@ interface IDBDatabase : EventTarget {
                 attribute EventHandler       onversionchange;
 };
 
 partial interface IDBDatabase {
     [Pref="dom.indexedDB.experimental"]
     readonly    attribute StorageType        storage;
 
     [Throws]
-    IDBRequest mozCreateFileHandle (DOMString name, optional DOMString type);
+    IDBRequest createMutableFile (DOMString name, optional DOMString type);
+
+    // this is deprecated due to renaming in the spec
+    [Throws]
+    IDBRequest mozCreateFileHandle (DOMString name, optional DOMString type); // now createMutableFile
 };
rename from dom/webidl/IDBFileHandle.webidl
rename to dom/webidl/IDBMutableFile.webidl
--- a/dom/webidl/IDBFileHandle.webidl
+++ b/dom/webidl/IDBMutableFile.webidl
@@ -1,8 +1,8 @@
 /* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-interface IDBFileHandle : FileHandle {
+interface IDBMutableFile : MutableFile {
   readonly attribute IDBDatabase database;
 };
deleted file mode 100644
--- a/dom/webidl/LockedFile.webidl
+++ /dev/null
@@ -1,48 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this file,
- * You can obtaone at http://mozilla.org/MPL/2.0/. */
-
-dictionary DOMFileMetadataParameters
-{
-  boolean size = true;
-  boolean lastModified = true;
-};
-
-interface LockedFile : EventTarget
-{
-  readonly attribute FileHandle? fileHandle;
-  readonly attribute FileMode mode;
-  readonly attribute boolean active;
-  attribute unsigned long long? location;
-
-  [Throws]
-  FileRequest? getMetadata(optional DOMFileMetadataParameters parameters);
-  [Throws]
-  FileRequest? readAsArrayBuffer(unsigned long long size);
-  [Throws]
-  FileRequest? readAsText(unsigned long long size,
-                         optional DOMString? encoding = null);
-
-  [Throws]
-  FileRequest? write(ArrayBuffer value);
-  [Throws]
-  FileRequest? write(Blob value);
-  [Throws]
-  FileRequest? write(DOMString value);
-  [Throws]
-  FileRequest? append(ArrayBuffer value);
-  [Throws]
-  FileRequest? append(Blob value);
-  [Throws]
-  FileRequest? append(DOMString value);
-  [Throws]
-  FileRequest? truncate(optional unsigned long long size);
-  [Throws]
-  FileRequest? flush();
-  [Throws]
-  void abort();
-
-  attribute EventHandler oncomplete;
-  attribute EventHandler onabort;
-  attribute EventHandler onerror;
-};
copy from dom/webidl/FileHandle.webidl
copy to dom/webidl/MutableFile.webidl
--- a/dom/webidl/FileHandle.webidl
+++ b/dom/webidl/MutableFile.webidl
@@ -1,18 +1,18 @@
 /* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-interface FileHandle : EventTarget {
+interface MutableFile : EventTarget {
   readonly attribute DOMString name;
   readonly attribute DOMString type;
 
   [Throws]
-  LockedFile open(optional FileMode mode = "readonly");
+  FileHandle open(optional FileMode mode = "readonly");
 
   [Throws]
   DOMRequest getFile();
 
   attribute EventHandler onabort;
   attribute EventHandler onerror;
 };
--- a/dom/webidl/moz.build
+++ b/dom/webidl/moz.build
@@ -198,19 +198,19 @@ WEBIDL_FILES = [
     'HTMLTitleElement.webidl',
     'HTMLTrackElement.webidl',
     'HTMLUListElement.webidl',
     'HTMLVideoElement.webidl',
     'IDBCursor.webidl',
     'IDBDatabase.webidl',
     'IDBEnvironment.webidl',
     'IDBFactory.webidl',
-    'IDBFileHandle.webidl',
     'IDBIndex.webidl',
     'IDBKeyRange.webidl',
+    'IDBMutableFile.webidl',
     'IDBObjectStore.webidl',
     'IDBOpenDBRequest.webidl',
     'IDBRequest.webidl',
     'IDBTransaction.webidl',
     'IDBVersionChangeEvent.webidl',
     'ImageData.webidl',
     'ImageDocument.webidl',
     'InputEvent.webidl',
@@ -222,17 +222,16 @@ WEBIDL_FILES = [
     'InterAppConnectionRequest.webidl',
     'InterAppMessagePort.webidl',
     'KeyboardEvent.webidl',
     'KeyEvent.webidl',
     'LegacyQueryInterface.webidl',
     'LinkStyle.webidl',
     'LocalMediaStream.webidl',
     'Location.webidl',
-    'LockedFile.webidl',
     'MediaElementAudioSourceNode.webidl',
     'MediaError.webidl',
     'MediaKeyError.webidl',
     'MediaKeyMessageEvent.webidl',
     'MediaKeyNeededEvent.webidl',
     'MediaKeys.webidl',
     'MediaKeySession.webidl',
     'MediaList.webidl',
@@ -254,16 +253,17 @@ WEBIDL_FILES = [
     'MouseEvent.webidl',
     'MouseScrollEvent.webidl',
     'MozActivity.webidl',
     'MozMmsMessage.webidl',
     'MozNamedAttrMap.webidl',
     'MozPowerManager.webidl',
     'MozTimeManager.webidl',
     'MozWakeLock.webidl',
+    'MutableFile.webidl',
     'MutationEvent.webidl',
     'MutationObserver.webidl',
     'NativeOSFileInternals.webidl',
     'NetDashboard.webidl',
     'NetworkInformation.webidl',
     'NetworkOptions.webidl',
     'Node.webidl',
     'NodeFilter.webidl',
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -3056,17 +3056,17 @@ EmitDestructuringLHS(ExclusiveContext *c
             // Per its post-condition, EmitDestructuringOpsHelper has left the
             // to-be-destructured value on top of the stack.
             if (Emit1(cx, bce, JSOP_POP) < 0)
                 return false;
         }
     } else if (emitOption == PushInitialValues) {
         // The lhs is a simple name so the to-be-destructured value is
         // its initial value and there is nothing to do.
-        JS_ASSERT(pn->getOp() == JSOP_GETLOCAL);
+        JS_ASSERT(pn->getOp() == JSOP_SETLOCAL);
         JS_ASSERT(pn->pn_dflags & PND_BOUND);
     } else {
         switch (pn->getKind()) {
           case PNK_NAME:
             if (!BindNameToSlot(cx, bce, pn))
                 return false;
 
             // Allow 'const [x,y] = o', make 'const x,y; [x,y] = o' a nop.
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -145,17 +145,17 @@ ParseContext<FullParseHandler>::define(T
     pn->pn_dflags &= ~PND_PLACEHOLDER;
     if (kind == Definition::CONST)
         pn->pn_dflags |= PND_CONST;
 
     Definition *dn = (Definition *)pn;
     switch (kind) {
       case Definition::ARG:
         JS_ASSERT(sc->isFunctionBox());
-        dn->setOp(JSOP_GETARG);
+        dn->setOp((js_CodeSpec[dn->getOp()].format & JOF_SET) ? JSOP_SETARG : JSOP_GETARG);
         dn->pn_dflags |= PND_BOUND;
         if (!dn->pn_cookie.set(ts, staticLevel, args_.length()))
             return false;
         if (!args_.append(dn))
             return false;
         if (args_.length() >= ARGNO_LIMIT) {
             ts.reportError(JSMSG_TOO_MANY_FUN_ARGS);
             return false;
@@ -164,33 +164,33 @@ ParseContext<FullParseHandler>::define(T
             break;
         if (!decls_.addUnique(name, dn))
             return false;
         break;
 
       case Definition::CONST:
       case Definition::VAR:
         if (sc->isFunctionBox()) {
-            dn->setOp(JSOP_GETLOCAL);
+            dn->setOp((js_CodeSpec[dn->getOp()].format & JOF_SET) ? JSOP_SETLOCAL : JSOP_GETLOCAL);
             dn->pn_dflags |= PND_BOUND;
             if (!dn->pn_cookie.set(ts, staticLevel, vars_.length()))
                 return false;
             if (!vars_.append(dn))
                 return false;
             if (vars_.length() >= LOCALNO_LIMIT) {
                 ts.reportError(JSMSG_TOO_MANY_LOCALS);
                 return false;
             }
         }
         if (!decls_.addUnique(name, dn))
             return false;
         break;
 
       case Definition::LET:
-        dn->setOp(JSOP_GETLOCAL);
+        dn->setOp((js_CodeSpec[dn->getOp()].format & JOF_SET) ? JSOP_SETLOCAL : JSOP_GETLOCAL);
         dn->pn_dflags |= (PND_LET | PND_BOUND);
         JS_ASSERT(dn->pn_cookie.level() == staticLevel); /* see bindLet */
         if (!decls_.addShadow(name, dn))
             return false;
         break;
 
       default:
         MOZ_ASSUME_UNREACHABLE("unexpected kind");
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/parallel/bug1022948.js
@@ -0,0 +1,12 @@
+if (!getBuildConfiguration().parallelJS)
+  quit(0);
+
+var a = Array.buildPar(999, function() {
+    return (Math.asin(Math.tanh(1)))
+});
+var count = 0;
+for (let e of a) {
+  count++;
+  print(count);
+  assertEq(e, a[0])
+}
--- a/js/src/jsnum.cpp
+++ b/js/src/jsnum.cpp
@@ -1100,37 +1100,33 @@ static JSConstDoubleSpec number_constant
     {9007199254740991,          "MAX_SAFE_INTEGER",  0,{0,0,0}},
     /* ES6 (April 2014 draft) 20.1.2.10 */
     {-9007199254740991,         "MIN_SAFE_INTEGER",  0,{0,0,0}},
     /* ES6 (May 2013 draft) 15.7.3.7 */
     {2.2204460492503130808472633361816e-16, "EPSILON", 0,{0,0,0}},
     {0,0,0,{0,0,0}}
 };
 
-#if (defined __GNUC__ && defined __i386__) || \
-    (defined __SUNPRO_CC && defined __i386)
-
 /*
  * Set the exception mask to mask all exceptions and set the FPU precision
  * to 53 bit mantissa (64 bit doubles).
  */
-static inline void FIX_FPU() {
+void
+js::FIX_FPU()
+{
+#if (defined __GNUC__ && defined __i386__) || \
+    (defined __SUNPRO_CC && defined __i386)
     short control;
     asm("fstcw %0" : "=m" (control) : );
     control &= ~0x300; // Lower bits 8 and 9 (precision control).
     control |= 0x2f3;  // Raise bits 0-5 (exception masks) and 9 (64-bit precision).
     asm("fldcw %0" : : "m" (control) );
+#endif
 }
 
-#else
-
-#define FIX_FPU() ((void)0)
-
-#endif
-
 bool
 js::InitRuntimeNumberState(JSRuntime *rt)
 {
     FIX_FPU();
 
     /*
      * Our NaN must be one particular canonical value, because we rely on NaN
      * encoding for our value representation.  See Value.h.
--- a/js/src/jsnum.h
+++ b/js/src/jsnum.h
@@ -327,11 +327,13 @@ NonObjectToUint32(ThreadSafeContext *cx,
 {
     if (v.isInt32()) {
         *out = uint32_t(v.toInt32());
         return true;
     }
     return NonObjectToUint32Slow(cx, v, out);
 }
 
+void FIX_FPU();
+
 } /* namespace js */
 
 #endif /* jsnum_h */
--- a/js/src/vm/ThreadPool.cpp
+++ b/js/src/vm/ThreadPool.cpp
@@ -4,16 +4,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "vm/ThreadPool.h"
 
 #include "mozilla/Atomics.h"
 
 #include "jslock.h"
+#include "jsnum.h" // for FIX_FPU
 
 #include "js/Utility.h"
 #include "vm/ForkJoin.h"
 #include "vm/Monitor.h"
 #include "vm/Runtime.h"
 
 #ifdef JSGC_FJGENERATIONAL
 #include "prmjtime.h"
@@ -169,16 +170,20 @@ ThreadPoolWorker::HelperThreadMain(void 
 
 #ifdef MOZ_NUWA_PROCESS
     if (IsNuwaProcess()) {
         JS_ASSERT(NuwaMarkCurrentThread != nullptr);
         NuwaMarkCurrentThread(nullptr, nullptr);
     }
 #endif
 
+    // Set the FPU control word to be the same as the main thread's, else we
+    // might get inconsistent results from math functions.
+    FIX_FPU();
+
     worker->helperLoop();
 }
 
 void
 ThreadPoolWorker::helperLoop()
 {
     MOZ_ASSERT(!isMainThread());
 
new file mode 100644
index 0000000000000000000000000000000000000000..3ba5363c77a7c1e0b597daa6bdd58ae4c0f9477e
GIT binary patch
literal 146964
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
--- a/layout/style/nsAnimationManager.cpp
+++ b/layout/style/nsAnimationManager.cpp
@@ -64,16 +64,17 @@ ElementAnimations::EnsureStyleRuleFor(Ti
   // of animation behaviour (the styles of the animation disappear, or the fill
   // mode behaviour). This loop checks for any finishing animations and forces
   // the style recalculation if we find any.
   if (aIsThrottled) {
     for (uint32_t animIdx = mAnimations.Length(); animIdx-- != 0; ) {
       ElementAnimation* anim = mAnimations[animIdx];
 
       if (anim->mProperties.IsEmpty()) {
+        // Empty @keyframes rule.
         continue;
       }
 
       // The ElapsedDurationAt() call here handles pausing.  But:
       // FIXME: avoid recalculating every time when paused.
       TimeDuration elapsedDuration = anim->ElapsedDurationAt(aRefreshTime);
       ComputedTiming computedTiming =
         ElementAnimation::GetComputedTimingAt(elapsedDuration, anim->mTiming);
@@ -107,37 +108,33 @@ ElementAnimations::EnsureStyleRuleFor(Ti
     // FIXME(spec): assume that properties in higher animations override
     // those in lower ones.
     // Therefore, we iterate from last animation to first.
     nsCSSPropertySet properties;
 
     for (uint32_t animIdx = mAnimations.Length(); animIdx-- != 0; ) {
       ElementAnimation* anim = mAnimations[animIdx];
 
-      if (anim->mProperties.IsEmpty()) {
-        // Empty keyframes rule.
-        continue;
-      }
-
       // The ElapsedDurationAt() call here handles pausing.  But:
       // FIXME: avoid recalculating every time when paused.
       TimeDuration elapsedDuration = anim->ElapsedDurationAt(aRefreshTime);
       ComputedTiming computedTiming =
         ElementAnimation::GetComputedTimingAt(elapsedDuration, anim->mTiming);
 
       if ((computedTiming.mPhase == ComputedTiming::AnimationPhase_Before ||
            computedTiming.mPhase == ComputedTiming::AnimationPhase_Active) &&
           !anim->IsPaused()) {
         mNeedsRefreshes = true;
       }
 
       // If the time fraction is null, we don't have fill data for the current
       // time so we shouldn't animate.
-      if (computedTiming.mTimeFraction == ComputedTiming::kNullTimeFraction)
+      if (computedTiming.mTimeFraction == ComputedTiming::kNullTimeFraction) {
         continue;
+      }
 
       NS_ABORT_IF_FALSE(0.0 <= computedTiming.mTimeFraction &&
                         computedTiming.mTimeFraction <= 1.0,
                         "timing fraction should be in [0-1]");
 
       for (uint32_t propIdx = 0, propEnd = anim->mProperties.Length();
            propIdx != propEnd; ++propIdx)
       {
@@ -211,22 +208,16 @@ ElementAnimations::EnsureStyleRuleFor(Ti
 
 void
 ElementAnimations::GetEventsAt(TimeStamp aRefreshTime,
                                EventArray& aEventsToDispatch)
 {
   for (uint32_t animIdx = mAnimations.Length(); animIdx-- != 0; ) {
     ElementAnimation* anim = mAnimations[animIdx];
 
-    // We should *not* skip animations with no keyframes (bug 1004377).
-    if (anim->mProperties.IsEmpty()) {
-      // Empty keyframes rule.
-      continue;
-    }
-
     TimeDuration elapsedDuration = anim->ElapsedDurationAt(aRefreshTime);
     ComputedTiming computedTiming =
       ElementAnimation::GetComputedTimingAt(elapsedDuration, anim->mTiming);
 
     switch (computedTiming.mPhase) {
       case ComputedTiming::AnimationPhase_Before:
         // Do nothing
         break;
--- a/layout/style/test/animation_utils.js
+++ b/layout/style/test/animation_utils.js
@@ -56,16 +56,29 @@ var gTF = {
   "step_end": step_end(1),
 };
 
 function is_approx(float1, float2, error, desc) {
   ok(Math.abs(float1 - float2) < error,
      desc + ": " + float1 + " and " + float2 + " should be within " + error);
 }
 
+function findKeyframesRule(name) {
+  for (var i = 0; i < document.styleSheets.length; i++) {
+    var match = [].find.call(document.styleSheets[i].cssRules, function(rule) {
+      return rule.type == CSSRule.KEYFRAMES_RULE &&
+             rule.name == name;
+    });
+    if (match) {
+      return match;
+    }
+  }
+  return undefined;
+}
+
 // Checks if off-main thread animation (OMTA) is available, and if it is, runs
 // the provided callback function. If OMTA is not available or is not
 // functioning correctly, the second callback, aOnSkip, is run instead.
 //
 // This function also does an internal test to verify that OMTA is working at
 // all so that if OMTA is not functioning correctly when it is expected to
 // function only a single failure is produced.
 //
@@ -175,8 +188,384 @@ function runOMTATest(aTestFunction, aOnS
         var firstScript = document.scripts[0];
         firstScript.parentNode.insertBefore(script, firstScript);
       } else {
         resolve();
       }
     });
   }
 }
+
+// Common architecture for setting up a series of asynchronous animation tests
+//
+// Usage example:
+//
+//    addAsyncAnimTest(function *() {
+//       .. do work ..
+//       yield functionThatReturnsAPromise();
+//       .. do work ..
+//    });
+//    runAllAsyncAnimTests().then(SimpleTest.finish());
+//
+(function() {
+  var tests = [];
+
+  window.addAsyncAnimTest = function(generator) {
+    tests.push(generator);
+  };
+
+  // Returns a promise when all tests have run
+  window.runAllAsyncAnimTests = function(aOnAbort) {
+    // runAsyncAnimTest returns a Promise that is resolved when the
+    // test is finished so we can chain them together
+    return tests.reduce(function(sequence, test) {
+        return sequence.then(function() {
+          return runAsyncAnimTest(test, aOnAbort);
+        });
+      }, Promise.resolve() /* the start of the sequence */);
+  };
+
+  // Takes a generator function that represents a test case. Each point in the
+  // test case that waits asynchronously for some result yields a Promise that
+  // is resolved when the asynchronous action has completed. By chaining these
+  // intermediate results together we run the test to completion.
+  //
+  // This method itself returns a Promise that is resolved when the generator
+  // function has completed.
+  //
+  // This arrangement is based on add_task() which is currently only available
+  // in mochitest-chrome (bug 872229). If add_task becomes available in
+  // mochitest-plain, we can remove this function and use add_task instead.
+  function runAsyncAnimTest(aTestFunc, aOnAbort) {
+    var generator;
+
+    function step(arg) {
+      var next;
+      try {
+        next = generator.next(arg);
+      } catch (e) {
+        return Promise.reject(e);
+      }
+      if (next.done) {
+        return Promise.resolve(next.value);
+      } else {
+        return Promise.resolve(next.value)
+               .then(step, function(err) { throw err; });
+      }
+    }
+
+    // Put refresh driver under test control
+    SpecialPowers.DOMWindowUtils.advanceTimeAndRefresh(0);
+
+    // Run test
+    generator = aTestFunc();
+    return step()
+    .catch(function(err) {
+      ok(false, err.message);
+      if (typeof aOnAbort == "function") {
+        aOnAbort();
+      }
+    }).then(function() {
+      // Restore clock
+      SpecialPowers.DOMWindowUtils.restoreNormalRefresh();
+    });
+  }
+})();
+
+//----------------------------------------------------------------------
+//
+// Helper functions for testing animated values on the compositor
+//
+//----------------------------------------------------------------------
+
+const RunningOn = {
+  MainThread: 0,
+  Compositor: 1,
+  Either: 2,
+  TodoMainThread: 3
+};
+
+const ExpectComparisonTo = {
+  Pass: 1,
+  Fail: 2
+};
+
+(function() {
+  window.omta_todo_is = function(elem, property, expected, runningOn, desc) {
+    return omta_is_approx(elem, property, expected, 0, runningOn, desc,
+                          ExpectComparisonTo.Fail);
+  };
+
+  window.omta_is = function(elem, property, expected, runningOn, desc) {
+    return omta_is_approx(elem, property, expected, 0, runningOn, desc);
+  };
+
+  // Many callers of this method will pass 'undefined' for
+  // expectedComparisonResult.
+  window.omta_is_approx = function(elem, property, expected, tolerance,
+                                   runningOn, desc, expectedComparisonResult) {
+    // Check input
+    const omtaProperties = [ "transform", "opacity" ];
+    if (omtaProperties.indexOf(property) === -1) {
+      ok(false, property + " is not an OMTA property");
+      return;
+    }
+    var isTransform = property == "transform";
+    var normalize = isTransform ? convertTo3dMatrix : parseFloat;
+    var compare = isTransform ?
+                  matricesRoughlyEqual :
+                  function(a, b, error) { return Math.abs(a - b) <= error; };
+    var normalizedToString = isTransform ?
+                             convert3dMatrixToString :
+                             JSON.stringify;
+
+    // Get actual values
+    var compositorStr =
+      SpecialPowers.DOMWindowUtils.getOMTAStyle(elem, property);
+    var computedStr = window.getComputedStyle(elem)[property];
+
+    // Prepare expected value
+    var expectedValue = normalize(expected);
+    if (expectedValue === null) {
+      ok(false, desc + ": test author should provide a valid 'expected' value" +
+                " - got " + expected.toString());
+      return;
+    }
+
+    // Check expected value appears in the right place
+    var actualStr;
+    switch (runningOn) {
+      case RunningOn.Either:
+        runningOn = compositorStr !== "" ?
+                    RunningOn.Compositor :
+                    RunningOn.MainThread;
+        actualStr = compositorStr !== "" ? compositorStr : computedStr;
+        break;
+
+      case RunningOn.Compositor:
+        if (compositorStr === "") {
+          ok(false, desc + ": should be animating on compositor");
+          return;
+        }
+        actualStr = compositorStr;
+        break;
+
+      case RunningOn.TodoMainThread:
+        todo(compositorStr === "",
+             desc + ": should NOT be animating on compositor");
+        actualStr = compositorStr === "" ? computedStr : compositorStr;
+        break;
+
+      default:
+        if (compositorStr !== "") {
+          ok(false, desc + ": should NOT be animating on compositor");
+          return;
+        }
+        actualStr = computedStr;
+        break;
+    }
+
+    var okOrTodo = expectedComparisonResult == ExpectComparisonTo.Fail ?
+                   todo :
+                   ok;
+
+    // Compare animated value with expected
+    var actualValue = normalize(actualStr);
+    if (actualValue === null) {
+      ok(false, desc + ": should return a valid result - got " + actualStr);
+      return;
+    }
+    okOrTodo(compare(expectedValue, actualValue, tolerance),
+             desc + " - got " + actualStr + ", expected " +
+             normalizedToString(expectedValue));
+
+    // For compositor animations do an additional check that they match
+    // the value calculated on the main thread
+    if (actualStr === compositorStr) {
+      var computedValue = normalize(computedStr);
+      if (computedValue === null) {
+        ok(false, desc + ": test framework should parse computed style" +
+                  " - got " + computedStr);
+        return;
+      }
+      okOrTodo(compare(computedValue, actualValue, 0),
+               desc + ": OMTA style and computed style should be equal" +
+               " - OMTA " + actualStr + ", computed " + computedStr);
+    }
+  };
+
+  function matricesRoughlyEqual(a, b, tolerance) {
+    tolerance = tolerance || 0.00011;
+    for (var i = 0; i < 4; i++) {
+      for (var j = 0; j < 4; j++) {
+        var diff = Math.abs(a[i][j] - b[i][j]);
+        if (diff > tolerance || isNaN(diff))
+          return false;
+      }
+    }
+    return true;
+  }
+
+  // Converts something representing an transform into a 3d matrix in
+  // column-major order.
+  // The following are supported:
+  //  "matrix(...)"
+  //  "matrix3d(...)"
+  //  [ 1, 0, 0, ... ]
+  //  { a: 1, ty: 23 } etc.
+  window.convertTo3dMatrix = function(matrixLike) {
+    if (typeof(matrixLike) == "string") {
+      return convertStringTo3dMatrix(matrixLike);
+    } else if (Array.isArray(matrixLike)) {
+      return convertArrayTo3dMatrix(matrixLike);
+    } else if (typeof(matrixLike) == "object") {
+      return convertObjectTo3dMatrix(matrixLike);
+    } else {
+      return null;
+    }
+  };
+
+  // In future most of these methods should be able to be replaced
+  // with DOMMatrix
+  window.isInvertible = function(matrix) {
+    return getDeterminant(matrix) != 0;
+  };
+
+  // Converts strings of the format "matrix(...)" and "matrix3d(...)" to a 3d
+  // matrix
+  function convertStringTo3dMatrix(str) {
+    if (str == "none")
+      return convertArrayTo3dMatrix([1, 0, 0, 1, 0, 0]);
+    var result = str.match("^matrix(3d)?\\(");
+    if (result === null)
+      return null;
+
+    return convertArrayTo3dMatrix(
+        str.substring(result[0].length, str.length-1)
+           .split(",")
+           .map(function(component) {
+             return Number(component);
+           })
+      );
+  }
+
+  // Takes an array of numbers of length 6 (2d matrix) or 16 (3d matrix)
+  // representing a matrix specified in column-major order and returns a 3d
+  // matrix represented as an array of arrays
+  function convertArrayTo3dMatrix(array) {
+    if (array.length == 6) {
+      return convertObjectTo3dMatrix(
+        { a: array[0], b: array[1],
+          c: array[2], d: array[3],
+          e: array[4], f: array[5] } );
+    } else if (array.length == 16) {
+      return [
+        array.slice(0, 4),
+        array.slice(4, 8),
+        array.slice(8, 12),
+        array.slice(12, 16)
+      ];
+    } else {
+      return null;
+    }
+  }
+
+  // Takes an object of the form { a: 1.1, e: 23 } and builds up a 3d matrix
+  // with unspecified values filled in with identity values.
+  function convertObjectTo3dMatrix(obj) {
+    return [
+      [
+        obj.a || obj.sx || obj.m11 || 1,
+        obj.b || obj.m12 || 0,
+        obj.m13 || 0,
+        obj.m14 || 0
+      ], [
+        obj.c || obj.m21 || 0,
+        obj.d || obj.sy || obj.m22 || 1,
+        obj.m23 || 0,
+        obj.m24 || 0
+      ], [
+        obj.m31 || 0,
+        obj.m32 || 0,
+        obj.sz || obj.m33 || 1,
+        obj.m34 || 0
+      ], [
+        obj.e || obj.tx || obj.m41 || 0,
+        obj.f || obj.ty || obj.m42 || 0,
+        obj.tz || obj.m43 || 0,
+        obj.m44 || 1
+      ]
+    ];
+  }
+
+  function convert3dMatrixToString(matrix) {
+    if (is2d(matrix)) {
+      return "matrix(" +
+             [ matrix[0][0], matrix[0][1],
+               matrix[1][0], matrix[1][1],
+               matrix[3][0], matrix[3][1] ].join(", ") + ")";
+    } else {
+      return "matrix3d(" +
+              matrix.reduce(function(outer, inner) {
+                  return outer.concat(inner);
+              }).join(", ") + ")";
+    }
+  }
+
+  function is2d(matrix) {
+    return matrix[0][2] === 0 && matrix[0][3] === 0 &&
+           matrix[1][2] === 0 && matrix[1][3] === 0 &&
+           matrix[2][0] === 0 && matrix[2][1] === 0 &&
+           matrix[2][2] === 1 && matrix[2][3] === 0 &&
+           matrix[3][2] === 0 && matrix[3][3] === 1;
+  }
+
+  function getDeterminant(matrix) {
+    if (is2d(matrix)) {
+      return matrix[0][0] * matrix[1][1] - matrix[0][1] * matrix[1][0];
+    }
+
+    return   matrix[0][3] * matrix[1][2] * matrix[2][1] * matrix[3][0]
+           - matrix[0][2] * matrix[1][3] * matrix[2][1] * matrix[3][0]
+           - matrix[0][3] * matrix[1][1] * matrix[2][2] * matrix[3][0]
+           + matrix[0][1] * matrix[1][3] * matrix[2][2] * matrix[3][0]
+           + matrix[0][2] * matrix[1][1] * matrix[2][3] * matrix[3][0]
+           - matrix[0][1] * matrix[1][2] * matrix[2][3] * matrix[3][0]
+           - matrix[0][3] * matrix[1][2] * matrix[2][0] * matrix[3][1]
+           + matrix[0][2] * matrix[1][3] * matrix[2][0] * matrix[3][1]
+           + matrix[0][3] * matrix[1][0] * matrix[2][2] * matrix[3][1]
+           - matrix[0][0] * matrix[1][3] * matrix[2][2] * matrix[3][1]
+           - matrix[0][2] * matrix[1][0] * matrix[2][3] * matrix[3][1]
+           + matrix[0][0] * matrix[1][2] * matrix[2][3] * matrix[3][1]
+           + matrix[0][3] * matrix[1][1] * matrix[2][0] * matrix[3][2]
+           - matrix[0][1] * matrix[1][3] * matrix[2][0] * matrix[3][2]
+           - matrix[0][3] * matrix[1][0] * matrix[2][1] * matrix[3][2]
+           + matrix[0][0] * matrix[1][3] * matrix[2][1] * matrix[3][2]
+           + matrix[0][1] * matrix[1][0] * matrix[2][3] * matrix[3][2]
+           - matrix[0][0] * matrix[1][1] * matrix[2][3] * matrix[3][2]
+           - matrix[0][2] * matrix[1][1] * matrix[2][0] * matrix[3][3]
+           + matrix[0][1] * matrix[1][2] * matrix[2][0] * matrix[3][3]
+           + matrix[0][2] * matrix[1][0] * matrix[2][1] * matrix[3][3]
+           - matrix[0][0] * matrix[1][2] * matrix[2][1] * matrix[3][3]
+           - matrix[0][1] * matrix[1][0] * matrix[2][2] * matrix[3][3]
+           + matrix[0][0] * matrix[1][1] * matrix[2][2] * matrix[3][3];
+  }
+})();
+
+//----------------------------------------------------------------------
+//
+// Promise wrappers for paint_listener.js
+//
+//----------------------------------------------------------------------
+
+// Returns a Promise that resolves once all paints have completed
+function waitForPaints() {
+  return new Promise(function(resolve, reject) {
+    waitForAllPaints(resolve);
+  });
+}
+
+// As with waitForPaints but also flushes pending style changes before waiting
+function waitForPaintsFlushed() {
+  return new Promise(function(resolve, reject) {
+    waitForAllPaintsFlushed(resolve);
+  });
+}
--- a/layout/style/test/test_animations.html
+++ b/layout/style/test/test_animations.html
@@ -131,16 +131,19 @@ https://bugzilla.mozilla.org/show_bug.cg
   }
 
   @keyframes important2 {
     from { margin-top: 50px;
            margin-bottom: 100px; }
     to   { margin-top: 150px !important; /* ignored */
            margin-bottom: 50px; }
   }
+
+  @keyframes empty { }
+  @keyframes nearlyempty { to { margin-left: 100px; } }
   </style>
 </head>
 <body>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=435442">Mozilla Bug 435442</a>
 <div id="display"></div>
 <pre id="test">
 <script type="application/javascript">
 "use strict";
@@ -1771,14 +1774,91 @@ check_events([{ type: 'animationstart', 
                 pseudoElement: "" },
               { type: 'animationend', target: div,
                 animationName: 'anim2', elapsedTime: 0,
                 pseudoElement: "" }],
              "events after skipping over zero-duration, zero iteration count"
              + " animation");
 done_div();
 
+/*
+ * Bug 1004377 - Animations with empty keyframes rule
+ */
+
+new_div("margin-right: 200px; animation: empty 2s 1s both");
+listen();
+advance_clock(0);
+check_events([], "events during delay");
+advance_clock(2000); // Skip to middle of animation
+div.clientTop; // Trigger events
+check_events([{ type: 'animationstart', target: div,
+                animationName: 'empty', elapsedTime: 0,
+                pseudoElement: "" }],
+             "middle of animation with empty keyframes rule");
+advance_clock(1000); // Skip to end of animation
+div.clientTop; // Trigger events
+check_events([{ type: 'animationend', target: div,
+                animationName: 'empty', elapsedTime: 2,
+                pseudoElement: "" }],
+             "end of animation with empty keyframes rule");
+done_div();
+
+// Test with a zero-duration animation and empty @keyframes rule
+new_div("margin-right: 200px; animation: empty 0s 1s both");
+listen();
+advance_clock(0);
+advance_clock(1000);
+div.clientTop; // Trigger events
+check_events([{ type: 'animationstart', target: div,
+                animationName: 'empty', elapsedTime: 0,
+                pseudoElement: "" },
+              { type: 'animationend', target: div,
+                animationName: 'empty', elapsedTime: 0,
+                pseudoElement: "" }],
+             "end of zero-duration animation with empty keyframes rule");
+done_div();
+
+// Test with a keyframes rule that becomes empty
+new_div("animation: nearlyempty 1s both linear");
+advance_clock(0);
+advance_clock(500);
+is(cs.getPropertyValue("margin-left"), "50px",
+   "margin-left for animation that is about to be emptied");
+listen();
+findKeyframesRule("nearlyempty").deleteRule("to");
+is(cs.getPropertyValue("margin-left"), "0px",
+   "margin-left for animation with (now) empty keyframes rule");
+check_events([], "events after emptying keyframes rule");
+advance_clock(500);
+div.clientTop; // Trigger events
+check_events([{ type: 'animationend', target: div,
+                animationName: 'nearlyempty', elapsedTime: 1,
+                pseudoElement: "" }],
+             "events at end of animation with newly " +
+             "empty keyframes rule");
+done_div();
+
+// Test when we update to point to an empty animation
+new_div("animation: always_fifty 1s both linear");
+advance_clock(0);
+advance_clock(500);
+is(cs.getPropertyValue("margin-left"), "50px",
+   "margin-left for animation that will soon point to an empty keyframes rule");
+listen();
+div.style.animationName = "empty";
+is(cs.getPropertyValue("margin-left"), "0px",
+   "margin-left for animation now points to empty keyframes rule");
+advance_clock(500);
+div.clientTop; // Trigger events
+check_events([{ type: 'animationstart', target: div,
+                animationName: 'empty', elapsedTime: 0,
+                pseudoElement: "" }],
+             "events at start of animation updated to use " +
+             "empty keyframes rule");
+
+done_div();
+
 SpecialPowers.DOMWindowUtils.restoreNormalRefresh();
 
 </script>
 </pre>
 </body>
 </html>
--- a/layout/style/test/test_animations_omta.html
+++ b/layout/style/test/test_animations_omta.html
@@ -133,16 +133,23 @@ https://bugzilla.mozilla.org/show_bug.cg
     }
     @keyframes important2 {
       from { opacity: 0.5;
              transform: translate(100px); }
       to   { opacity: 0.2 !important; /* ignored */
              transform: translate(50px); }
     }
 
+    @keyframes empty { }
+    @keyframes nearlyempty {
+      to {
+        transform: translate(100px);
+      }
+    }
+
     .target {
       /* The animation target needs geometry in order to qualify for OMTA */
       width: 100px;
       height: 100px;
       background-color: white;
     }
   </style>
 </head>
@@ -153,97 +160,57 @@ https://bugzilla.mozilla.org/show_bug.cg
 <div id="display"></div>
 <pre id="test">
 <script type="application/javascript">
 "use strict";
 
 /** Test for css3-animations running on the compositor thread (Bug 964646) **/
  
 // Global state
-var gAsyncTests     = [],
-    gDisplay        = document.getElementById("display"),
+var gDisplay        = document.getElementById("display"),
     gDiv            = null,
     gEventsReceived = [];
 
+// Shortcut omta_is and friends by filling in the initial 'elem' argument
+// with gDiv.
+[ 'omta_is', 'omta_todo_is', 'omta_is_approx' ].forEach(function(fn) {
+  var origFn = window[fn];
+  window[fn] = function() {
+    var args = Array.slice(arguments);
+    if (!(args[0] instanceof Element)) {
+      args.unshift(gDiv);
+    }
+    return origFn.apply(window, args);
+  };
+});
+
 SimpleTest.waitForExplicitFinish();
 runOMTATest(function() {
-  // The async test runner returns a Promise that is resolved when the
-  // test is finished so we can chain them together
-  gAsyncTests.reduce(function(sequence, test) {
-    return sequence.then(function() { return runAsyncTest(test); });
-  }, Promise.resolve() /* the start of the sequence */)
-  // Final step in the sequence
-  .then(function() {
+  var onAbort = function() {
+    if (gDiv) {
+      done_div();
+    }
+  };
+  runAllAsyncAnimTests(onAbort).then(function() {
     SimpleTest.finish();
   });
 }, SimpleTest.finish);
 
-// Takes a generator function that represents a test case. Each point in the
-// test case that waits asynchronously for some result yields a Promise that is
-// resolved when the asychronous action has completed. By chaining these
-// intermediate results together we run the test to completion.
-//
-// This method itself returns a Promise that is resolved when the generator
-// function has completed.
-//
-// This arrangement is based on add_task() which is currently only available
-// in mochitest-chrome (bug 872229). Once add_task is available in
-// mochitest-plain we can remove this function and use add_task instead.
-function runAsyncTest(test) {
-  var generator;
-
-  function step(arg) {
-    var next;
-    try {
-      next = generator.next(arg);
-    } catch (e) {
-      return Promise.reject(e);
-    }
-    if (next.done) {
-      return Promise.resolve(next.value);
-    } else {
-      return Promise.resolve(next.value)
-             .then(step, function(err) { throw err; });
-    }
-  }
-
-  // Put refresh driver under test control
-  advance_clock(0);
-
-  // Run test
-  generator = test();
-  return step()
-  .catch(function(err) {
-    ok(false, err.message);
-    // Clear up the test div in case we aborted the test before doing clean-up
-    if (gDiv) {
-      done_div();
-    }
-  }).then(function() {
-    // Restore clock
-    SpecialPowers.DOMWindowUtils.restoreNormalRefresh();
-  });
-}
-
-function addAsyncTest(generator) {
-  gAsyncTests.push(generator);
-}
-
 //----------------------------------------------------------------------
 //
 // Test cases
 //
 //----------------------------------------------------------------------
 
 // This test is not in test_animations.html but is here to test that
 // transform animations are actually run on the compositor thread as expected.
-addAsyncTest(function *() {
+addAsyncAnimTest(function *() {
   new_div("animation: transform-anim linear 300s");
 
-  yield waitForPaints();
+  yield waitForPaintsFlushed();
 
   advance_clock(200000);
   omta_is("transform", { tx: 100 * 2 / 3 }, RunningOn.Compositor,
           "OMTA animation is animating as expected");
   done_div();
 });
 
 function *testFillMode(fillMode, fillsBackwards, fillsForwards)
@@ -257,17 +224,17 @@ function *testFillMode(fillMode, fillsBa
     desc = "default fill mode: ";
   }
   new_div(style);
   listen();
 
   // Currently backwards fill is not performed on the compositor thread but we
   // should wait for paints so we can test that transform values are *not* being
   // set on the compositor thread.
-  yield waitForPaints();
+  yield waitForPaintsFlushed();
 
   if (fillsBackwards)
     omta_is("transform", { tx: 0 }, RunningOn.MainThread,
             desc + "does affect value during delay (0s)");
   else
     omta_is("transform", { tx: 30 }, RunningOn.MainThread,
             desc + "doesn't affect value during delay (0s)");
 
@@ -338,34 +305,34 @@ function *testFillMode(fillMode, fillsBa
             desc + "affects value after animation");
   else
     omta_is("transform", { tx: 30 }, RunningOn.MainThread,
             desc + "does not affect value after animation");
 
   done_div();
 }
 
-addAsyncTest(function() { return testFillMode("", false, false); });
-addAsyncTest(function() { return testFillMode("none", false, false); });
-addAsyncTest(function() { return testFillMode("forwards", false, true); });
-addAsyncTest(function() { return testFillMode("backwards", true, false); });
-addAsyncTest(function() { return testFillMode("both", true, true); });
+addAsyncAnimTest(function() { return testFillMode("", false, false); });
+addAsyncAnimTest(function() { return testFillMode("none", false, false); });
+addAsyncAnimTest(function() { return testFillMode("forwards", false, true); });
+addAsyncAnimTest(function() { return testFillMode("backwards", true, false); });
+addAsyncAnimTest(function() { return testFillMode("both", true, true); });
 
 // Test that animations continue running when the animation name
 // list is changed.
 //
 // test_animations.html combines all these tests into one block but this is
 // difficult for OMTA because currently there are only two properties to which
 // we apply OMTA. Instead we break the test down into a few independent pieces
 // in order to exercise the same functionality.
 
 // Append to list
-addAsyncTest(function *() {
+addAsyncAnimTest(function *() {
   new_div("animation: anim1 linear 10s");
-  yield waitForPaints();
+  yield waitForPaintsFlushed();
     omta_is("transform", { tx: 0 }, RunningOn.Either,
             "just anim1, translate at start");
   advance_clock(1000);
     omta_is("transform", { tx: 16 }, RunningOn.Compositor,
             "just anim1, translate at 1s");
   // append anim2
   gDiv.style.animation = "anim1 linear 10s, anim2 linear 10s";
   yield waitForPaintsFlushed();
@@ -377,19 +344,19 @@ addAsyncTest(function *() {
     omta_is("transform", { tx: 32 }, RunningOn.Compositor,
             "anim1 + anim2, translate at 2s");
     omta_is("opacity", 0.1, RunningOn.Compositor,
             "anim1 + anim2, opacity at 2s");
   done_div();
 });
 
 // Prepend to list; delete from list
-addAsyncTest(function *() {
+addAsyncAnimTest(function *() {
   new_div("animation: anim1 linear 10s");
-  yield waitForPaints();
+  yield waitForPaintsFlushed();
     omta_is("transform", { tx: 0 }, RunningOn.Either,
             "just anim1, translate at start");
   advance_clock(1000);
     omta_is("transform", { tx: 16 }, RunningOn.Compositor,
                      "just anim1, translate at 1s");
   // prepend anim2
   gDiv.style.animation = "anim2 linear 10s, anim1 linear 10s";
   yield waitForPaintsFlushed();
@@ -411,19 +378,19 @@ addAsyncTest(function *() {
   advance_clock(1000);
     omta_is("transform", { tx: 48 }, RunningOn.Compositor,
             "just anim1, translate at 3s");
     omta_is("opacity", 1, RunningOn.MainThread, "just anim1, opacity at 3s");
   done_div();
 });
 
 // Swap elements
-addAsyncTest(function *() {
+addAsyncAnimTest(function *() {
   new_div("animation: anim1 linear 10s, anim2 linear 10s");
-  yield waitForPaints();
+  yield waitForPaintsFlushed();
     omta_is("transform", { tx: 0 }, RunningOn.Either,
             "anim1 + anim2, translate at start");
     omta_is("opacity", 0, RunningOn.Compositor,
             "anim1 + anim2, opacity at start");
   advance_clock(1000);
     omta_is("transform", { tx: 16 }, RunningOn.Compositor,
             "anim1 + anim2, translate at 1s");
     omta_is("opacity", 0.1, RunningOn.Compositor,
@@ -519,167 +486,166 @@ addAsyncTest(function *() {
  * css3-animations:  3. Keyframes
  * http://dev.w3.org/csswg/css3-animations/#keyframes
  */
 
 // Test the rules on keyframes that lack a 0% or 100% rule:
 // (simultaneously, test that reverse animations have their keyframes
 // run backwards)
 
-addAsyncTest(function *() {
+addAsyncAnimTest(function *() {
   // 100px at 0%, 50px at 50%, 150px at 100%
   new_div("transform: translate(100px); " +
           "animation: kf1 ease 1s alternate infinite");
-  advance_clock(0);
-  yield waitForPaints();
+  yield waitForPaintsFlushed();
   omta_is("transform", { tx: 100 }, RunningOn.Compositor, "no-0% at 0.0s");
   advance_clock(100);
-  omta_is_approx("transform", { tx: 100 - 50 * gTF.ease(0.2) },
-                 RunningOn.Compositor, 0.01, "no-0% at 0.1s");
+  omta_is_approx("transform", { tx: 100 - 50 * gTF.ease(0.2) }, 0.01,
+                 RunningOn.Compositor, "no-0% at 0.1s");
   advance_clock(200);
-  omta_is_approx("transform", { tx: 100 - 50 * gTF.ease(0.6) },
-                 RunningOn.Compositor, 0.01, "no-0% at 0.3s");
+  omta_is_approx("transform", { tx: 100 - 50 * gTF.ease(0.6) }, 0.01,
+                 RunningOn.Compositor, "no-0% at 0.3s");
   advance_clock(200);
   omta_is("transform", { tx: 50 }, RunningOn.Compositor, "no-0% at 0.5s");
   advance_clock(200);
-  omta_is_approx("transform", { tx: 50 + 100 * gTF.ease(0.4) },
-                 RunningOn.Compositor, 0.01, "no-0% at 0.7s");
+  omta_is_approx("transform", { tx: 50 + 100 * gTF.ease(0.4) }, 0.01,
+                 RunningOn.Compositor, "no-0% at 0.7s");
   advance_clock(200);
-  omta_is_approx("transform", { tx: 50 + 100 * gTF.ease(0.8) },
-                 RunningOn.Compositor, 0.01, "no-0% at 0.9s");
+  omta_is_approx("transform", { tx: 50 + 100 * gTF.ease(0.8) }, 0.01,
+                 RunningOn.Compositor, "no-0% at 0.9s");
   advance_clock(100);
   omta_is("transform", { tx: 150 }, RunningOn.Compositor, "no-0% at 1.0s");
   advance_clock(100);
-  omta_is_approx("transform", { tx: 50 + 100 * gTF.ease(0.8) },
-                 RunningOn.Compositor, 0.01, "no-0% at 1.1s");
+  omta_is_approx("transform", { tx: 50 + 100 * gTF.ease(0.8) }, 0.01,
+                 RunningOn.Compositor, "no-0% at 1.1s");
   advance_clock(300);
-  omta_is_approx("transform", { tx: 50 + 100 * gTF.ease(0.2) },
-                 RunningOn.Compositor, 0.01, "no-0% at 1.4s");
+  omta_is_approx("transform", { tx: 50 + 100 * gTF.ease(0.2) }, 0.01,
+                 RunningOn.Compositor, "no-0% at 1.4s");
   advance_clock(300);
-  omta_is_approx("transform", { tx: 100 - 50 * gTF.ease(0.6) },
-                 RunningOn.Compositor, 0.01, "no-0% at 1.7s");
+  omta_is_approx("transform", { tx: 100 - 50 * gTF.ease(0.6) }, 0.01,
+                 RunningOn.Compositor, "no-0% at 1.7s");
   advance_clock(200);
-  omta_is_approx("transform", { tx: 100 - 50 * gTF.ease(0.2) },
-                 RunningOn.Compositor, 0.01, "no-0% at 1.9s");
+  omta_is_approx("transform", { tx: 100 - 50 * gTF.ease(0.2) }, 0.01,
+                 RunningOn.Compositor, "no-0% at 1.9s");