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 idunknown
push userunknown
push dateunknown
reviewersmerge
milestone33.0a1
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 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..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");
   advance_clock(100);
   omta_is("transform", { tx: 100 }, RunningOn.Compositor, "no-0% at 2.0s");
   done_div();
 
   // 150px at 0%, 50px at 50%, 100px at 100%
   new_div("transform: translate(100px); " +
           "animation: kf2 ease-in 1s alternate infinite");
-  yield waitForPaints();
+  yield waitForPaintsFlushed();
   omta_is("transform", { tx: 150 }, RunningOn.Compositor, "no-100% at 0.0s");
   advance_clock(100);
-  omta_is_approx("transform", { tx: 150 - 100 * gTF.ease_in(0.2) },
-                 RunningOn.Compositor, 0.01, "no-100% at 0.1s");
+  omta_is_approx("transform", { tx: 150 - 100 * gTF.ease_in(0.2) }, 0.01,
+                 RunningOn.Compositor, "no-100% at 0.1s");
   advance_clock(200);
-  omta_is_approx("transform", { tx: 150 - 100 * gTF.ease_in(0.6) },
-                 RunningOn.Compositor, 0.01, "no-100% at 0.3s");
+  omta_is_approx("transform", { tx: 150 - 100 * gTF.ease_in(0.6) }, 0.01,
+                 RunningOn.Compositor, "no-100% at 0.3s");
   advance_clock(200);
   omta_is("transform", { tx: 50 }, RunningOn.Compositor, "no-100% at 0.5s");
   advance_clock(200);
-  omta_is_approx("transform", { tx: 50 + 50 * gTF.ease_in(0.4) },
-                 RunningOn.Compositor, 0.01, "no-100% at 0.7s");
+  omta_is_approx("transform", { tx: 50 + 50 * gTF.ease_in(0.4) }, 0.01,
+                 RunningOn.Compositor, "no-100% at 0.7s");
   advance_clock(200);
-  omta_is_approx("transform", { tx: 50 + 50 * gTF.ease_in(0.8) },
-                 RunningOn.Compositor, 0.01, "no-100% at 0.9s");
+  omta_is_approx("transform", { tx: 50 + 50 * gTF.ease_in(0.8) }, 0.01,
+                 RunningOn.Compositor, "no-100% at 0.9s");
   advance_clock(100);
   omta_is("transform", { tx: 100 }, RunningOn.Compositor, "no-100% at 1.0s");
   advance_clock(100);
-  omta_is_approx("transform", { tx: 50 + 50 * gTF.ease_in(0.8) },
-                 RunningOn.Compositor, 0.01, "no-100% at 1.1s");
+  omta_is_approx("transform", { tx: 50 + 50 * gTF.ease_in(0.8) }, 0.01,
+                 RunningOn.Compositor, "no-100% at 1.1s");
   advance_clock(300);