Merge inbound to mozilla-central. a=merge
authorshindli <shindli@mozilla.com>
Tue, 07 May 2019 12:46:42 +0300
changeset 531661 6fd64908d113d7cdf085be1e2858e10dd69643e3
parent 531649 7aee5a30dd15cf0e203705808de4fc84cd56393d (current diff)
parent 531660 c64f610ccf7669b35c1a02cd03b35dd5949c8e53 (diff)
child 531686 b840fb920d287edc9b2fb6b35e20b5bb18d5236f
child 531693 79146a949e96aa65b2387c8abc840637b3b9f423
push id11265
push userffxbld-merge
push dateMon, 13 May 2019 10:53:39 +0000
treeherdermozilla-beta@77e0fe8dbdd3 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone68.0a1
first release with
nightly linux32
6fd64908d113 / 68.0a1 / 20190507094727 / files
nightly linux64
6fd64908d113 / 68.0a1 / 20190507094727 / files
nightly mac
6fd64908d113 / 68.0a1 / 20190507094727 / files
nightly win32
6fd64908d113 / 68.0a1 / 20190507094727 / files
nightly win64
6fd64908d113 / 68.0a1 / 20190507094727 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge inbound to mozilla-central. a=merge
python/mozbuild/mozbuild/artifacts.py
--- a/dom/cache/Context.cpp
+++ b/dom/cache/Context.cpp
@@ -384,18 +384,17 @@ Context::QuotaInitRunnable::Run() {
         resolver->Resolve(NS_ERROR_ABORT);
         break;
       }
 
       QuotaManager* qm = QuotaManager::Get();
       MOZ_DIAGNOSTIC_ASSERT(qm);
       nsresult rv = qm->EnsureOriginIsInitialized(
           PERSISTENCE_TYPE_DEFAULT, mQuotaInfo.mSuffix, mQuotaInfo.mGroup,
-          mQuotaInfo.mOrigin,
-          /* aCreateIfNotExists */ true, getter_AddRefs(mQuotaInfo.mDir));
+          mQuotaInfo.mOrigin, getter_AddRefs(mQuotaInfo.mDir));
       if (NS_FAILED(rv)) {
         resolver->Resolve(rv);
         break;
       }
 
       mState = STATE_RUN_ON_TARGET;
 
       MOZ_ALWAYS_SUCCEEDS(mTarget->Dispatch(this, nsIThread::DISPATCH_NORMAL));
--- a/dom/indexedDB/ActorsParent.cpp
+++ b/dom/indexedDB/ActorsParent.cpp
@@ -17154,18 +17154,17 @@ nsresult Maintenance::DirectoryWork() {
             persistenceType, group, origin, std::move(databasePaths)));
 
         nsCOMPtr<nsIFile> directory;
 
         // Idle maintenance may occur before origin is initailized.
         // Ensure origin is initialized first. It will initialize all origins
         // for temporary storage including IDB origins.
         rv = quotaManager->EnsureOriginIsInitialized(
-            persistenceType, suffix, group, origin,
-            /* aCreateIfNotExists */ true, getter_AddRefs(directory));
+            persistenceType, suffix, group, origin, getter_AddRefs(directory));
 
         if (NS_WARN_IF(NS_FAILED(rv))) {
           return rv;
         }
       }
     }
   }
 
@@ -19864,18 +19863,17 @@ nsresult OpenDatabaseOp::DoDatabaseWork(
   PersistenceType persistenceType = mCommonParams.metadata().persistenceType();
 
   QuotaManager* quotaManager = QuotaManager::Get();
   MOZ_ASSERT(quotaManager);
 
   nsCOMPtr<nsIFile> dbDirectory;
 
   nsresult rv = quotaManager->EnsureOriginIsInitialized(
-      persistenceType, mSuffix, mGroup, mOrigin,
-      /* aCreateIfNotExists */ true, getter_AddRefs(dbDirectory));
+      persistenceType, mSuffix, mGroup, mOrigin, getter_AddRefs(dbDirectory));
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   rv = dbDirectory->Append(NS_LITERAL_STRING(IDB_DIRECTORY_NAME));
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
--- a/dom/localstorage/ActorsParent.cpp
+++ b/dom/localstorage/ActorsParent.cpp
@@ -295,17 +295,17 @@ int32_t MakeSchemaVersion(uint32_t aMajo
 }
 
 nsCString GetArchivedOriginHashKey(const nsACString& aOriginSuffix,
                                    const nsACString& aOriginNoSuffix) {
   return aOriginSuffix + NS_LITERAL_CSTRING(":") + aOriginNoSuffix;
 }
 
 nsresult CreateTables(mozIStorageConnection* aConnection) {
-  AssertIsOnIOThread();
+  MOZ_ASSERT(IsOnIOThread() || IsOnConnectionThread());
   MOZ_ASSERT(aConnection);
 
   // Table `database`
   nsresult rv = aConnection->ExecuteSimpleSQL(
       NS_LITERAL_CSTRING("CREATE TABLE database"
                          "( origin TEXT NOT NULL"
                          ", usage INTEGER NOT NULL DEFAULT 0"
                          ", last_vacuum_time INTEGER NOT NULL DEFAULT 0"
@@ -423,17 +423,17 @@ nsresult SetDefaultPragmas(mozIStorageCo
 
   return NS_OK;
 }
 
 nsresult CreateStorageConnection(nsIFile* aDBFile, nsIFile* aUsageFile,
                                  const nsACString& aOrigin,
                                  mozIStorageConnection** aConnection,
                                  bool* aRemovedUsageFile) {
-  AssertIsOnIOThread();
+  MOZ_ASSERT(IsOnIOThread() || IsOnConnectionThread());
   MOZ_ASSERT(aDBFile);
   MOZ_ASSERT(aUsageFile);
   MOZ_ASSERT(aConnection);
   MOZ_ASSERT(aRemovedUsageFile);
 
   // aRemovedUsageFile has to be initialized even when this method fails.
   *aRemovedUsageFile = false;
 
@@ -837,17 +837,17 @@ nsresult GetShadowFile(const nsAString& 
     return rv;
   }
 
   archiveFile.forget(aArchiveFile);
   return NS_OK;
 }
 
 nsresult SetShadowJournalMode(mozIStorageConnection* aConnection) {
-  AssertIsOnIOThread();
+  MOZ_ASSERT(IsOnIOThread() || IsOnConnectionThread());
   MOZ_ASSERT(aConnection);
 
   // Try enabling WAL mode. This can fail in various circumstances so we have to
   // check the results here.
   NS_NAMED_LITERAL_CSTRING(journalModeQueryStart, "PRAGMA journal_mode = ");
   NS_NAMED_LITERAL_CSTRING(journalModeWAL, "wal");
 
   nsCOMPtr<mozIStorageStatement> stmt;
@@ -925,17 +925,17 @@ nsresult SetShadowJournalMode(mozIStorag
     }
   }
 
   return NS_OK;
 }
 
 nsresult CreateShadowStorageConnection(const nsAString& aBasePath,
                                        mozIStorageConnection** aConnection) {
-  AssertIsOnIOThread();
+  MOZ_ASSERT(IsOnIOThread() || IsOnConnectionThread());
   MOZ_ASSERT(!aBasePath.IsEmpty());
   MOZ_ASSERT(aConnection);
 
   nsCOMPtr<nsIFile> shadowFile;
   nsresult rv = GetShadowFile(aBasePath, getter_AddRefs(shadowFile));
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
@@ -1437,25 +1437,30 @@ class DatastoreOperationBase : public Ru
         mMayProceed(true) {}
 
   ~DatastoreOperationBase() override { MOZ_ASSERT(!mMayProceed); }
 };
 
 class ConnectionDatastoreOperationBase : public DatastoreOperationBase {
  protected:
   RefPtr<Connection> mConnection;
+  /**
+   * This boolean flag is used by the CloseOp to avoid creating empty databases.
+   */
+  const bool mEnsureStorageConnection;
 
  public:
   // This callback will be called on the background thread before releasing the
   // final reference to this request object. Subclasses may perform any
   // additional cleanup here but must always call the base class implementation.
   virtual void Cleanup();
 
  protected:
-  ConnectionDatastoreOperationBase(Connection* aConnection);
+  ConnectionDatastoreOperationBase(Connection* aConnection,
+                                   bool aEnsureStorageConnection = true);
 
   ~ConnectionDatastoreOperationBase();
 
   // Must be overridden in subclasses. Called on the target thread to allow the
   // subclass to perform necessary datastore operations. A successful return
   // value will trigger an OnSuccess callback on the background thread while
   // while a failure value will trigger an OnFailure callback.
   virtual nsresult DoDatastoreWork() = 0;
@@ -1476,28 +1481,42 @@ class ConnectionDatastoreOperationBase :
 
 class Connection final {
   friend class ConnectionThread;
 
  public:
   class CachedStatement;
 
  private:
+  class InitOriginHelper;
+
   class FlushOp;
   class CloseOp;
 
   RefPtr<ConnectionThread> mConnectionThread;
+  RefPtr<QuotaClient> mQuotaClient;
   nsCOMPtr<nsITimer> mFlushTimer;
   nsCOMPtr<mozIStorageConnection> mStorageConnection;
   nsAutoPtr<ArchivedOriginScope> mArchivedOriginScope;
   nsInterfaceHashtable<nsCStringHashKey, mozIStorageStatement>
       mCachedStatements;
   WriteOptimizer mWriteOptimizer;
+  const nsCString mSuffix;
+  const nsCString mGroup;
   const nsCString mOrigin;
-  const nsString mDirectoryPath;
+  nsString mDirectoryPath;
+  /**
+   * Propagated from PrepareDatastoreOp. PrepareDatastoreOp may defer the
+   * creation of the localstorage client directory and database on the
+   * QuotaManager IO thread in its DatabaseWork method to
+   * Connection::EnsureStorageConnection, in which case the method needs to know
+   * it is responsible for taking those actions (without redundantly performing
+   * the existence checks).
+   */
+  const bool mDatabaseNotAvailable;
   bool mFlushScheduled;
 #ifdef DEBUG
   bool mInUpdateBatch;
 #endif
 
  public:
   NS_INLINE_DECL_REFCOUNTING(mozilla::dom::Connection)
 
@@ -1536,31 +1555,31 @@ class Connection final {
 
   //////////////////////////////////////////////////////////////////////////////
   // Methods which can only be called on the connection thread.
 
   nsresult EnsureStorageConnection();
 
   mozIStorageConnection* StorageConnection() const {
     AssertIsOnConnectionThread();
-    MOZ_ASSERT(mStorageConnection);
 
     return mStorageConnection;
   }
 
   void CloseStorageConnection();
 
   nsresult GetCachedStatement(const nsACString& aQuery,
                               CachedStatement* aCachedStatement);
 
  private:
   // Only created by ConnectionThread.
-  Connection(ConnectionThread* aConnectionThread, const nsACString& aOrigin,
-             const nsAString& aDirectoryPath,
-             nsAutoPtr<ArchivedOriginScope>&& aArchivedOriginScope);
+  Connection(ConnectionThread* aConnectionThread, const nsACString& aSuffix,
+             const nsACString& aGroup, const nsACString& aOrigin,
+             nsAutoPtr<ArchivedOriginScope>&& aArchivedOriginScope,
+             bool aDatabaseNotAvailable);
 
   ~Connection();
 
   void ScheduleFlush();
 
   void Flush();
 
   static void FlushTimerCallback(nsITimer* aTimer, void* aClosure);
@@ -1585,34 +1604,77 @@ class Connection::CachedStatement final 
   void Assign(Connection* aConnection,
               already_AddRefed<mozIStorageStatement> aStatement);
 
   // No funny business allowed.
   CachedStatement(const CachedStatement&) = delete;
   CachedStatement& operator=(const CachedStatement&) = delete;
 };
 
+/**
+ * Helper to invoke EnsureOriginIsInitialized and InitUsageForOrigin on the
+ * QuotaManager IO thread from the LocalStorage connection thread when creating
+ * a database connection on demand. This is necessary because we attempt to
+ * defer the creation of the origin directory and the database until absolutely
+ * needed, but the directory creation and origin initialization must happen on
+ * the QM IO thread for invariant reasons. (We can't just use a mutex because
+ * there could be logic on the IO thread that also wants to deal with the same
+ * origin, so we need to queue a runnable and wait our turn.)
+ */
+class Connection::InitOriginHelper final : public Runnable {
+  mozilla::Monitor mMonitor;
+  const nsCString mSuffix;
+  const nsCString mGroup;
+  const nsCString mOrigin;
+  nsString mOriginDirectoryPath;
+  nsresult mIOThreadResultCode;
+  bool mWaiting;
+
+ public:
+  InitOriginHelper(const nsACString& aSuffix, const nsACString& aGroup,
+                   const nsACString& aOrigin)
+      : Runnable("dom::localstorage::Connection::InitOriginHelper"),
+        mMonitor("InitOriginHelper::mMonitor"),
+        mSuffix(aSuffix),
+        mGroup(aGroup),
+        mOrigin(aOrigin),
+        mIOThreadResultCode(NS_OK),
+        mWaiting(true) {
+    AssertIsOnConnectionThread();
+  }
+
+  nsresult BlockAndReturnOriginDirectoryPath(nsAString& aOriginDirectoryPath);
+
+ private:
+  ~InitOriginHelper() {}
+
+  nsresult RunOnIOThread();
+
+  NS_DECL_NSIRUNNABLE
+};
+
 class Connection::FlushOp final : public ConnectionDatastoreOperationBase {
-  RefPtr<QuotaClient> mQuotaClient;
   WriteOptimizer mWriteOptimizer;
   bool mShadowWrites;
 
  public:
   FlushOp(Connection* aConnection, WriteOptimizer&& aWriteOptimizer);
 
  private:
   nsresult DoDatastoreWork() override;
 };
 
 class Connection::CloseOp final : public ConnectionDatastoreOperationBase {
   nsCOMPtr<nsIRunnable> mCallback;
 
  public:
   CloseOp(Connection* aConnection, nsIRunnable* aCallback)
-      : ConnectionDatastoreOperationBase(aConnection), mCallback(aCallback) {}
+      : ConnectionDatastoreOperationBase(aConnection,
+                                         /* aEnsureStorageConnection */ false),
+        mCallback(aCallback) {}
 
  private:
   nsresult DoDatastoreWork() override;
 
   void Cleanup() override;
 };
 
 class ConnectionThread final {
@@ -1628,18 +1690,20 @@ class ConnectionThread final {
     NS_ASSERT_OWNINGTHREAD(ConnectionThread);
   }
 
   bool IsOnConnectionThread();
 
   void AssertIsOnConnectionThread();
 
   already_AddRefed<Connection> CreateConnection(
-      const nsACString& aOrigin, const nsAString& aDirectoryPath,
-      nsAutoPtr<ArchivedOriginScope>&& aArchivedOriginScope);
+      const nsACString& aSuffix, const nsACString& aGroup,
+      const nsACString& aOrigin,
+      nsAutoPtr<ArchivedOriginScope>&& aArchivedOriginScope,
+      bool aDatabaseNotAvailable);
 
   void Shutdown();
 
   NS_INLINE_DECL_REFCOUNTING(ConnectionThread)
 
  private:
   ~ConnectionThread();
 };
@@ -2339,25 +2403,24 @@ class PrepareDatastoreOp
   nsDataHashtable<nsStringHashKey, LSValue> mValues;
   nsTArray<LSItemInfo> mOrderedItems;
   const LSRequestCommonParams mParams;
   Maybe<ContentParentId> mContentParentId;
   nsCString mSuffix;
   nsCString mGroup;
   nsCString mMainThreadOrigin;
   nsCString mOrigin;
-  nsString mDirectoryPath;
   nsString mDatabaseFilePath;
   uint32_t mPrivateBrowsingId;
   int64_t mUsage;
   int64_t mSizeOfKeys;
   int64_t mSizeOfItems;
   uint64_t mDatastoreId;
   NestedState mNestedState;
-  const bool mCreateIfNotExists;
+  const bool mForPreload;
   bool mDatabaseNotAvailable;
   bool mRequestedDirectoryLock;
   bool mInvalidated;
 
 #ifdef DEBUG
   int64_t mDEBUGUsage;
 #endif
 
@@ -2878,16 +2941,32 @@ void InitUsageForOrigin(const nsACString
   if (!gUsages) {
     gUsages = new UsageHashtable();
   }
 
   MOZ_ASSERT(!gUsages->Contains(aOrigin));
   gUsages->Put(aOrigin, aUsage);
 }
 
+bool GetUsageForOrigin(const nsACString& aOrigin, int64_t& aUsage) {
+  AssertIsOnIOThread();
+
+  if (gUsages) {
+    int64_t usage;
+    if (gUsages->Get(aOrigin, &usage)) {
+      MOZ_ASSERT(usage >= 0);
+
+      aUsage = usage;
+      return true;
+    }
+  }
+
+  return false;
+}
+
 void UpdateUsageForOrigin(const nsACString& aOrigin, int64_t aUsage) {
   AssertIsOnIOThread();
   MOZ_ASSERT(gUsages);
   MOZ_ASSERT(gUsages->Contains(aOrigin));
 
   gUsages->Put(aOrigin, aUsage);
 }
 
@@ -3998,18 +4077,19 @@ nsresult WriteOptimizer::ClearInfo::Perf
  * DatastoreOperationBase
  ******************************************************************************/
 
 /*******************************************************************************
  * ConnectionDatastoreOperationBase
  ******************************************************************************/
 
 ConnectionDatastoreOperationBase::ConnectionDatastoreOperationBase(
-    Connection* aConnection)
-    : mConnection(aConnection) {
+    Connection* aConnection, bool aEnsureStorageConnection)
+    : mConnection(aConnection),
+      mEnsureStorageConnection(aEnsureStorageConnection) {
   MOZ_ASSERT(aConnection);
 }
 
 ConnectionDatastoreOperationBase::~ConnectionDatastoreOperationBase() {
   MOZ_ASSERT(!mConnection,
              "ConnectionDatabaseOperationBase::Cleanup() was not called by a "
              "subclass!");
 }
@@ -4033,22 +4113,30 @@ void ConnectionDatastoreOperationBase::O
 void ConnectionDatastoreOperationBase::RunOnConnectionThread() {
   AssertIsOnConnectionThread();
   MOZ_ASSERT(mConnection);
   MOZ_ASSERT(NS_SUCCEEDED(ResultCode()));
 
   if (!MayProceedOnNonOwningThread()) {
     SetFailureCode(NS_ERROR_FAILURE);
   } else {
-    nsresult rv = mConnection->EnsureStorageConnection();
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      SetFailureCode(rv);
-    } else {
-      MOZ_ASSERT(mConnection->StorageConnection());
-
+    nsresult rv = NS_OK;
+
+    // The boolean flag is only used by the CloseOp to avoid creating empty
+    // databases.
+    if (mEnsureStorageConnection) {
+      rv = mConnection->EnsureStorageConnection();
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        SetFailureCode(rv);
+      } else {
+        MOZ_ASSERT(mConnection->StorageConnection());
+      }
+    }
+
+    if (NS_SUCCEEDED(rv)) {
       rv = DoDatastoreWork();
       if (NS_FAILED(rv)) {
         SetFailureCode(rv);
       }
     }
   }
 
   MOZ_ALWAYS_SUCCEEDS(OwningEventTarget()->Dispatch(this, NS_DISPATCH_NORMAL));
@@ -4082,32 +4170,36 @@ ConnectionDatastoreOperationBase::Run() 
   return NS_OK;
 }
 
 /*******************************************************************************
  * Connection implementation
  ******************************************************************************/
 
 Connection::Connection(ConnectionThread* aConnectionThread,
+                       const nsACString& aSuffix, const nsACString& aGroup,
                        const nsACString& aOrigin,
-                       const nsAString& aDirectoryPath,
-                       nsAutoPtr<ArchivedOriginScope>&& aArchivedOriginScope)
+                       nsAutoPtr<ArchivedOriginScope>&& aArchivedOriginScope,
+                       bool aDatabaseNotAvailable)
     : mConnectionThread(aConnectionThread),
+      mQuotaClient(QuotaClient::GetInstance()),
       mArchivedOriginScope(std::move(aArchivedOriginScope)),
+      mSuffix(aSuffix),
+      mGroup(aGroup),
       mOrigin(aOrigin),
-      mDirectoryPath(aDirectoryPath),
+      mDatabaseNotAvailable(aDatabaseNotAvailable),
       mFlushScheduled(false)
 #ifdef DEBUG
       ,
       mInUpdateBatch(false)
 #endif
 {
   AssertIsOnOwningThread();
+  MOZ_ASSERT(!aGroup.IsEmpty());
   MOZ_ASSERT(!aOrigin.IsEmpty());
-  MOZ_ASSERT(!aDirectoryPath.IsEmpty());
 }
 
 Connection::~Connection() {
   AssertIsOnOwningThread();
   MOZ_ASSERT(!mStorageConnection);
   MOZ_ASSERT(!mCachedStatements.Count());
   MOZ_ASSERT(!mInUpdateBatch);
   MOZ_ASSERT(!mFlushScheduled);
@@ -4189,42 +4281,145 @@ void Connection::EndUpdateBatch() {
 #ifdef DEBUG
   mInUpdateBatch = false;
 #endif
 }
 
 nsresult Connection::EnsureStorageConnection() {
   AssertIsOnConnectionThread();
 
-  if (!mStorageConnection) {
-    nsCOMPtr<nsIFile> file;
-    nsresult rv = NS_NewLocalFile(mDirectoryPath, false, getter_AddRefs(file));
+  if (mStorageConnection) {
+    return NS_OK;
+  }
+
+  nsresult rv;
+
+  QuotaManager* quotaManager = QuotaManager::Get();
+  MOZ_ASSERT(quotaManager);
+
+  if (!mDatabaseNotAvailable) {
+    nsCOMPtr<nsIFile> directoryEntry;
+    rv = quotaManager->GetDirectoryForOrigin(PERSISTENCE_TYPE_DEFAULT, mOrigin,
+                                             getter_AddRefs(directoryEntry));
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
 
-    rv = file->Append(NS_LITERAL_STRING(DATA_FILE_NAME));
+    rv = directoryEntry->Append(NS_LITERAL_STRING(LS_DIRECTORY_NAME));
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+
+    rv = directoryEntry->GetPath(mDirectoryPath);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
 
-    nsString filePath;
-    rv = file->GetPath(filePath);
+    rv = directoryEntry->Append(NS_LITERAL_STRING(DATA_FILE_NAME));
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+
+    nsString databaseFilePath;
+    rv = directoryEntry->GetPath(databaseFilePath);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
 
     nsCOMPtr<mozIStorageConnection> storageConnection;
-    rv = GetStorageConnection(filePath, getter_AddRefs(storageConnection));
+    rv = GetStorageConnection(databaseFilePath,
+                              getter_AddRefs(storageConnection));
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
 
     mStorageConnection = storageConnection;
-  }
+
+    return NS_OK;
+  }
+
+  RefPtr<InitOriginHelper> helper =
+      new InitOriginHelper(mSuffix, mGroup, mOrigin);
+
+  nsString originDirectoryPath;
+  rv = helper->BlockAndReturnOriginDirectoryPath(originDirectoryPath);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  nsCOMPtr<nsIFile> directoryEntry;
+  rv = NS_NewLocalFile(originDirectoryPath, false,
+                       getter_AddRefs(directoryEntry));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = directoryEntry->Append(NS_LITERAL_STRING(LS_DIRECTORY_NAME));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = directoryEntry->GetPath(mDirectoryPath);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  bool exists;
+  rv = directoryEntry->Exists(&exists);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  if (!exists) {
+    rv = directoryEntry->Create(nsIFile::DIRECTORY_TYPE, 0755);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+  }
+
+  rv = directoryEntry->Append(NS_LITERAL_STRING(DATA_FILE_NAME));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  nsCOMPtr<nsIFile> usageFile;
+  rv = GetUsageFile(mDirectoryPath, getter_AddRefs(usageFile));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  nsCOMPtr<mozIStorageConnection> storageConnection;
+  bool removedUsageFile;
+
+  rv = CreateStorageConnection(directoryEntry, usageFile, mOrigin,
+                               getter_AddRefs(storageConnection),
+                               &removedUsageFile);
+
+  MOZ_ASSERT(!removedUsageFile);
+
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  MOZ_ASSERT(mQuotaClient);
+
+  MutexAutoLock shadowDatabaseLock(mQuotaClient->ShadowDatabaseMutex());
+
+  nsCOMPtr<mozIStorageConnection> shadowConnection;
+  if (!gInitializedShadowStorage) {
+    rv = CreateShadowStorageConnection(quotaManager->GetBasePath(),
+                                       getter_AddRefs(shadowConnection));
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+
+    gInitializedShadowStorage = true;
+  }
+
+  mStorageConnection = storageConnection;
 
   return NS_OK;
 }
 
 void Connection::CloseStorageConnection() {
   AssertIsOnConnectionThread();
   MOZ_ASSERT(mStorageConnection);
 
@@ -4343,24 +4538,86 @@ void Connection::CachedStatement::Assign
 
   mStatement = aStatement;
 
   if (mStatement) {
     mScoper.emplace(mStatement);
   }
 }
 
+nsresult Connection::InitOriginHelper::BlockAndReturnOriginDirectoryPath(
+    nsAString& aOriginDirectoryPath) {
+  AssertIsOnConnectionThread();
+
+  QuotaManager* quotaManager = QuotaManager::Get();
+  MOZ_ASSERT(quotaManager);
+
+  MOZ_ALWAYS_SUCCEEDS(
+      quotaManager->IOThread()->Dispatch(this, NS_DISPATCH_NORMAL));
+
+  mozilla::MonitorAutoLock lock(mMonitor);
+  while (mWaiting) {
+    lock.Wait();
+  }
+
+  if (NS_WARN_IF(NS_FAILED(mIOThreadResultCode))) {
+    return mIOThreadResultCode;
+  }
+
+  aOriginDirectoryPath = mOriginDirectoryPath;
+  return NS_OK;
+}
+
+nsresult Connection::InitOriginHelper::RunOnIOThread() {
+  AssertIsOnIOThread();
+
+  QuotaManager* quotaManager = QuotaManager::Get();
+  MOZ_ASSERT(quotaManager);
+
+  nsCOMPtr<nsIFile> directoryEntry;
+  nsresult rv = quotaManager->EnsureOriginIsInitialized(
+      PERSISTENCE_TYPE_DEFAULT, mSuffix, mGroup, mOrigin,
+      getter_AddRefs(directoryEntry));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = directoryEntry->GetPath(mOriginDirectoryPath);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  InitUsageForOrigin(mOrigin, 0);
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+Connection::InitOriginHelper::Run() {
+  AssertIsOnIOThread();
+
+  nsresult rv = RunOnIOThread();
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    mIOThreadResultCode = rv;
+  }
+
+  mozilla::MonitorAutoLock lock(mMonitor);
+  MOZ_ASSERT(mWaiting);
+
+  mWaiting = false;
+  lock.Notify();
+
+  return NS_OK;
+}
+
 Connection::FlushOp::FlushOp(Connection* aConnection,
                              WriteOptimizer&& aWriteOptimizer)
     : ConnectionDatastoreOperationBase(aConnection),
-      mQuotaClient(QuotaClient::GetInstance()),
       mWriteOptimizer(std::move(aWriteOptimizer)),
-      mShadowWrites(gShadowWrites) {
-  MOZ_ASSERT(mQuotaClient);
-}
+      mShadowWrites(gShadowWrites) {}
 
 nsresult Connection::FlushOp::DoDatastoreWork() {
   AssertIsOnConnectionThread();
   MOZ_ASSERT(mConnection);
 
   QuotaManager* quotaManager = QuotaManager::Get();
   MOZ_ASSERT(quotaManager);
 
@@ -4368,19 +4625,20 @@ nsresult Connection::FlushOp::DoDatastor
       mConnection->StorageConnection();
   MOZ_ASSERT(storageConnection);
 
   nsresult rv;
 
   Maybe<MutexAutoLock> shadowDatabaseLock;
 
   if (mShadowWrites) {
-    MOZ_ASSERT(mQuotaClient);
-
-    shadowDatabaseLock.emplace(mQuotaClient->ShadowDatabaseMutex());
+    MOZ_ASSERT(mConnection->mQuotaClient);
+
+    shadowDatabaseLock.emplace(
+        mConnection->mQuotaClient->ShadowDatabaseMutex());
 
     rv = AttachShadowDatabase(quotaManager->GetBasePath(), storageConnection);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
   }
 
   CachedStatement stmt;
@@ -4454,17 +4712,19 @@ nsresult Connection::FlushOp::DoDatastor
 
   return NS_OK;
 }
 
 nsresult Connection::CloseOp::DoDatastoreWork() {
   AssertIsOnConnectionThread();
   MOZ_ASSERT(mConnection);
 
-  mConnection->CloseStorageConnection();
+  if (mConnection->StorageConnection()) {
+    mConnection->CloseStorageConnection();
+  }
 
   return NS_OK;
 }
 
 void Connection::CloseOp::Cleanup() {
   AssertIsOnOwningThread();
   MOZ_ASSERT(mConnection);
 
@@ -4501,24 +4761,27 @@ bool ConnectionThread::IsOnConnectionThr
   return NS_SUCCEEDED(mThread->IsOnCurrentThread(&current)) && current;
 }
 
 void ConnectionThread::AssertIsOnConnectionThread() {
   MOZ_ASSERT(IsOnConnectionThread());
 }
 
 already_AddRefed<Connection> ConnectionThread::CreateConnection(
-    const nsACString& aOrigin, const nsAString& aDirectoryPath,
-    nsAutoPtr<ArchivedOriginScope>&& aArchivedOriginScope) {
+    const nsACString& aSuffix, const nsACString& aGroup,
+    const nsACString& aOrigin,
+    nsAutoPtr<ArchivedOriginScope>&& aArchivedOriginScope,
+    bool aDatabaseNotAvailable) {
   AssertIsOnOwningThread();
   MOZ_ASSERT(!aOrigin.IsEmpty());
   MOZ_ASSERT(!mConnections.GetWeak(aOrigin));
 
-  RefPtr<Connection> connection = new Connection(
-      this, aOrigin, aDirectoryPath, std::move(aArchivedOriginScope));
+  RefPtr<Connection> connection =
+      new Connection(this, aSuffix, aGroup, aOrigin,
+                     std::move(aArchivedOriginScope), aDatabaseNotAvailable);
   mConnections.Put(aOrigin, connection);
 
   return connection.forget();
 }
 
 void ConnectionThread::Shutdown() {
   AssertIsOnOwningThread();
   MOZ_ASSERT(mThread);
@@ -6283,18 +6546,18 @@ PrepareDatastoreOp::PrepareDatastoreOp(
               : aParams.get_LSRequestPrepareDatastoreParams().commonParams()),
       mContentParentId(aContentParentId),
       mPrivateBrowsingId(0),
       mUsage(0),
       mSizeOfKeys(0),
       mSizeOfItems(0),
       mDatastoreId(0),
       mNestedState(NestedState::BeforeNesting),
-      mCreateIfNotExists(aParams.type() ==
-                         LSRequestParams::TLSRequestPrepareDatastoreParams),
+      mForPreload(aParams.type() ==
+                  LSRequestParams::TLSRequestPreloadDatastoreParams),
       mDatabaseNotAvailable(false),
       mRequestedDirectoryLock(false),
       mInvalidated(false)
 #ifdef DEBUG
       ,
       mDEBUGUsage(0)
 #endif
 {
@@ -6596,91 +6859,136 @@ nsresult PrepareDatastoreOp::DatabaseWor
   if (NS_WARN_IF(QuotaClient::IsShuttingDownOnNonBackgroundThread()) ||
       !MayProceedOnNonOwningThread()) {
     return NS_ERROR_FAILURE;
   }
 
   QuotaManager* quotaManager = QuotaManager::Get();
   MOZ_ASSERT(quotaManager);
 
-  nsresult rv;
+  // This must be called before EnsureTemporaryStorageIsInitialized.
+  nsresult rv = quotaManager->EnsureStorageIsInitialized();
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  // This ensures that gUsages gets populated with usages for existings origin
+  // directories.
+  rv = quotaManager->EnsureTemporaryStorageIsInitialized();
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
 
   if (!gArchivedOrigins) {
     rv = LoadArchivedOrigins();
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
     MOZ_ASSERT(gArchivedOrigins);
   }
 
   bool hasDataForMigration = mArchivedOriginScope->HasMatches(gArchivedOrigins);
 
-  bool createIfNotExists = mCreateIfNotExists || hasDataForMigration;
-
+  // If there's nothing to preload (except the case when we want to migrate data
+  // during preloading), then we can finish the operation without creating a
+  // datastore in GetResponse (GetResponse won't create a datastore if
+  // mDatatabaseNotAvailable and mForPreload are both true).
+  int64_t usage;
+  if (mForPreload && !GetUsageForOrigin(mOrigin, usage) &&
+      !hasDataForMigration) {
+    return DatabaseNotAvailable();
+  }
+
+  // The origin directory doesn't need to be created when we don't have data for
+  // migration. It will be created on the connection thread in
+  // Connection::EnsureStorageConnection.
+  // However, origin quota must be initialized, GetQuotaObject in GetResponse
+  // would fail otherwise.
   nsCOMPtr<nsIFile> directoryEntry;
-  rv = quotaManager->EnsureOriginIsInitialized(
-      PERSISTENCE_TYPE_DEFAULT, mSuffix, mGroup, mOrigin, createIfNotExists,
-      getter_AddRefs(directoryEntry));
-  if (rv == NS_ERROR_NOT_AVAILABLE) {
-    return DatabaseNotAvailable();
-  }
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
+  if (hasDataForMigration) {
+    rv = quotaManager->EnsureOriginIsInitialized(
+        PERSISTENCE_TYPE_DEFAULT, mSuffix, mGroup, mOrigin,
+        getter_AddRefs(directoryEntry));
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+  } else {
+    rv = quotaManager->GetDirectoryForOrigin(PERSISTENCE_TYPE_DEFAULT, mOrigin,
+                                             getter_AddRefs(directoryEntry));
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+
+    quotaManager->EnsureQuotaForOrigin(PERSISTENCE_TYPE_DEFAULT, mGroup,
+                                       mOrigin);
   }
 
   rv = directoryEntry->Append(NS_LITERAL_STRING(LS_DIRECTORY_NAME));
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
-  rv = EnsureDirectoryEntry(directoryEntry, createIfNotExists,
+  nsString directoryPath;
+  rv = directoryEntry->GetPath(directoryPath);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  // The ls directory doesn't need to be created when we don't have data for
+  // migration. It will be created on the connection thread in
+  // Connection::EnsureStorageConnection.
+  rv = EnsureDirectoryEntry(directoryEntry,
+                            /* aCreateIfNotExists */ hasDataForMigration,
                             /* aIsDirectory */ true);
-  if (rv == NS_ERROR_NOT_AVAILABLE) {
-    return DatabaseNotAvailable();
-  }
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  rv = directoryEntry->GetPath(mDirectoryPath);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   rv = directoryEntry->Append(NS_LITERAL_STRING(DATA_FILE_NAME));
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
+  rv = directoryEntry->GetPath(mDatabaseFilePath);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  // The database doesn't need to be created when we don't have data for
+  // migration. It will be created on the connection thread in
+  // Connection::EnsureStorageConnection.
   bool alreadyExisted;
-  rv = EnsureDirectoryEntry(directoryEntry, createIfNotExists,
+  rv = EnsureDirectoryEntry(directoryEntry,
+                            /* aCreateIfNotExists */ hasDataForMigration,
                             /* aIsDirectory */ false, &alreadyExisted);
-  if (rv == NS_ERROR_NOT_AVAILABLE) {
-    return DatabaseNotAvailable();
-  }
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   if (alreadyExisted) {
     MOZ_ASSERT(gUsages);
     DebugOnly<bool> hasUsage = gUsages->Get(mOrigin, &mUsage);
     MOZ_ASSERT(hasUsage);
   } else {
+    // The database doesn't exist.
+
+    if (!hasDataForMigration) {
+      // The database doesn't exist and we don't have data for migration.
+      // Finish the operation, but create an empty datastore in GetResponse
+      // (GetResponse will create an empty datastore if mDatabaseNotAvailable
+      // is true and mForPreload is false).
+      return DatabaseNotAvailable();
+    }
+
     MOZ_ASSERT(mUsage == 0);
     InitUsageForOrigin(mOrigin, mUsage);
   }
 
-  rv = directoryEntry->GetPath(mDatabaseFilePath);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
   nsCOMPtr<nsIFile> usageFile;
-  rv = GetUsageFile(mDirectoryPath, getter_AddRefs(usageFile));
+  rv = GetUsageFile(directoryPath, getter_AddRefs(usageFile));
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   nsCOMPtr<mozIStorageConnection> connection;
   bool removedUsageFile;
 
   rv = CreateStorageConnection(directoryEntry, usageFile, mOrigin,
@@ -6863,17 +7171,20 @@ nsresult PrepareDatastoreOp::EnsureDirec
   bool exists;
   nsresult rv = aEntry->Exists(&exists);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   if (!exists) {
     if (!aCreateIfNotExists) {
-      return NS_ERROR_NOT_AVAILABLE;
+      if (aAlreadyExisted) {
+        *aAlreadyExisted = false;
+      }
+      return NS_OK;
     }
 
     if (aIsDirectory) {
       rv = aEntry->Create(nsIFile::DIRECTORY_TYPE, 0755);
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
     }
@@ -6956,17 +7267,18 @@ nsresult PrepareDatastoreOp::BeginLoadDa
     return NS_ERROR_FAILURE;
   }
 
   if (!gConnectionThread) {
     gConnectionThread = new ConnectionThread();
   }
 
   mConnection = gConnectionThread->CreateConnection(
-      mOrigin, mDirectoryPath, std::move(mArchivedOriginScope));
+      mSuffix, mGroup, mOrigin, std::move(mArchivedOriginScope),
+      /* aDatabaseNotAvailable */ false);
   MOZ_ASSERT(mConnection);
 
   // Must set this before dispatching otherwise we will race with the
   // connection thread.
   mNestedState = NestedState::DatabaseWorkLoadData;
 
   // Can't assign to mLoadDataOp directly since that's a weak reference and
   // LoadDataOp is reference counted.
@@ -7053,32 +7365,48 @@ nsresult PrepareDatastoreOp::NestedRun()
   return NS_OK;
 }
 
 void PrepareDatastoreOp::GetResponse(LSRequestResponse& aResponse) {
   AssertIsOnOwningThread();
   MOZ_ASSERT(mState == State::SendingResults);
   MOZ_ASSERT(NS_SUCCEEDED(ResultCode()));
 
-  if (mDatabaseNotAvailable) {
-    MOZ_ASSERT(!mCreateIfNotExists);
-
+  // A datastore is not created when we are just trying to preload data and
+  // there's no database file.
+  if (mDatabaseNotAvailable && mForPreload) {
     LSRequestPreloadDatastoreResponse preloadDatastoreResponse;
 
     aResponse = preloadDatastoreResponse;
 
     return;
   }
 
   if (!mDatastore) {
     MOZ_ASSERT(mUsage == mDEBUGUsage);
 
     RefPtr<QuotaObject> quotaObject;
 
     if (mPrivateBrowsingId == 0) {
+      if (!mConnection) {
+        // This can happen when there's no database file.
+        MOZ_ASSERT(mDatabaseNotAvailable);
+
+        // Even though there's no database file, we need to create a connection
+        // and pass it to datastore.
+        if (!gConnectionThread) {
+          gConnectionThread = new ConnectionThread();
+        }
+
+        mConnection = gConnectionThread->CreateConnection(
+            mSuffix, mGroup, mOrigin, std::move(mArchivedOriginScope),
+            /* aDatabaseNotAvailable */ true);
+        MOZ_ASSERT(mConnection);
+      }
+
       quotaObject = GetQuotaObject();
       MOZ_ASSERT(quotaObject);
     }
 
     mDatastore = new Datastore(mOrigin, mPrivateBrowsingId, mUsage, mSizeOfKeys,
                                mSizeOfItems, mDirectoryLock.forget(),
                                mConnection.forget(), quotaObject.forget(),
                                mValues, mOrderedItems);
@@ -7092,38 +7420,38 @@ void PrepareDatastoreOp::GetResponse(LSR
     MOZ_ASSERT(!gDatastores->Get(mOrigin));
     gDatastores->Put(mOrigin, mDatastore);
   }
 
   mDatastoreId = ++gLastDatastoreId;
 
   nsAutoPtr<PreparedDatastore> preparedDatastore(
       new PreparedDatastore(mDatastore, mContentParentId, mOrigin, mDatastoreId,
-                            /* aForPreload */ !mCreateIfNotExists));
+                            /* aForPreload */ mForPreload));
 
   if (!gPreparedDatastores) {
     gPreparedDatastores = new PreparedDatastoreHashtable();
   }
   gPreparedDatastores->Put(mDatastoreId, preparedDatastore);
 
   if (mInvalidated) {
     preparedDatastore->Invalidate();
   }
 
   preparedDatastore.forget();
 
-  if (mCreateIfNotExists) {
+  if (mForPreload) {
+    LSRequestPreloadDatastoreResponse preloadDatastoreResponse;
+
+    aResponse = preloadDatastoreResponse;
+  } else {
     LSRequestPrepareDatastoreResponse prepareDatastoreResponse;
     prepareDatastoreResponse.datastoreId() = mDatastoreId;
 
     aResponse = prepareDatastoreResponse;
-  } else {
-    LSRequestPreloadDatastoreResponse preloadDatastoreResponse;
-
-    aResponse = preloadDatastoreResponse;
   }
 }
 
 void PrepareDatastoreOp::Cleanup() {
   AssertIsOnOwningThread();
 
   if (mDatastore) {
     MOZ_ASSERT(mDatastoreId > 0);
@@ -8139,22 +8467,20 @@ nsresult QuotaClient::GetUsageForOrigin(
                                         UsageInfo* aUsageInfo) {
   AssertIsOnIOThread();
   MOZ_ASSERT(aPersistenceType == PERSISTENCE_TYPE_DEFAULT);
   MOZ_ASSERT(aUsageInfo);
 
   // We can't open the database at this point, since it can be already used
   // by the connection thread. Use the cached value instead.
 
-  if (gUsages) {
-    int64_t usage;
-    if (gUsages->Get(aOrigin, &usage)) {
-      MOZ_ASSERT(usage >= 0);
-      aUsageInfo->AppendToDatabaseUsage(usage);
-    }
+  int64_t usage;
+  if (mozilla::dom::GetUsageForOrigin(aOrigin, usage)) {
+    MOZ_ASSERT(usage >= 0);
+    aUsageInfo->AppendToDatabaseUsage(usage);
   }
 
   return NS_OK;
 }
 
 nsresult QuotaClient::AboutToClearOrigins(
     const Nullable<PersistenceType>& aPersistenceType,
     const OriginScope& aOriginScope) {
--- a/dom/quota/ActorsParent.cpp
+++ b/dom/quota/ActorsParent.cpp
@@ -608,20 +608,30 @@ namespace {
 
 class OriginInfo final {
   friend class GroupInfo;
   friend class QuotaManager;
   friend class QuotaObject;
 
  public:
   OriginInfo(GroupInfo* aGroupInfo, const nsACString& aOrigin, uint64_t aUsage,
-             int64_t aAccessTime, bool aPersisted);
+             int64_t aAccessTime, bool aPersisted, bool aDirectoryExists);
 
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(OriginInfo)
 
+  GroupInfo* GetGroupInfo() const { return mGroupInfo; }
+
+  const nsCString& Origin() const { return mOrigin; }
+
+  int64_t LockedUsage() const {
+    AssertCurrentThreadOwnsQuotaMutex();
+
+    return mUsage;
+  }
+
   int64_t LockedAccessTime() const {
     AssertCurrentThreadOwnsQuotaMutex();
 
     return mAccessTime;
   }
 
   bool LockedPersisted() const {
     AssertCurrentThreadOwnsQuotaMutex();
@@ -649,16 +659,29 @@ class OriginInfo final {
 
   nsDataHashtable<nsStringHashKey, QuotaObject*> mQuotaObjects;
 
   GroupInfo* mGroupInfo;
   const nsCString mOrigin;
   uint64_t mUsage;
   int64_t mAccessTime;
   bool mPersisted;
+  /**
+   * In some special cases like the LocalStorage client where it's possible to
+   * create a Quota-using representation but not actually write any data, we
+   * want to be able to track quota for an origin without creating its origin
+   * directory or the per-client files until they are actually needed to store
+   * data. In those cases, the OriginInfo will be created by
+   * EnsureQuotaForOrigin and the resulting mDirectoryExists will be false until
+   * the origin actually needs to be created. It is possible for mUsage to be
+   * greater than zero while mDirectoryExists is false, representing a state
+   * where a client like LocalStorage has reserved quota for disk writes, but
+   * has not yet flushed the data to disk.
+   */
+  bool mDirectoryExists;
 };
 
 class OriginInfoLRUComparator {
  public:
   bool Equals(const OriginInfo* a, const OriginInfo* b) const {
     return a && b ? a->LockedAccessTime() == b->LockedAccessTime()
                   : !a && !b ? true : false;
   }
@@ -684,16 +707,18 @@ class GroupInfo final {
         mUsage(0) {
     MOZ_ASSERT(aPersistenceType != PERSISTENCE_TYPE_PERSISTENT);
 
     MOZ_COUNT_CTOR(GroupInfo);
   }
 
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(GroupInfo)
 
+  PersistenceType GetPersistenceType() const { return mPersistenceType; }
+
  private:
   // Private destructor, to discourage deletion outside of Release():
   ~GroupInfo() { MOZ_COUNT_DTOR(GroupInfo); }
 
   already_AddRefed<OriginInfo> LockedGetOriginInfo(const nsACString& aOrigin);
 
   void LockedAddOriginInfo(OriginInfo* aOriginInfo);
 
@@ -1119,16 +1144,22 @@ class GetUsageOp final : public QuotaUsa
   bool mGetAll;
 
  public:
   explicit GetUsageOp(const UsageRequestParams& aParams);
 
  private:
   ~GetUsageOp() {}
 
+  void ProcessOriginInternal(QuotaManager* aQuotaManager,
+                             const PersistenceType aPersistenceType,
+                             const nsACString& aOrigin,
+                             const int64_t aTimestamp, const bool aPersisted,
+                             const uint64_t aUsage);
+
   nsresult DoDirectoryWork(QuotaManager* aQuotaManager) override;
 
   bool IsCanceled() override;
 
   nsresult ProcessOrigin(QuotaManager* aQuotaManager, nsIFile* aOriginDir,
                          const bool aPersistent,
                          const PersistenceType aPersistenceType) override;
 
@@ -2219,36 +2250,31 @@ nsresult CreateDirectoryMetadata2(nsIFil
 
   return NS_OK;
 }
 
 nsresult CreateDirectoryMetadataFiles(nsIFile* aDirectory, bool aPersisted,
                                       const nsACString& aSuffix,
                                       const nsACString& aGroup,
                                       const nsACString& aOrigin,
-                                      int64_t* aTimestamp) {
-  AssertIsOnIOThread();
-
-  int64_t timestamp = PR_Now();
+                                      int64_t aTimestamp) {
+  AssertIsOnIOThread();
 
   nsresult rv =
-      CreateDirectoryMetadata(aDirectory, timestamp, aSuffix, aGroup, aOrigin);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  rv = CreateDirectoryMetadata2(aDirectory, timestamp, aPersisted, aSuffix,
+      CreateDirectoryMetadata(aDirectory, aTimestamp, aSuffix, aGroup, aOrigin);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = CreateDirectoryMetadata2(aDirectory, aTimestamp, aPersisted, aSuffix,
                                 aGroup, aOrigin);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
-  if (aTimestamp) {
-    *aTimestamp = timestamp;
-  }
   return NS_OK;
 }
 
 nsresult GetBinaryInputStream(nsIFile* aDirectory, const nsAString& aFilename,
                               nsIBinaryInputStream** aStream) {
   MOZ_ASSERT(!NS_IsMainThread());
   MOZ_ASSERT(aDirectory);
   MOZ_ASSERT(aStream);
@@ -3380,16 +3406,38 @@ uint64_t QuotaManager::CollectOriginsFor
     }
 
     return sizeToBeFreed;
   }
 
   return 0;
 }
 
+template <typename P>
+void QuotaManager::CollectPendingOriginsForListing(P aPredicate) {
+  MutexAutoLock lock(mQuotaMutex);
+
+  for (auto iter = mGroupInfoPairs.Iter(); !iter.Done(); iter.Next()) {
+    GroupInfoPair* pair = iter.UserData();
+
+    MOZ_ASSERT(!iter.Key().IsEmpty());
+    MOZ_ASSERT(pair);
+
+    RefPtr<GroupInfo> groupInfo =
+        pair->LockedGetGroupInfo(PERSISTENCE_TYPE_DEFAULT);
+    if (groupInfo) {
+      for (RefPtr<OriginInfo>& originInfo : groupInfo->mOriginInfos) {
+        if (!originInfo->mDirectoryExists) {
+          aPredicate(originInfo);
+        }
+      }
+    }
+  }
+}
+
 nsresult QuotaManager::Init(const nsAString& aBasePath) {
   mBasePath = aBasePath;
 
   nsCOMPtr<nsIFile> baseDir;
   nsresult rv = NS_NewLocalFile(aBasePath, false, getter_AddRefs(baseDir));
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
@@ -3516,34 +3564,76 @@ void QuotaManager::InitQuotaForOrigin(Pe
                                       const nsACString& aOrigin,
                                       uint64_t aUsageBytes, int64_t aAccessTime,
                                       bool aPersisted) {
   AssertIsOnIOThread();
   MOZ_ASSERT(aPersistenceType != PERSISTENCE_TYPE_PERSISTENT);
 
   MutexAutoLock lock(mQuotaMutex);
 
-  GroupInfoPair* pair;
-  if (!mGroupInfoPairs.Get(aGroup, &pair)) {
-    pair = new GroupInfoPair();
-    mGroupInfoPairs.Put(aGroup, pair);
-    // The hashtable is now responsible to delete the GroupInfoPair.
-  }
-
-  RefPtr<GroupInfo> groupInfo = pair->LockedGetGroupInfo(aPersistenceType);
-  if (!groupInfo) {
-    groupInfo = new GroupInfo(pair, aPersistenceType, aGroup);
-    pair->LockedSetGroupInfo(aPersistenceType, groupInfo);
-  }
+  RefPtr<GroupInfo> groupInfo =
+      LockedGetOrCreateGroupInfo(aPersistenceType, aGroup);
 
   RefPtr<OriginInfo> originInfo =
-      new OriginInfo(groupInfo, aOrigin, aUsageBytes, aAccessTime, aPersisted);
+      new OriginInfo(groupInfo, aOrigin, aUsageBytes, aAccessTime, aPersisted,
+                     /* aDirectoryExists */ true);
   groupInfo->LockedAddOriginInfo(originInfo);
 }
 
+void QuotaManager::EnsureQuotaForOrigin(PersistenceType aPersistenceType,
+                                        const nsACString& aGroup,
+                                        const nsACString& aOrigin) {
+  AssertIsOnIOThread();
+  MOZ_ASSERT(aPersistenceType != PERSISTENCE_TYPE_PERSISTENT);
+
+  MutexAutoLock lock(mQuotaMutex);
+
+  RefPtr<GroupInfo> groupInfo =
+      LockedGetOrCreateGroupInfo(aPersistenceType, aGroup);
+
+  RefPtr<OriginInfo> originInfo = groupInfo->LockedGetOriginInfo(aOrigin);
+  if (!originInfo) {
+    originInfo = new OriginInfo(
+        groupInfo, aOrigin, /* aUsageBytes */ 0, /* aAccessTime */ PR_Now(),
+        /* aPersisted */ false, /* aDirectoryExists */ false);
+    groupInfo->LockedAddOriginInfo(originInfo);
+  }
+}
+
+void QuotaManager::NoteOriginDirectoryCreated(PersistenceType aPersistenceType,
+                                              const nsACString& aGroup,
+                                              const nsACString& aOrigin,
+                                              bool aPersisted,
+                                              int64_t& aTimestamp) {
+  AssertIsOnIOThread();
+  MOZ_ASSERT(aPersistenceType != PERSISTENCE_TYPE_PERSISTENT);
+
+  int64_t timestamp;
+
+  MutexAutoLock lock(mQuotaMutex);
+
+  RefPtr<GroupInfo> groupInfo =
+      LockedGetOrCreateGroupInfo(aPersistenceType, aGroup);
+
+  RefPtr<OriginInfo> originInfo = groupInfo->LockedGetOriginInfo(aOrigin);
+  if (originInfo) {
+    originInfo->mPersisted = aPersisted;
+    originInfo->mDirectoryExists = true;
+    timestamp = originInfo->LockedAccessTime();
+  } else {
+    timestamp = PR_Now();
+    RefPtr<OriginInfo> originInfo = new OriginInfo(
+        groupInfo, aOrigin, /* aUsageBytes */ 0, /* aAccessTime */ timestamp,
+        aPersisted, /* aDirectoryExists */ true);
+    groupInfo->LockedAddOriginInfo(originInfo);
+  }
+
+  aTimestamp = timestamp;
+}
+
 void QuotaManager::DecreaseUsageForOrigin(PersistenceType aPersistenceType,
                                           const nsACString& aGroup,
                                           const nsACString& aOrigin,
                                           int64_t aSize) {
   MOZ_ASSERT(!NS_IsMainThread());
   MOZ_ASSERT(aPersistenceType != PERSISTENCE_TYPE_PERSISTENT);
 
   MutexAutoLock lock(mQuotaMutex);
@@ -5624,41 +5714,37 @@ void QuotaManager::OpenDirectoryInternal
         mClients[index]->AbortOperations(iter.Get()->GetKey());
       }
     }
   }
 }
 
 nsresult QuotaManager::EnsureOriginIsInitialized(
     PersistenceType aPersistenceType, const nsACString& aSuffix,
-    const nsACString& aGroup, const nsACString& aOrigin,
-    bool aCreateIfNotExists, nsIFile** aDirectory) {
+    const nsACString& aGroup, const nsACString& aOrigin, nsIFile** aDirectory) {
   AssertIsOnIOThread();
   MOZ_ASSERT(aDirectory);
 
   nsCOMPtr<nsIFile> directory;
   bool created;
   nsresult rv = EnsureOriginIsInitializedInternal(
-      aPersistenceType, aSuffix, aGroup, aOrigin, aCreateIfNotExists,
-      getter_AddRefs(directory), &created);
-  if (rv == NS_ERROR_NOT_AVAILABLE) {
-    return rv;
-  }
+      aPersistenceType, aSuffix, aGroup, aOrigin, getter_AddRefs(directory),
+      &created);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   directory.forget(aDirectory);
   return NS_OK;
 }
 
 nsresult QuotaManager::EnsureOriginIsInitializedInternal(
     PersistenceType aPersistenceType, const nsACString& aSuffix,
-    const nsACString& aGroup, const nsACString& aOrigin,
-    bool aCreateIfNotExists, nsIFile** aDirectory, bool* aCreated) {
+    const nsACString& aGroup, const nsACString& aOrigin, nsIFile** aDirectory,
+    bool* aCreated) {
   AssertIsOnIOThread();
   MOZ_ASSERT(aDirectory);
   MOZ_ASSERT(aCreated);
 
   nsresult rv = EnsureStorageIsInitialized();
   NS_ENSURE_SUCCESS(rv, rv);
 
   // Get directory for this origin and persistence type.
@@ -5676,30 +5762,29 @@ nsresult QuotaManager::EnsureOriginIsIni
   } else {
     rv = EnsureTemporaryStorageIsInitialized();
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
   }
 
   bool created;
-  rv = EnsureOriginDirectory(directory, aCreateIfNotExists, &created);
-  if (rv == NS_ERROR_NOT_AVAILABLE) {
-    return rv;
-  }
+  rv = EnsureOriginDirectory(directory, &created);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   int64_t timestamp;
   if (aPersistenceType == PERSISTENCE_TYPE_PERSISTENT) {
     if (created) {
+      timestamp = PR_Now();
+
       rv = CreateDirectoryMetadataFiles(directory,
                                         /* aPersisted */ true, aSuffix, aGroup,
-                                        aOrigin, &timestamp);
+                                        aOrigin, timestamp);
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
     } else {
       rv = GetDirectoryMetadata2WithRestore(directory,
                                             /* aPersistent */ true, &timestamp,
                                             /* aPersisted */ nullptr);
       if (NS_WARN_IF(NS_FAILED(rv))) {
@@ -5710,27 +5795,25 @@ nsresult QuotaManager::EnsureOriginIsIni
     }
 
     rv = InitializeOrigin(aPersistenceType, aGroup, aOrigin, timestamp,
                           /* aPersisted */ true, directory);
     NS_ENSURE_SUCCESS(rv, rv);
 
     mInitializedOrigins.AppendElement(aOrigin);
   } else if (created) {
+    NoteOriginDirectoryCreated(aPersistenceType, aGroup, aOrigin,
+                               /* aPersisted */ false, timestamp);
+
     rv = CreateDirectoryMetadataFiles(directory,
                                       /* aPersisted */ false, aSuffix, aGroup,
-                                      aOrigin, &timestamp);
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      return rv;
-    }
-
-    // Don't need to traverse the directory, since it's empty.
-    InitQuotaForOrigin(aPersistenceType, aGroup, aOrigin,
-                       /* aUsageBytes */ 0, timestamp,
-                       /* aPersisted */ false);
+                                      aOrigin, timestamp);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
   }
 
   directory.forget(aDirectory);
   *aCreated = created;
   return NS_OK;
 }
 
 nsresult QuotaManager::EnsureTemporaryStorageIsInitialized() {
@@ -5798,33 +5881,28 @@ nsresult QuotaManager::EnsureTemporarySt
   mTemporaryStorageInitialized = true;
 
   CheckTemporaryStorageLimits();
 
   return rv;
 }
 
 nsresult QuotaManager::EnsureOriginDirectory(nsIFile* aDirectory,
-                                             bool aCreateIfNotExists,
                                              bool* aCreated) {
   AssertIsOnIOThread();
   MOZ_ASSERT(aDirectory);
   MOZ_ASSERT(aCreated);
 
   bool exists;
   nsresult rv = aDirectory->Exists(&exists);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   if (!exists) {
-    if (!aCreateIfNotExists) {
-      return NS_ERROR_NOT_AVAILABLE;
-    }
-
     nsString leafName;
     rv = aDirectory->GetLeafName(leafName);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
 
     if (!IsSanitizedOriginValid(NS_ConvertUTF16toUTF8(leafName))) {
       QM_WARNING(
@@ -6263,16 +6341,37 @@ void QuotaManager::LockedRemoveQuotaForO
 
       if (!pair->LockedHasGroupInfos()) {
         mGroupInfoPairs.Remove(aGroup);
       }
     }
   }
 }
 
+already_AddRefed<GroupInfo> QuotaManager::LockedGetOrCreateGroupInfo(
+    PersistenceType aPersistenceType, const nsACString& aGroup) {
+  mQuotaMutex.AssertCurrentThreadOwns();
+  MOZ_ASSERT(aPersistenceType != PERSISTENCE_TYPE_PERSISTENT);
+
+  GroupInfoPair* pair;
+  if (!mGroupInfoPairs.Get(aGroup, &pair)) {
+    pair = new GroupInfoPair();
+    mGroupInfoPairs.Put(aGroup, pair);
+    // The hashtable is now responsible to delete the GroupInfoPair.
+  }
+
+  RefPtr<GroupInfo> groupInfo = pair->LockedGetGroupInfo(aPersistenceType);
+  if (!groupInfo) {
+    groupInfo = new GroupInfo(pair, aPersistenceType, aGroup);
+    pair->LockedSetGroupInfo(aPersistenceType, groupInfo);
+  }
+
+  return groupInfo.forget();
+}
+
 already_AddRefed<OriginInfo> QuotaManager::LockedGetOriginInfo(
     PersistenceType aPersistenceType, const nsACString& aGroup,
     const nsACString& aOrigin) {
   mQuotaMutex.AssertCurrentThreadOwns();
   MOZ_ASSERT(aPersistenceType != PERSISTENCE_TYPE_PERSISTENT);
 
   GroupInfoPair* pair;
   if (mGroupInfoPairs.Get(aGroup, &pair)) {
@@ -6518,22 +6617,24 @@ bool QuotaManager::IsSanitizedOriginVali
   return valid;
 }
 
 /*******************************************************************************
  * Local class implementations
  ******************************************************************************/
 
 OriginInfo::OriginInfo(GroupInfo* aGroupInfo, const nsACString& aOrigin,
-                       uint64_t aUsage, int64_t aAccessTime, bool aPersisted)
+                       uint64_t aUsage, int64_t aAccessTime, bool aPersisted,
+                       bool aDirectoryExists)
     : mGroupInfo(aGroupInfo),
       mOrigin(aOrigin),
       mUsage(aUsage),
       mAccessTime(aAccessTime),
-      mPersisted(aPersisted) {
+      mPersisted(aPersisted),
+      mDirectoryExists(aDirectoryExists) {
   MOZ_ASSERT(aGroupInfo);
   MOZ_ASSERT_IF(aPersisted,
                 aGroupInfo->mPersistenceType == PERSISTENCE_TYPE_DEFAULT);
 
   MOZ_COUNT_CTOR(OriginInfo);
 }
 
 void OriginInfo::LockedDecreaseUsage(int64_t aSize) {
@@ -7626,16 +7727,57 @@ nsresult TraverseRepositoryHelper::Trave
 }
 
 GetUsageOp::GetUsageOp(const UsageRequestParams& aParams)
     : mGetAll(aParams.get_AllUsageParams().getAll()) {
   AssertIsOnOwningThread();
   MOZ_ASSERT(aParams.type() == UsageRequestParams::TAllUsageParams);
 }
 
+void GetUsageOp::ProcessOriginInternal(QuotaManager* aQuotaManager,
+                                       const PersistenceType aPersistenceType,
+                                       const nsACString& aOrigin,
+                                       const int64_t aTimestamp,
+                                       const bool aPersisted,
+                                       const uint64_t aUsage) {
+  if (!mGetAll && aQuotaManager->IsOriginInternal(aOrigin)) {
+    return;
+  }
+
+  OriginUsage* originUsage;
+
+  // We can't store pointers to OriginUsage objects in the hashtable
+  // since AppendElement() reallocates its internal array buffer as number
+  // of elements grows.
+  uint32_t index;
+  if (mOriginUsagesIndex.Get(aOrigin, &index)) {
+    originUsage = &mOriginUsages[index];
+  } else {
+    index = mOriginUsages.Length();
+
+    originUsage = mOriginUsages.AppendElement();
+
+    originUsage->origin() = aOrigin;
+    originUsage->persisted() = false;
+    originUsage->usage() = 0;
+    originUsage->lastAccessed() = 0;
+
+    mOriginUsagesIndex.Put(aOrigin, index);
+  }
+
+  if (aPersistenceType == PERSISTENCE_TYPE_DEFAULT) {
+    originUsage->persisted() = aPersisted;
+  }
+
+  originUsage->usage() = originUsage->usage() + aUsage;
+
+  originUsage->lastAccessed() =
+      std::max<int64_t>(originUsage->lastAccessed(), aTimestamp);
+}
+
 bool GetUsageOp::IsCanceled() {
   AssertIsOnIOThread();
 
   return mCanceled;
 }
 
 nsresult GetUsageOp::ProcessOrigin(QuotaManager* aQuotaManager,
                                    nsIFile* aOriginDir, const bool aPersistent,
@@ -7650,54 +7792,25 @@ nsresult GetUsageOp::ProcessOrigin(Quota
   nsCString group;
   nsCString origin;
   nsresult rv = aQuotaManager->GetDirectoryMetadata2WithRestore(
       aOriginDir, aPersistent, &timestamp, &persisted, suffix, group, origin);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
-  if (!mGetAll && aQuotaManager->IsOriginInternal(origin)) {
-    return NS_OK;
-  }
-
-  OriginUsage* originUsage;
-
-  // We can't store pointers to OriginUsage objects in the hashtable
-  // since AppendElement() reallocates its internal array buffer as number
-  // of elements grows.
-  uint32_t index;
-  if (mOriginUsagesIndex.Get(origin, &index)) {
-    originUsage = &mOriginUsages[index];
-  } else {
-    index = mOriginUsages.Length();
-
-    originUsage = mOriginUsages.AppendElement();
-
-    originUsage->origin() = origin;
-    originUsage->persisted() = false;
-    originUsage->usage() = 0;
-
-    mOriginUsagesIndex.Put(origin, index);
-  }
-
-  if (aPersistenceType == PERSISTENCE_TYPE_DEFAULT) {
-    originUsage->persisted() = persisted;
-  }
-
-  originUsage->lastAccessed() = timestamp;
-
   UsageInfo usageInfo;
   rv = GetUsageForOrigin(aQuotaManager, aPersistenceType, group, origin,
                          &usageInfo);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
-  originUsage->usage() = originUsage->usage() + usageInfo.TotalUsage();
+  ProcessOriginInternal(aQuotaManager, aPersistenceType, origin, timestamp,
+                        persisted, usageInfo.TotalUsage());
 
   return NS_OK;
 }
 
 nsresult GetUsageOp::DoDirectoryWork(QuotaManager* aQuotaManager) {
   AssertIsOnIOThread();
 
   AUTO_PROFILER_LABEL("GetUsageOp::DoDirectoryWork", OTHER);
@@ -7706,16 +7819,28 @@ nsresult GetUsageOp::DoDirectoryWork(Quo
 
   for (const PersistenceType type : kAllPersistenceTypes) {
     rv = TraverseRepository(aQuotaManager, type);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
   }
 
+  // TraverseRepository above only consulted the filesystem. We also need to
+  // consider origins which may have pending quota usage, such as buffered
+  // LocalStorage writes for an origin which didn't previously have any
+  // LocalStorage data.
+
+  aQuotaManager->CollectPendingOriginsForListing([&](OriginInfo* aOriginInfo) {
+    ProcessOriginInternal(
+        aQuotaManager, aOriginInfo->GetGroupInfo()->GetPersistenceType(),
+        aOriginInfo->Origin(), aOriginInfo->LockedAccessTime(),
+        aOriginInfo->LockedPersisted(), aOriginInfo->LockedUsage());
+  });
+
   return NS_OK;
 }
 
 void GetUsageOp::GetResponse(UsageRequestResponse& aResponse) {
   AssertIsOnOwningThread();
 
   aResponse = AllUsageResponse();
 
@@ -7921,17 +8046,17 @@ nsresult InitOriginOp::DoDirectoryWork(Q
   MOZ_ASSERT(!mPersistenceType.IsNull());
 
   AUTO_PROFILER_LABEL("InitOriginOp::DoDirectoryWork", OTHER);
 
   nsCOMPtr<nsIFile> directory;
   bool created;
   nsresult rv = aQuotaManager->EnsureOriginIsInitializedInternal(
       mPersistenceType.Value(), mSuffix, mGroup, mOriginScope.GetOrigin(),
-      /* aCreateIfNotExists */ true, getter_AddRefs(directory), &created);
+      getter_AddRefs(directory), &created);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   mCreated = created;
 
   return NS_OK;
 }
@@ -8434,39 +8559,39 @@ nsresult PersistOp::DoDirectoryWork(Quot
   nsresult rv = aQuotaManager->GetDirectoryForOrigin(mPersistenceType.Value(),
                                                      mOriginScope.GetOrigin(),
                                                      getter_AddRefs(directory));
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   bool created;
-  rv = aQuotaManager->EnsureOriginDirectory(directory,
-                                            /* aCreateIfNotExists */ true,
-                                            &created);
+  rv = aQuotaManager->EnsureOriginDirectory(directory, &created);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   if (created) {
     int64_t timestamp;
+
+    // Origin directory has been successfully created.
+    // Create OriginInfo too if temporary storage was already initialized.
+    if (aQuotaManager->IsTemporaryStorageInitialized()) {
+      aQuotaManager->NoteOriginDirectoryCreated(
+          mPersistenceType.Value(), mGroup, mOriginScope.GetOrigin(),
+          /* aPersisted */ true, timestamp);
+    } else {
+      timestamp = PR_Now();
+    }
+
     rv = CreateDirectoryMetadataFiles(directory,
                                       /* aPersisted */ true, mSuffix, mGroup,
-                                      mOriginScope.GetOrigin(), &timestamp);
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      return rv;
-    }
-
-    // Directory metadata has been successfully created.
-    // Create OriginInfo too if temporary storage was already initialized.
-    if (aQuotaManager->IsTemporaryStorageInitialized()) {
-      aQuotaManager->InitQuotaForOrigin(mPersistenceType.Value(), mGroup,
-                                        mOriginScope.GetOrigin(),
-                                        /* aUsageBytes */ 0, timestamp,
-                                        /* aPersisted */ true);
+                                      mOriginScope.GetOrigin(), timestamp);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
     }
   } else {
     // Get the persisted flag (restore the metadata file if necessary).
     bool persisted;
     rv = aQuotaManager->GetDirectoryMetadata2WithRestore(
         directory,
         /* aPersistent */ false,
         /* aTimestamp */ nullptr, &persisted);
@@ -8551,16 +8676,24 @@ nsresult ListInitializedOriginsOp::DoDir
 
   for (const PersistenceType type : kAllPersistenceTypes) {
     rv = TraverseRepository(aQuotaManager, type);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
   }
 
+  // TraverseRepository above only consulted the file-system to get a list of
+  // known origins, but we also need to include origins that have pending quota
+  // usage.
+
+  aQuotaManager->CollectPendingOriginsForListing([&](OriginInfo* aOriginInfo) {
+    mOrigins.AppendElement(aOriginInfo->Origin());
+  });
+
   return NS_OK;
 }
 
 bool ListInitializedOriginsOp::IsCanceled() {
   AssertIsOnIOThread();
 
   return mCanceled;
 }
--- a/dom/quota/QuotaManager.h
+++ b/dom/quota/QuotaManager.h
@@ -140,21 +140,51 @@ class QuotaManager final : public Backgr
   }
 
   bool IsTemporaryStorageInitialized() const {
     AssertIsOnIOThread();
 
     return mTemporaryStorageInitialized;
   }
 
+  /**
+   * For initialization of an origin where the directory already exists. This is
+   * used by EnsureTemporaryStorageIsInitialized/InitializeRepository once it
+   * has tallied origin usage by calling each of the QuotaClient InitOrigin
+   * methods.
+   */
   void InitQuotaForOrigin(PersistenceType aPersistenceType,
                           const nsACString& aGroup, const nsACString& aOrigin,
                           uint64_t aUsageBytes, int64_t aAccessTime,
                           bool aPersisted);
 
+  /**
+   * For use in special-cases like LSNG where we need to be able to know that
+   * there is no data stored for an origin. LSNG knows that there is 0 usage for
+   * its storage of an origin and wants to make sure there is a QuotaObject
+   * tracking this. This method will create a non-persisted, 0-usage,
+   * mDirectoryExists=false OriginInfo if there isn't already an OriginInfo. If
+   * an OriginInfo already exists, it will be left as-is, because that implies a
+   * different client has usages for the origin (and there's no need to add
+   * LSNG's 0 usage to the QuotaObject).
+   */
+  void EnsureQuotaForOrigin(PersistenceType aPersistenceType,
+                            const nsACString& aGroup,
+                            const nsACString& aOrigin);
+
+  /**
+   * For use when creating an origin directory. It's possible that origin usage
+   * is already being tracked due to a call to EnsureQuotaForOrigin, and in that
+   * case we need to update the existing OriginInfo rather than create a new one.
+   */
+  void NoteOriginDirectoryCreated(PersistenceType aPersistenceType,
+                                  const nsACString& aGroup,
+                                  const nsACString& aOrigin, bool aPersisted,
+                                  int64_t& aTimestamp);
+
   void DecreaseUsageForOrigin(PersistenceType aPersistenceType,
                               const nsACString& aGroup,
                               const nsACString& aOrigin, int64_t aSize);
 
   void UpdateOriginAccessTime(PersistenceType aPersistenceType,
                               const nsACString& aGroup,
                               const nsACString& aOrigin);
 
@@ -243,42 +273,55 @@ class QuotaManager final : public Backgr
                              const Nullable<Client::Type>& aClientType,
                              bool aExclusive,
                              OpenDirectoryListener* aOpenListener);
 
   // Collect inactive and the least recently used origins.
   uint64_t CollectOriginsForEviction(
       uint64_t aMinSizeToBeFreed, nsTArray<RefPtr<DirectoryLockImpl>>& aLocks);
 
+  /**
+   * Helper method to invoke the provided predicate on all "pending" OriginInfo
+   * instances. These are origins for which the origin directory has not yet
+   * been created but for which quota is already being tracked. This happens,
+   * for example, for the LocalStorage client where an origin that previously
+   * was not using LocalStorage can start issuing writes which it buffers until
+   * eventually flushing them. We defer creating the origin directory for as
+   * long as possible in that case, so the directory won't exist. Logic that
+   * would otherwise only consult the filesystem also needs to use this method.
+   */
+  template <typename P>
+  void CollectPendingOriginsForListing(P aPredicate);
+
   void AssertStorageIsInitialized() const
 #ifdef DEBUG
       ;
 #else
   {
   }
 #endif
 
   nsresult EnsureStorageIsInitialized();
 
   nsresult EnsureOriginIsInitialized(PersistenceType aPersistenceType,
                                      const nsACString& aSuffix,
                                      const nsACString& aGroup,
                                      const nsACString& aOrigin,
-                                     bool aCreateIfNotExists,
                                      nsIFile** aDirectory);
 
-  nsresult EnsureOriginIsInitializedInternal(
-      PersistenceType aPersistenceType, const nsACString& aSuffix,
-      const nsACString& aGroup, const nsACString& aOrigin,
-      bool aCreateIfNotExists, nsIFile** aDirectory, bool* aCreated);
+  nsresult EnsureOriginIsInitializedInternal(PersistenceType aPersistenceType,
+                                             const nsACString& aSuffix,
+                                             const nsACString& aGroup,
+                                             const nsACString& aOrigin,
+                                             nsIFile** aDirectory,
+                                             bool* aCreated);
 
   nsresult EnsureTemporaryStorageIsInitialized();
 
-  nsresult EnsureOriginDirectory(nsIFile* aDirectory, bool aCreateIfNotExists,
-                                 bool* aCreated);
+  nsresult EnsureOriginDirectory(nsIFile* aDirectory, bool* aCreated);
 
   nsresult AboutToClearOrigins(
       const Nullable<PersistenceType>& aPersistenceType,
       const OriginScope& aOriginScope,
       const Nullable<Client::Type>& aClientType);
 
   void OriginClearCompleted(PersistenceType aPersistenceType,
                             const nsACString& aOrigin,
@@ -394,16 +437,19 @@ class QuotaManager final : public Backgr
 
   uint64_t LockedCollectOriginsForEviction(
       uint64_t aMinSizeToBeFreed, nsTArray<RefPtr<DirectoryLockImpl>>& aLocks);
 
   void LockedRemoveQuotaForOrigin(PersistenceType aPersistenceType,
                                   const nsACString& aGroup,
                                   const nsACString& aOrigin);
 
+  already_AddRefed<GroupInfo> LockedGetOrCreateGroupInfo(
+      PersistenceType aPersistenceType, const nsACString& aGroup);
+
   already_AddRefed<OriginInfo> LockedGetOriginInfo(
       PersistenceType aPersistenceType, const nsACString& aGroup,
       const nsACString& aOrigin);
 
   nsresult MaybeUpgradeIndexedDBDirectory();
 
   nsresult MaybeUpgradePersistentStorageDirectory();
 
--- a/dom/simpledb/ActorsParent.cpp
+++ b/dom/simpledb/ActorsParent.cpp
@@ -1147,17 +1147,17 @@ nsresult OpenOp::DatabaseWork() {
   }
 
   QuotaManager* quotaManager = QuotaManager::Get();
   MOZ_ASSERT(quotaManager);
 
   nsCOMPtr<nsIFile> dbDirectory;
   nsresult rv = quotaManager->EnsureOriginIsInitialized(
       PERSISTENCE_TYPE_DEFAULT, mSuffix, mGroup, mOrigin,
-      /* aCreateIfNotExists */ true, getter_AddRefs(dbDirectory));
+      getter_AddRefs(dbDirectory));
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   rv = dbDirectory->Append(NS_LITERAL_STRING(SDB_DIRECTORY_NAME));
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
--- a/gfx/angle/checkout/out/gen/angle/id/commit.h
+++ b/gfx/angle/checkout/out/gen/angle/id/commit.h
@@ -1,3 +1,3 @@
-#define ANGLE_COMMIT_HASH "11fe1e4ce42b"
+#define ANGLE_COMMIT_HASH "5814bb88b10e"
 #define ANGLE_COMMIT_HASH_SIZE 12
-#define ANGLE_COMMIT_DATE "2019-04-12 12:49:22 -0700"
+#define ANGLE_COMMIT_DATE "2019-05-06 18:40:01 -0700"
--- a/gfx/angle/checkout/src/compiler/translator/OutputHLSL.cpp
+++ b/gfx/angle/checkout/src/compiler/translator/OutputHLSL.cpp
@@ -228,32 +228,34 @@ const TConstantUnion *OutputHLSL::writeC
 }
 
 OutputHLSL::OutputHLSL(sh::GLenum shaderType,
                        int shaderVersion,
                        const TExtensionBehavior &extensionBehavior,
                        const char *sourcePath,
                        ShShaderOutput outputType,
                        int numRenderTargets,
+                       int maxDualSourceDrawBuffers,
                        const std::vector<Uniform> &uniforms,
                        ShCompileOptions compileOptions,
                        sh::WorkGroupSize workGroupSize,
                        TSymbolTable *symbolTable,
                        PerformanceDiagnostics *perfDiagnostics,
                        const std::vector<InterfaceBlock> &shaderStorageBlocks)
     : TIntermTraverser(true, true, true, symbolTable),
       mShaderType(shaderType),
       mShaderVersion(shaderVersion),
       mExtensionBehavior(extensionBehavior),
       mSourcePath(sourcePath),
       mOutputType(outputType),
       mCompileOptions(compileOptions),
       mInsideFunction(false),
       mInsideMain(false),
       mNumRenderTargets(numRenderTargets),
+      mMaxDualSourceDrawBuffers(maxDualSourceDrawBuffers),
       mCurrentFunctionMetadata(nullptr),
       mWorkGroupSize(workGroupSize),
       mPerfDiagnostics(perfDiagnostics)
 {
     mUsesFragColor   = false;
     mUsesFragData    = false;
     mUsesDepthRange  = false;
     mUsesFragCoord   = false;
@@ -271,16 +273,17 @@ OutputHLSL::OutputHLSL(sh::GLenum shader
     mUsesLocalInvocationID       = false;
     mUsesGlobalInvocationID      = false;
     mUsesLocalInvocationIndex    = false;
     mUsesXor                     = false;
     mUsesDiscardRewriting        = false;
     mUsesNestedBreak             = false;
     mRequiresIEEEStrictCompiling = false;
     mUseZeroArray                = false;
+    mUsesSecondaryColor          = false;
 
     mUniqueIndex = 0;
 
     mOutputLod0Function      = false;
     mInsideDiscontinuousLoop = false;
     mNestedLoopDepth         = 0;
 
     mExcessiveLoopIndex = nullptr;
@@ -624,16 +627,18 @@ void OutputHLSL::header(TInfoSinkBase &o
     {
         out << DefineZeroArray() << "\n";
     }
 
     if (mShaderType == GL_FRAGMENT_SHADER)
     {
         const bool usingMRTExtension =
             IsExtensionEnabled(mExtensionBehavior, TExtension::EXT_draw_buffers);
+        const bool usingBFEExtension =
+            IsExtensionEnabled(mExtensionBehavior, TExtension::EXT_blend_func_extended);
 
         out << "// Varyings\n";
         writeReferencedVaryings(out);
         out << "\n";
 
         if (mShaderVersion >= 300)
         {
             for (const auto &outputVariable : mReferencedOutputVariables)
@@ -658,16 +663,33 @@ void OutputHLSL::header(TInfoSinkBase &o
                 if (i + 1 != numColorValues)
                 {
                     out << ",";
                 }
                 out << "\n";
             }
 
             out << "};\n";
+
+            if (usingBFEExtension && mUsesSecondaryColor)
+            {
+                out << "static float4 gl_SecondaryColor[" << mMaxDualSourceDrawBuffers
+                    << "] = \n"
+                       "{\n";
+                for (int i = 0; i < mMaxDualSourceDrawBuffers; i++)
+                {
+                    out << "    float4(0, 0, 0, 0)";
+                    if (i + 1 != mMaxDualSourceDrawBuffers)
+                    {
+                        out << ",";
+                    }
+                    out << "\n";
+                }
+                out << "};\n";
+            }
         }
 
         if (mUsesFragDepth)
         {
             out << "static float gl_Depth = 0.0;\n";
         }
 
         if (mUsesFragCoord)
@@ -775,16 +797,21 @@ void OutputHLSL::header(TInfoSinkBase &o
         {
             out << "#define GL_USES_FRAG_COLOR\n";
         }
 
         if (mUsesFragData)
         {
             out << "#define GL_USES_FRAG_DATA\n";
         }
+
+        if (mShaderVersion < 300 && usingBFEExtension && mUsesSecondaryColor)
+        {
+            out << "#define GL_USES_SECONDARY_COLOR\n";
+        }
     }
     else if (mShaderType == GL_VERTEX_SHADER)
     {
         out << "// Attributes\n";
         writeReferencedAttributes(out);
         out << "\n"
                "static float4 gl_Position = float4(0, 0, 0, 0);\n";
 
@@ -1102,16 +1129,26 @@ void OutputHLSL::visitSymbol(TIntermSymb
             out << "gl_Color[0]";
             mUsesFragColor = true;
         }
         else if (qualifier == EvqFragData)
         {
             out << "gl_Color";
             mUsesFragData = true;
         }
+        else if (qualifier == EvqSecondaryFragColorEXT)
+        {
+            out << "gl_SecondaryColor[0]";
+            mUsesSecondaryColor = true;
+        }
+        else if (qualifier == EvqSecondaryFragDataEXT)
+        {
+            out << "gl_SecondaryColor";
+            mUsesSecondaryColor = true;
+        }
         else if (qualifier == EvqFragCoord)
         {
             mUsesFragCoord = true;
             out << name;
         }
         else if (qualifier == EvqPointCoord)
         {
             mUsesPointCoord = true;
--- a/gfx/angle/checkout/src/compiler/translator/OutputHLSL.h
+++ b/gfx/angle/checkout/src/compiler/translator/OutputHLSL.h
@@ -38,16 +38,17 @@ class OutputHLSL : public TIntermTravers
 {
   public:
     OutputHLSL(sh::GLenum shaderType,
                int shaderVersion,
                const TExtensionBehavior &extensionBehavior,
                const char *sourcePath,
                ShShaderOutput outputType,
                int numRenderTargets,
+               int maxDualSourceDrawBuffers,
                const std::vector<Uniform> &uniforms,
                ShCompileOptions compileOptions,
                sh::WorkGroupSize workGroupSize,
                TSymbolTable *symbolTable,
                PerformanceDiagnostics *perfDiagnostics,
                const std::vector<InterfaceBlock> &shaderStorageBlocks);
 
     ~OutputHLSL();
@@ -202,18 +203,20 @@ class OutputHLSL : public TIntermTravers
     bool mUsesLocalInvocationID;
     bool mUsesGlobalInvocationID;
     bool mUsesLocalInvocationIndex;
     bool mUsesXor;
     bool mUsesDiscardRewriting;
     bool mUsesNestedBreak;
     bool mRequiresIEEEStrictCompiling;
     mutable bool mUseZeroArray;
+    bool mUsesSecondaryColor;
 
     int mNumRenderTargets;
+    int mMaxDualSourceDrawBuffers;
 
     int mUniqueIndex;  // For creating unique names
 
     CallDAG mCallDag;
     MetadataList mASTMetadataList;
     ASTMetadataHLSL *mCurrentFunctionMetadata;
     bool mOutputLod0Function;
     bool mInsideDiscontinuousLoop;
--- a/gfx/angle/checkout/src/compiler/translator/TranslatorHLSL.cpp
+++ b/gfx/angle/checkout/src/compiler/translator/TranslatorHLSL.cpp
@@ -37,16 +37,18 @@ TranslatorHLSL::TranslatorHLSL(sh::GLenu
 {}
 
 void TranslatorHLSL::translate(TIntermBlock *root,
                                ShCompileOptions compileOptions,
                                PerformanceDiagnostics *perfDiagnostics)
 {
     const ShBuiltInResources &resources = getResources();
     int numRenderTargets                = resources.EXT_draw_buffers ? resources.MaxDrawBuffers : 1;
+    int maxDualSourceDrawBuffers =
+        resources.EXT_blend_func_extended ? resources.MaxDualSourceDrawBuffers : 0;
 
     sh::AddDefaultReturnStatements(root);
 
     // Note that SimplifyLoopConditions needs to be run before any other AST transformations that
     // may need to generate new statements from loop conditions or loop expressions.
     // Note that SeparateDeclarations has already been run in TCompiler::compileTreeImpl().
     SimplifyLoopConditions(root,
                            IntermNodePatternMatcher::kExpressionReturningArray |
@@ -130,20 +132,20 @@ void TranslatorHLSL::translate(TIntermBl
     if (getShaderVersion() >= 310)
     {
         // Due to ssbo also can be used as the argument of atomic memory functions, we should put
         // RewriteExpressionsWithShaderStorageBlock before RewriteAtomicFunctionExpressions.
         sh::RewriteExpressionsWithShaderStorageBlock(root, &getSymbolTable());
         sh::RewriteAtomicFunctionExpressions(root, &getSymbolTable(), getShaderVersion());
     }
 
-    sh::OutputHLSL outputHLSL(getShaderType(), getShaderVersion(), getExtensionBehavior(),
-                              getSourcePath(), getOutputType(), numRenderTargets, getUniforms(),
-                              compileOptions, getComputeShaderLocalSize(), &getSymbolTable(),
-                              perfDiagnostics, mShaderStorageBlocks);
+    sh::OutputHLSL outputHLSL(
+        getShaderType(), getShaderVersion(), getExtensionBehavior(), getSourcePath(),
+        getOutputType(), numRenderTargets, maxDualSourceDrawBuffers, getUniforms(), compileOptions,
+        getComputeShaderLocalSize(), &getSymbolTable(), perfDiagnostics, mShaderStorageBlocks);
 
     outputHLSL.output(root, getInfoSink().obj);
 
     mShaderStorageBlockRegisterMap = outputHLSL.getShaderStorageBlockRegisterMap();
     mUniformBlockRegisterMap       = outputHLSL.getUniformBlockRegisterMap();
     mUniformRegisterMap            = outputHLSL.getUniformRegisterMap();
     mReadonlyImage2DRegisterIndex  = outputHLSL.getReadonlyImage2DRegisterIndex();
     mImage2DRegisterIndex          = outputHLSL.getImage2DRegisterIndex();
--- a/gfx/angle/checkout/src/libANGLE/MemoryProgramCache.cpp
+++ b/gfx/angle/checkout/src/libANGLE/MemoryProgramCache.cpp
@@ -54,17 +54,17 @@ HashStream &operator<<(HashStream &strea
     }
     return stream;
 }
 
 HashStream &operator<<(HashStream &stream, const ProgramBindings &bindings)
 {
     for (const auto &binding : bindings)
     {
-        stream << binding.first << binding.second;
+        stream << binding.first << binding.second.location;
     }
     return stream;
 }
 
 HashStream &operator<<(HashStream &stream, const std::vector<std::string> &strings)
 {
     for (const auto &str : strings)
     {
--- a/gfx/angle/checkout/src/libANGLE/Program.cpp
+++ b/gfx/angle/checkout/src/libANGLE/Program.cpp
@@ -137,17 +137,21 @@ GLint GetVariableLocation(const std::vec
         const VariableLocation &variableLocation = locationList[location];
         if (!variableLocation.used())
         {
             continue;
         }
 
         const VarT &variable = list[variableLocation.index];
 
-        if (angle::BeginsWith(variable.name, name))
+        // Array output variables may be bound out of order, so we need to ensure we only pick the
+        // first element if given the base name. Uniforms don't allow this behavior and some code
+        // seemingly depends on the opposite behavior, so only enable it for output variables.
+        if (angle::BeginsWith(variable.name, name) &&
+            (!std::is_base_of<sh::OutputVariable, VarT>::value || variableLocation.arrayIndex == 0))
         {
             if (name.length() == variable.name.length())
             {
                 ASSERT(name == variable.name);
                 // GLES 3.1 November 2016 page 87.
                 // The string exactly matches the name of the active variable.
                 return static_cast<GLint>(location);
             }
@@ -857,23 +861,66 @@ SamplerBinding::~SamplerBinding() = defa
 
 // ProgramBindings implementation.
 ProgramBindings::ProgramBindings() {}
 
 ProgramBindings::~ProgramBindings() {}
 
 void ProgramBindings::bindLocation(GLuint index, const std::string &name)
 {
-    mBindings[name] = index;
-}
-
-int ProgramBindings::getBinding(const std::string &name) const
+    mBindings[name] = ProgramBinding(index);
+
+    // EXT_blend_func_extended spec: "If it specifies the base name of an array,
+    // it identifies the resources associated with the first element of the array."
+    //
+    // Normalize array bindings so that "name" and "name[0]" map to the same entry.
+    // If this binding is of the form "name[0]", then mark the "name" binding as
+    // aliased but do not update it yet in case "name" is not actually an array.
+    size_t nameLengthWithoutArrayIndex;
+    unsigned int arrayIndex = ParseArrayIndex(name, &nameLengthWithoutArrayIndex);
+    if (arrayIndex == 0)
+    {
+        std::string baseName = name.substr(0u, nameLengthWithoutArrayIndex);
+        auto iter            = mBindings.find(baseName);
+        if (iter != mBindings.end())
+        {
+            iter->second.aliased = true;
+        }
+    }
+}
+
+int ProgramBindings::getBindingByName(const std::string &name) const
 {
     auto iter = mBindings.find(name);
-    return (iter != mBindings.end()) ? iter->second : -1;
+    return (iter != mBindings.end()) ? iter->second.location : -1;
+}
+
+int ProgramBindings::getBinding(const sh::VariableWithLocation &variable) const
+{
+    const std::string &name = variable.name;
+
+    // Check with the normalized array name if applicable.
+    if (variable.isArray())
+    {
+        size_t nameLengthWithoutArrayIndex;
+        unsigned int arrayIndex = ParseArrayIndex(name, &nameLengthWithoutArrayIndex);
+        if (arrayIndex == 0)
+        {
+            std::string baseName = name.substr(0u, nameLengthWithoutArrayIndex);
+            auto iter            = mBindings.find(baseName);
+            // If "name" exists and is not aliased, that means it was modified more
+            // recently than its "name[0]" form and should be used instead of that.
+            if (iter != mBindings.end() && !iter->second.aliased)
+            {
+                return iter->second.location;
+            }
+        }
+    }
+
+    return getBindingByName(name);
 }
 
 ProgramBindings::const_iterator ProgramBindings::begin() const
 {
     return mBindings.begin();
 }
 
 ProgramBindings::const_iterator ProgramBindings::end() const
@@ -1146,17 +1193,17 @@ BindingInfo Program::getFragmentInputBin
     Shader *fragmentShader = mState.getAttachedShader(ShaderType::Fragment);
     ASSERT(fragmentShader);
 
     // Find the actual fragment shader varying we're interested in
     const std::vector<sh::Varying> &inputs = fragmentShader->getInputVaryings();
 
     for (const auto &binding : mFragmentInputBindings)
     {
-        if (binding.second != static_cast<GLuint>(index))
+        if (binding.second.location != static_cast<GLuint>(index))
             continue;
 
         ret.valid = true;
 
         size_t nameLengthWithoutArrayIndex;
         unsigned int arrayIndex = ParseArrayIndex(binding.first, &nameLengthWithoutArrayIndex);
 
         for (const auto &in : inputs)
@@ -1534,16 +1581,17 @@ void Program::unlink()
     mState.mLinkedTransformFeedbackVaryings.clear();
     mState.mUniforms.clear();
     mState.mUniformLocations.clear();
     mState.mUniformBlocks.clear();
     mState.mActiveUniformBlockBindings.reset();
     mState.mAtomicCounterBuffers.clear();
     mState.mOutputVariables.clear();
     mState.mOutputLocations.clear();
+    mState.mSecondaryOutputLocations.clear();
     mState.mOutputVariableTypes.clear();
     mState.mDrawBufferTypeMask.reset();
     mState.mActiveOutputVariables.reset();
     mState.mComputeShaderLocalSize.fill(1);
     mState.mSamplerBindings.clear();
     mState.mImageBindings.clear();
     mState.mActiveImagesMask.reset();
     mState.mNumViews                          = -1;
@@ -2924,17 +2972,17 @@ bool Program::linkValidateFragmentInputB
         mState.mAttachedShaders[ShaderType::Fragment]->getInputVaryings();
     for (const sh::Varying &input : fragmentInputVaryings)
     {
         if (input.isBuiltIn() || !input.staticUse)
         {
             continue;
         }
 
-        const auto inputBinding = mFragmentInputBindings.getBinding(input.name);
+        const auto inputBinding = mFragmentInputBindings.getBinding(input);
         if (inputBinding == -1)
             continue;
 
         const auto it = staticFragmentInputLocations.find(inputBinding);
         if (it == std::end(staticFragmentInputLocations))
         {
             staticFragmentInputLocations.insert(std::make_pair(inputBinding, input.name));
         }
@@ -3115,17 +3163,17 @@ bool Program::linkAttributes(const Caps 
     // Assign locations to attributes that have a binding location and check for attribute aliasing.
     for (sh::Attribute &attribute : mState.mAttributes)
     {
         // GLSL ES 3.10 January 2016 section 4.3.4: Vertex shader inputs can't be arrays or
         // structures, so we don't need to worry about adjusting their names or generating entries
         // for each member/element (unlike uniforms for example).
         ASSERT(!attribute.isArray() && !attribute.isStruct());
 
-        int bindingLocation = mAttributeBindings.getBinding(attribute.name);
+        int bindingLocation = mAttributeBindings.getBinding(attribute);
         if (attribute.location == -1 && bindingLocation != -1)
         {
             attribute.location = bindingLocation;
         }
 
         if (attribute.location != -1)
         {
             // Location is set by glBindAttribLocation or by location layout qualifier
@@ -3703,43 +3751,97 @@ bool CompareOutputVariable(const sh::Out
 }
 
 int Program::getOutputLocationForLink(const sh::OutputVariable &outputVariable) const
 {
     if (outputVariable.location != -1)
     {
         return outputVariable.location;
     }
-    int apiLocation = mFragmentOutputLocations.getBinding(outputVariable.name);
+    int apiLocation = mFragmentOutputLocations.getBinding(outputVariable);
     if (apiLocation != -1)
     {
         return apiLocation;
     }
     return -1;
 }
 
 bool Program::isOutputSecondaryForLink(const sh::OutputVariable &outputVariable) const
 {
     if (outputVariable.index != -1)
     {
         ASSERT(outputVariable.index == 0 || outputVariable.index == 1);
         return (outputVariable.index == 1);
     }
-    int apiIndex = mFragmentOutputIndexes.getBinding(outputVariable.name);
+    int apiIndex = mFragmentOutputIndexes.getBinding(outputVariable);
     if (apiIndex != -1)
     {
         // Index layout qualifier from the shader takes precedence, so the index from the API is
         // checked only if the index was not set in the shader. This is not specified in the EXT
         // spec, but is specified in desktop OpenGL specs.
         return (apiIndex == 1);
     }
     // EXT_blend_func_extended: Outputs get index 0 by default.
     return false;
 }
 
+namespace
+{
+
+bool FindUsedOutputLocation(std::vector<VariableLocation> &outputLocations,
+                            unsigned int baseLocation,
+                            unsigned int elementCount,
+                            const std::vector<VariableLocation> &reservedLocations,
+                            unsigned int variableIndex)
+{
+    if (baseLocation + elementCount > outputLocations.size())
+    {
+        elementCount =
+            baseLocation < outputLocations.size() ? outputLocations.size() - baseLocation : 0;
+    }
+    for (unsigned int elementIndex = 0; elementIndex < elementCount; elementIndex++)
+    {
+        const unsigned int location = baseLocation + elementIndex;
+        if (outputLocations[location].used())
+        {
+            VariableLocation locationInfo(elementIndex, variableIndex);
+            if (std::find(reservedLocations.begin(), reservedLocations.end(), locationInfo) ==
+                reservedLocations.end())
+            {
+                return true;
+            }
+        }
+    }
+    return false;
+}
+
+void AssignOutputLocations(std::vector<VariableLocation> &outputLocations,
+                           unsigned int baseLocation,
+                           unsigned int elementCount,
+                           const std::vector<VariableLocation> &reservedLocations,
+                           unsigned int variableIndex)
+{
+    if (baseLocation + elementCount > outputLocations.size())
+    {
+        outputLocations.resize(baseLocation + elementCount);
+    }
+    for (unsigned int elementIndex = 0; elementIndex < elementCount; elementIndex++)
+    {
+        VariableLocation locationInfo(elementIndex, variableIndex);
+        if (std::find(reservedLocations.begin(), reservedLocations.end(), locationInfo) ==
+            reservedLocations.end())
+        {
+            const unsigned int location = baseLocation + elementIndex;
+            outputLocations[location]   = locationInfo;
+        }
+    }
+}
+
+}  // anonymous namespace
+
 bool Program::linkOutputVariables(const Caps &caps,
                                   const Extensions &extensions,
                                   const Version &version,
                                   GLuint combinedImageUniformsCount,
                                   GLuint combinedShaderStorageBlocksCount)
 {
     Shader *fragmentShader = mState.mAttachedShaders[ShaderType::Fragment];
     ASSERT(fragmentShader != nullptr);
@@ -3814,166 +3916,184 @@ bool Program::linkOutputVariables(const 
         {
             // We're following the GLES 3.1 November 2016 spec section 7.3.1.1 Naming Active
             // Resources and including [0] at the end of array variable names.
             outputVariable.name += "[0]";
             outputVariable.mappedName += "[0]";
         }
     }
 
-    bool hasSecondaryOutputs = false;
+    // EXT_blend_func_extended doesn't specify anything related to binding specific elements of an
+    // output array in explicit terms.
+    //
+    // Assuming fragData is an output array, you can defend the position that:
+    // P1) you must support binding "fragData" because it's specified
+    // P2) you must support querying "fragData[x]" because it's specified
+    // P3) you must support binding "fragData[0]" because it's a frequently used pattern
+    //
+    // Then you can make the leap of faith:
+    // P4) you must support binding "fragData[x]" because you support "fragData[0]"
+    // P5) you must support binding "fragData[x]" because you support querying "fragData[x]"
+    //
+    // The spec brings in the "world of arrays" when it mentions binding the arrays and the
+    // automatic binding. Thus it must be interpreted that the thing is not undefined, rather you
+    // must infer the only possible interpretation (?). Note again: this need of interpretation
+    // might be completely off of what GL spec logic is.
+    //
+    // The other complexity is that unless you implement this feature, it's hard to understand what
+    // should happen when the client invokes the feature. You cannot add an additional error as it
+    // is not specified. One can ignore it, but obviously it creates the discrepancies...
+
+    std::vector<VariableLocation> reservedLocations;
+
+    // Process any output API bindings for arrays that don't alias to the first element.
+    for (const auto &binding : mFragmentOutputLocations)
+    {
+        size_t nameLengthWithoutArrayIndex;
+        unsigned int arrayIndex = ParseArrayIndex(binding.first, &nameLengthWithoutArrayIndex);
+        if (arrayIndex == 0 || arrayIndex == GL_INVALID_INDEX)
+        {
+            continue;
+        }
+        for (unsigned int outputVariableIndex = 0;
+             outputVariableIndex < mState.mOutputVariables.size(); outputVariableIndex++)
+        {
+            const sh::OutputVariable &outputVariable = mState.mOutputVariables[outputVariableIndex];
+            // Check that the binding corresponds to an output array and its array index fits.
+            if (outputVariable.isBuiltIn() || !outputVariable.isArray() ||
+                !angle::BeginsWith(outputVariable.name, binding.first,
+                                   nameLengthWithoutArrayIndex) ||
+                arrayIndex >= outputVariable.getOutermostArraySize())
+            {
+                continue;
+            }
+
+            // Get the API index that corresponds to this exact binding.
+            // This index may differ from the index used for the array's base.
+            auto &outputLocations = mFragmentOutputIndexes.getBindingByName(binding.first) == 1
+                                        ? mState.mSecondaryOutputLocations
+                                        : mState.mOutputLocations;
+            unsigned int location = binding.second.location;
+            VariableLocation locationInfo(arrayIndex, outputVariableIndex);
+            if (location >= outputLocations.size())
+            {
+                outputLocations.resize(location + 1);
+            }
+            if (outputLocations[location].used())
+            {
+                mInfoLog << "Location of variable " << outputVariable.name
+                         << " conflicts with another variable.";
+                return false;
+            }
+            outputLocations[location] = locationInfo;
+
+            // Note the array binding location so that it can be skipped later.
+            reservedLocations.push_back(locationInfo);
+        }
+    }
 
     // Reserve locations for output variables whose location is fixed in the shader or through the
-    // API.
+    // API. Otherwise, the remaining unallocated outputs will be processed later.
     for (unsigned int outputVariableIndex = 0; outputVariableIndex < mState.mOutputVariables.size();
          outputVariableIndex++)
     {
         const sh::OutputVariable &outputVariable = mState.mOutputVariables[outputVariableIndex];
 
         // Don't store outputs for gl_FragDepth, gl_FragColor, etc.
         if (outputVariable.isBuiltIn())
             continue;
 
-        int baseLocation = getOutputLocationForLink(outputVariable);
-        if (baseLocation == -1)
+        int fixedLocation = getOutputLocationForLink(outputVariable);
+        if (fixedLocation == -1)
         {
             // Here we're only reserving locations for variables whose location is fixed.
             continue;
         }
-
-        auto *outputLocations = &mState.mOutputLocations;
-        if (isOutputSecondaryForLink(outputVariable))
-        {
-            outputLocations = &mState.mSecondaryOutputLocations;
-            // Note that this check doesn't need to be before checking baseLocation == -1 above. If
-            // an output has an index specified it will always also have the location specified.
-            hasSecondaryOutputs = true;
-        }
+        unsigned int baseLocation = static_cast<unsigned int>(fixedLocation);
+
+        auto &outputLocations = isOutputSecondaryForLink(outputVariable)
+                                    ? mState.mSecondaryOutputLocations
+                                    : mState.mOutputLocations;
 
         // GLSL ES 3.10 section 4.3.6: Output variables cannot be arrays of arrays or arrays of
         // structures, so we may use getBasicTypeElementCount().
         unsigned int elementCount          = outputVariable.getBasicTypeElementCount();
-        unsigned int outputLocationsNeeded = static_cast<unsigned int>(baseLocation) + elementCount;
-        if (outputLocationsNeeded > outputLocations->size())
-        {
-            outputLocations->resize(outputLocationsNeeded);
-        }
-        for (unsigned int elementIndex = 0; elementIndex < elementCount; elementIndex++)
+        if (FindUsedOutputLocation(outputLocations, baseLocation, elementCount, reservedLocations,
+                                   outputVariableIndex))
         {
-            const unsigned int location = static_cast<unsigned int>(baseLocation) + elementIndex;
-            ASSERT(location < outputLocations->size());
-            if (outputLocations->at(location).used())
-            {
-                mInfoLog << "Location of variable " << outputVariable.name
-                         << " conflicts with another variable.";
-                return false;
-            }
-            if (outputVariable.isArray())
-            {
-                (*outputLocations)[location] = VariableLocation(elementIndex, outputVariableIndex);
-            }
-            else
-            {
-                VariableLocation locationInfo;
-                locationInfo.index           = outputVariableIndex;
-                (*outputLocations)[location] = locationInfo;
-            }
+            mInfoLog << "Location of variable " << outputVariable.name
+                     << " conflicts with another variable.";
+            return false;
         }
+        AssignOutputLocations(outputLocations, baseLocation, elementCount, reservedLocations,
+                              outputVariableIndex);
     }
 
     // Here we assign locations for the output variables that don't yet have them. Note that we're
     // not necessarily able to fit the variables optimally, since then we might have to try
     // different arrangements of output arrays. Now we just assign the locations in the order that
     // we got the output variables. The spec isn't clear on what kind of algorithm is required for
     // finding locations for the output variables, so this should be acceptable at least for now.
     GLuint maxLocation = caps.maxDrawBuffers;
-    if (hasSecondaryOutputs)
+    if (!mState.mSecondaryOutputLocations.empty())
     {
         // EXT_blend_func_extended: Program outputs will be validated against
         // MAX_DUAL_SOURCE_DRAW_BUFFERS_EXT if there's even one output with index one.
         maxLocation = extensions.maxDualSourceDrawBuffers;
     }
 
     for (unsigned int outputVariableIndex = 0; outputVariableIndex < mState.mOutputVariables.size();
          outputVariableIndex++)
     {
         const sh::OutputVariable &outputVariable = mState.mOutputVariables[outputVariableIndex];
 
         // Don't store outputs for gl_FragDepth, gl_FragColor, etc.
         if (outputVariable.isBuiltIn())
             continue;
 
-        if (getOutputLocationForLink(outputVariable) != -1)
-        {
-            continue;
-        }
-
-        auto *outputLocations = &mState.mOutputLocations;
-        if (isOutputSecondaryForLink(outputVariable))
+        int fixedLocation     = getOutputLocationForLink(outputVariable);
+        auto &outputLocations = isOutputSecondaryForLink(outputVariable)
+                                    ? mState.mSecondaryOutputLocations
+                                    : mState.mOutputLocations;
+        unsigned int baseLocation = 0;
+        unsigned int elementCount = outputVariable.getBasicTypeElementCount();
+        if (fixedLocation != -1)
         {
-            outputLocations = &mState.mSecondaryOutputLocations;
+            // Secondary inputs might have caused the max location to drop below what has already
+            // been explicitly assigned locations. Check for any fixed locations above the max
+            // that should cause linking to fail.
+            baseLocation = static_cast<unsigned int>(fixedLocation);
         }
-
-        int baseLocation          = 0;
-        unsigned int elementCount = outputVariable.getBasicTypeElementCount();
-        bool elementsFit          = false;
-        while (!elementsFit)
+        else
         {
+            // No fixed location, so try to fit the output in unassigned locations.
             // Try baseLocations starting from 0 one at a time and see if the variable fits.
-            elementsFit = true;
-            if (baseLocation + elementCount > maxLocation)
+            while (FindUsedOutputLocation(outputLocations, baseLocation, elementCount,
+                                          reservedLocations, outputVariableIndex))
             {
-                // EXT_blend_func_extended: Linking can fail:
-                // "if the explicit binding assignments do not leave enough space for the linker to
-                // automatically assign a location for a varying out array, which requires multiple
-                // contiguous locations."
-                mInfoLog << "Could not fit output variable into available locations: "
-                         << outputVariable.name;
-                return false;
-            }
-            unsigned int outputLocationsNeeded =
-                static_cast<unsigned int>(baseLocation) + elementCount;
-            if (outputLocationsNeeded > outputLocations->size())
-            {
-                outputLocations->resize(outputLocationsNeeded);
+                baseLocation++;
             }
-            for (unsigned int elementIndex = 0; elementIndex < elementCount; elementIndex++)
-            {
-                const unsigned int location =
-                    static_cast<unsigned int>(baseLocation) + elementIndex;
-                ASSERT(location < outputLocations->size());
-                if (outputLocations->at(location).used())
-                {
-                    elementsFit = false;
-                    break;
-                }
-            }
-            if (elementsFit)
-            {
-                for (unsigned int elementIndex = 0; elementIndex < elementCount; elementIndex++)
-                {
-                    const unsigned int location =
-                        static_cast<unsigned int>(baseLocation) + elementIndex;
-                    if (outputVariable.isArray())
-                    {
-                        (*outputLocations)[location] =
-                            VariableLocation(elementIndex, outputVariableIndex);
-                    }
-                    else
-                    {
-                        VariableLocation locationInfo;
-                        locationInfo.index           = outputVariableIndex;
-                        (*outputLocations)[location] = locationInfo;
-                    }
-                }
-            }
-            else
-            {
-                ++baseLocation;
-            }
+            AssignOutputLocations(outputLocations, baseLocation, elementCount, reservedLocations,
+                                  outputVariableIndex);
+        }
+
+        // Check for any elements assigned above the max location that are actually used.
+        if (baseLocation + elementCount > maxLocation &&
+            (baseLocation >= maxLocation ||
+             FindUsedOutputLocation(outputLocations, maxLocation,
+                                    baseLocation + elementCount - maxLocation, reservedLocations,
+                                    outputVariableIndex)))
+        {
+            // EXT_blend_func_extended: Linking can fail:
+            // "if the explicit binding assignments do not leave enough space for the linker to
+            // automatically assign a location for a varying out array, which requires multiple
+            // contiguous locations."
+            mInfoLog << "Could not fit output variable into available locations: "
+                     << outputVariable.name;
+            return false;
         }
     }
 
     return true;
 }
 
 void Program::setUniformValuesFromBindingQualifiers()
 {
--- a/gfx/angle/checkout/src/libANGLE/Program.h
+++ b/gfx/angle/checkout/src/libANGLE/Program.h
@@ -174,16 +174,21 @@ struct VariableLocation
 
     // If used is false, it means this location is only used to fill an empty space in an array,
     // and there is no corresponding uniform variable for this location. It can also mean the
     // uniform was optimized out by the implementation.
     bool used() const { return (index != kUnused); }
     void markUnused() { index = kUnused; }
     void markIgnored() { ignored = true; }
 
+    bool operator==(const VariableLocation &other) const
+    {
+        return arrayIndex == other.arrayIndex && index == other.index;
+    }
+
     // "arrayIndex" stores the index of the innermost GLSL array. It's zero for non-arrays.
     unsigned int arrayIndex;
     // "index" is an index of the variable. The variable contains the indices for other than the
     // innermost GLSL arrays.
     unsigned int index;
 
     // If this location was bound to an unreferenced uniform.  Setting data on this uniform is a
     // no-op.
@@ -468,31 +473,42 @@ class ProgramState final : angle::NonCop
     ActiveTextureArray<uint32_t> mActiveSamplerRefCounts;
     ActiveTextureArray<TextureType> mActiveSamplerTypes;
     ActiveTextureArray<SamplerFormat> mActiveSamplerFormats;
 
     // Cached mask of active images.
     ActiveTextureMask mActiveImagesMask;
 };
 
+struct ProgramBinding
+{
+    ProgramBinding() : location(GL_INVALID_INDEX), aliased(false) {}
+    ProgramBinding(GLuint index) : location(index), aliased(false) {}
+
+    GLuint location;
+    // Whether another binding was set that may potentially alias this.
+    bool aliased;
+};
+
 class ProgramBindings final : angle::NonCopyable
 {
   public:
     ProgramBindings();
     ~ProgramBindings();
 
     void bindLocation(GLuint index, const std::string &name);
-    int getBinding(const std::string &name) const;
+    int getBindingByName(const std::string &name) const;
+    int getBinding(const sh::VariableWithLocation &variable) const;
 
-    using const_iterator = std::unordered_map<std::string, GLuint>::const_iterator;
+    using const_iterator = std::unordered_map<std::string, ProgramBinding>::const_iterator;
     const_iterator begin() const;
     const_iterator end() const;
 
   private:
-    std::unordered_map<std::string, GLuint> mBindings;
+    std::unordered_map<std::string, ProgramBinding> mBindings;
 };
 
 struct ProgramVaryingRef
 {
     const sh::Varying *get() const { return vertex ? vertex : fragment; }
 
     const sh::Varying *vertex   = nullptr;
     const sh::Varying *fragment = nullptr;
--- a/gfx/angle/checkout/src/libANGLE/ProgramLinkedResources.cpp
+++ b/gfx/angle/checkout/src/libANGLE/ProgramLinkedResources.cpp
@@ -27,30 +27,16 @@ LinkedUniform *FindUniform(std::vector<L
     {
         if (uniform.name == name)
             return &uniform;
     }
 
     return nullptr;
 }
 
-int GetUniformLocationBinding(const ProgramBindings &uniformLocationBindings,
-                              const sh::Uniform &uniform)
-{
-    int binding = uniformLocationBindings.getBinding(uniform.name);
-    if (uniform.isArray() && binding == -1)
-    {
-        // Bindings for array uniforms can be set either with or without [0] in the end.
-        ASSERT(angle::EndsWith(uniform.name, "[0]"));
-        std::string nameWithoutIndex = uniform.name.substr(0u, uniform.name.length() - 3u);
-        return uniformLocationBindings.getBinding(nameWithoutIndex);
-    }
-    return binding;
-}
-
 template <typename VarT>
 void SetActive(std::vector<VarT> *list, const std::string &name, ShaderType shaderType, bool active)
 {
     for (auto &variable : *list)
     {
         if (variable.name == name)
         {
             variable.setActive(shaderType, active);
@@ -659,17 +645,17 @@ bool UniformLinker::indexUniforms(InfoLo
         const LinkedUniform &uniform = mUniforms[uniformIndex];
 
         if ((uniform.isBuiltIn() && !uniform.isEmulatedBuiltIn()) ||
             IsAtomicCounterType(uniform.type))
         {
             continue;
         }
 
-        int preSetLocation = GetUniformLocationBinding(uniformLocationBindings, uniform);
+        int preSetLocation = uniformLocationBindings.getBinding(uniform);
         int shaderLocation = uniform.location;
 
         if (shaderLocation != -1)
         {
             preSetLocation = shaderLocation;
         }
 
         unsigned int elementCount = uniform.getBasicTypeElementCount();
@@ -735,17 +721,17 @@ bool UniformLinker::gatherUniformLocatio
 
     for (const LinkedUniform &uniform : mUniforms)
     {
         if (uniform.isBuiltIn() && !uniform.isEmulatedBuiltIn())
         {
             continue;
         }
 
-        int apiBoundLocation = GetUniformLocationBinding(uniformLocationBindings, uniform);
+        int apiBoundLocation = uniformLocationBindings.getBinding(uniform);
         int shaderLocation   = uniform.location;
 
         if (shaderLocation != -1)
         {
             unsigned int elementCount = uniform.getBasicTypeElementCount();
 
             for (unsigned int arrayIndex = 0; arrayIndex < elementCount; arrayIndex++)
             {
@@ -780,17 +766,17 @@ bool UniformLinker::gatherUniformLocatio
             }
         }
     }
 
     // Record the uniform locations that were bound using the API for uniforms that were not found
     // from the shader. Other uniforms should not be assigned to those locations.
     for (const auto &locationBinding : uniformLocationBindings)
     {
-        GLuint location = locationBinding.second;
+        GLuint location = locationBinding.second.location;
         if (reservedLocations.find(location) == reservedLocations.end())
         {
             ignoredLocations->insert(location);
             *maxUniformLocation = std::max(*maxUniformLocation, static_cast<int>(location));
         }
     }
 
     return true;
--- a/gfx/angle/checkout/src/libANGLE/renderer/d3d/DynamicHLSL.cpp
+++ b/gfx/angle/checkout/src/libANGLE/renderer/d3d/DynamicHLSL.cpp
@@ -92,23 +92,24 @@ void HLSLTypeString(std::ostringstream &
     }
 
     HLSLComponentTypeString(ostream, gl::VariableComponentType(type),
                             gl::VariableComponentCount(type));
 }
 
 const PixelShaderOutputVariable *FindOutputAtLocation(
     const std::vector<PixelShaderOutputVariable> &outputVariables,
-    unsigned int location)
+    unsigned int location,
+    size_t index = 0)
 {
-    for (size_t variableIndex = 0; variableIndex < outputVariables.size(); ++variableIndex)
+    for (auto &outputVar : outputVariables)
     {
-        if (outputVariables[variableIndex].outputIndex == location)
+        if (outputVar.outputLocation == location && outputVar.outputIndex == index)
         {
-            return &outputVariables[variableIndex];
+            return &outputVar;
         }
     }
 
     return nullptr;
 }
 
 void WriteArrayString(std::ostringstream &strstr, unsigned int i)
 {
@@ -283,34 +284,37 @@ std::string DynamicHLSL::generatePixelSh
 
     size_t numOutputs = outputLayout.size();
 
     // Workaround for HLSL 3.x: We can't do a depth/stencil only render, the runtime will complain.
     if (numOutputs == 0 && (shaderModel == 3 || !mRenderer->getShaderModelSuffix().empty()))
     {
         numOutputs = 1u;
     }
-    const PixelShaderOutputVariable defaultOutput(GL_FLOAT_VEC4, "dummy", "float4(0, 0, 0, 1)", 0);
+    const PixelShaderOutputVariable defaultOutput(GL_FLOAT_VEC4, "dummy", "float4(0, 0, 0, 1)", 0,
+                                                  0);
+    size_t outputIndex = 0;
 
     for (size_t layoutIndex = 0; layoutIndex < numOutputs; ++layoutIndex)
     {
         GLenum binding = outputLayout.empty() ? GL_COLOR_ATTACHMENT0 : outputLayout[layoutIndex];
 
         if (binding != GL_NONE)
         {
             unsigned int location = (binding - GL_COLOR_ATTACHMENT0);
+            outputIndex =
+                layoutIndex > 0 && binding == outputLayout[layoutIndex - 1] ? outputIndex + 1 : 0;
 
             const PixelShaderOutputVariable *outputVariable =
                 outputLayout.empty() ? &defaultOutput
-                                     : FindOutputAtLocation(outputVariables, location);
+                                     : FindOutputAtLocation(outputVariables, location, outputIndex);
 
             // OpenGL ES 3.0 spec $4.2.1
             // If [...] not all user-defined output variables are written, the values of fragment
-            // colors
-            // corresponding to unwritten variables are similarly undefined.
+            // colors corresponding to unwritten variables are similarly undefined.
             if (outputVariable)
             {
                 declarationStream << "    ";
                 HLSLTypeString(declarationStream, outputVariable->type);
                 declarationStream << " " << outputVariable->name << " : " << targetSemantic
                                   << static_cast<int>(layoutIndex) << ";\n";
 
                 copyStream << "    output." << outputVariable->name << " = "
@@ -1193,20 +1197,36 @@ void DynamicHLSL::getPixelShaderOutputKe
         for (unsigned int renderTargetIndex = 0; renderTargetIndex < numRenderTargets;
              renderTargetIndex++)
         {
             PixelShaderOutputVariable outputKeyVariable;
             outputKeyVariable.type = GL_FLOAT_VEC4;
             outputKeyVariable.name = "gl_Color" + Str(renderTargetIndex);
             outputKeyVariable.source =
                 broadcast ? "gl_Color[0]" : "gl_Color[" + Str(renderTargetIndex) + "]";
-            outputKeyVariable.outputIndex = renderTargetIndex;
+            outputKeyVariable.outputLocation = renderTargetIndex;
 
             outPixelShaderKey->push_back(outputKeyVariable);
         }
+
+        if (metadata.usesSecondaryColor())
+        {
+            for (unsigned int secondaryIndex = 0;
+                 secondaryIndex < data.getExtensions().maxDualSourceDrawBuffers; secondaryIndex++)
+            {
+                PixelShaderOutputVariable outputKeyVariable;
+                outputKeyVariable.type           = GL_FLOAT_VEC4;
+                outputKeyVariable.name           = "gl_SecondaryColor" + Str(secondaryIndex);
+                outputKeyVariable.source         = "gl_SecondaryColor[" + Str(secondaryIndex) + "]";
+                outputKeyVariable.outputLocation = secondaryIndex;
+                outputKeyVariable.outputIndex    = 1;
+
+                outPixelShaderKey->push_back(outputKeyVariable);
+            }
+        }
     }
     else
     {
         const auto &shaderOutputVars =
             metadata.getFragmentShader()->getData().getActiveOutputVariables();
 
         for (size_t outputLocationIndex = 0u;
              outputLocationIndex < programData.getOutputLocations().size(); ++outputLocationIndex)
@@ -1227,17 +1247,49 @@ void DynamicHLSL::getPixelShaderOutputKe
             ASSERT(outputVariable.active);
 
             PixelShaderOutputVariable outputKeyVariable;
             outputKeyVariable.type = outputVariable.type;
             outputKeyVariable.name = variableName + elementString;
             outputKeyVariable.source =
                 variableName +
                 (outputVariable.isArray() ? ArrayString(outputLocation.arrayIndex) : "");
-            outputKeyVariable.outputIndex = outputLocationIndex;
+            outputKeyVariable.outputLocation = outputLocationIndex;
+
+            outPixelShaderKey->push_back(outputKeyVariable);
+        }
+
+        // Now generate any secondary outputs...
+        for (size_t outputLocationIndex = 0u;
+             outputLocationIndex < programData.getSecondaryOutputLocations().size();
+             ++outputLocationIndex)
+        {
+            const VariableLocation &outputLocation =
+                programData.getSecondaryOutputLocations().at(outputLocationIndex);
+            if (!outputLocation.used())
+            {
+                continue;
+            }
+            const sh::ShaderVariable &outputVariable = shaderOutputVars[outputLocation.index];
+            const std::string &variableName          = "out_" + outputVariable.name;
+
+            // Fragment outputs can't be arrays of arrays. ESSL 3.10 section 4.3.6.
+            const std::string &elementString =
+                (outputVariable.isArray() ? Str(outputLocation.arrayIndex) : "");
+
+            ASSERT(outputVariable.active);
+
+            PixelShaderOutputVariable outputKeyVariable;
+            outputKeyVariable.type = outputVariable.type;
+            outputKeyVariable.name = variableName + elementString;
+            outputKeyVariable.source =
+                variableName +
+                (outputVariable.isArray() ? ArrayString(outputLocation.arrayIndex) : "");
+            outputKeyVariable.outputLocation = outputLocationIndex;
+            outputKeyVariable.outputIndex    = 1;
 
             outPixelShaderKey->push_back(outputKeyVariable);
         }
     }
 }
 
 // BuiltinVarying Implementation.
 BuiltinVarying::BuiltinVarying() : enabled(false), index(0), systemValue(false) {}
--- a/gfx/angle/checkout/src/libANGLE/renderer/d3d/DynamicHLSL.h
+++ b/gfx/angle/checkout/src/libANGLE/renderer/d3d/DynamicHLSL.h
@@ -62,23 +62,29 @@ inline std::ostream &operator<<(std::ost
 }
 
 struct PixelShaderOutputVariable
 {
     PixelShaderOutputVariable() {}
     PixelShaderOutputVariable(GLenum typeIn,
                               const std::string &nameIn,
                               const std::string &sourceIn,
+                              size_t outputLocationIn,
                               size_t outputIndexIn)
-        : type(typeIn), name(nameIn), source(sourceIn), outputIndex(outputIndexIn)
+        : type(typeIn),
+          name(nameIn),
+          source(sourceIn),
+          outputLocation(outputLocationIn),
+          outputIndex(outputIndexIn)
     {}
 
     GLenum type = GL_NONE;
     std::string name;
     std::string source;
+    size_t outputLocation = 0;
     size_t outputIndex = 0;
 };
 
 struct BuiltinVarying final : private angle::NonCopyable
 {
     BuiltinVarying();
 
     std::string str() const;
--- a/gfx/angle/checkout/src/libANGLE/renderer/d3d/ProgramD3D.cpp
+++ b/gfx/angle/checkout/src/libANGLE/renderer/d3d/ProgramD3D.cpp
@@ -62,26 +62,42 @@ void GetDefaultInputLayoutFromShader(gl:
                     gl::GetVertexFormatID(attribType, GL_FALSE, components, pureInt);
 
                 inputLayoutOut->push_back(defaultID);
             }
         }
     }
 }
 
+size_t GetMaxOutputIndex(const std::vector<PixelShaderOutputVariable> &shaderOutputVars,
+                         size_t location)
+{
+    size_t maxIndex = 0;
+    for (auto &outputVar : shaderOutputVars)
+    {
+        if (outputVar.outputLocation == location)
+        {
+            maxIndex = std::max(maxIndex, outputVar.outputIndex);
+        }
+    }
+    return maxIndex;
+}
+
 void GetDefaultOutputLayoutFromShader(
     const std::vector<PixelShaderOutputVariable> &shaderOutputVars,
     std::vector<GLenum> *outputLayoutOut)
 {
     outputLayoutOut->clear();
 
     if (!shaderOutputVars.empty())
     {
-        outputLayoutOut->push_back(GL_COLOR_ATTACHMENT0 +
-                                   static_cast<unsigned int>(shaderOutputVars[0].outputIndex));
+        size_t location = shaderOutputVars[0].outputLocation;
+        size_t maxIndex = GetMaxOutputIndex(shaderOutputVars, location);
+        outputLayoutOut->assign(maxIndex + 1,
+                                GL_COLOR_ATTACHMENT0 + static_cast<unsigned int>(location));
     }
 }
 
 void GetDefaultImage2DBindLayoutFromComputeShader(const std::vector<sh::Uniform> &image2DUniforms,
                                                   gl::ImageUnitTextureTypeMap *image2DBindLayout)
 {
     image2DBindLayout->clear();
 
@@ -472,16 +488,21 @@ int ProgramD3DMetadata::getRendererMajor
 
 bool ProgramD3DMetadata::usesBroadcast(const gl::State &data) const
 {
     return (mAttachedShaders[gl::ShaderType::Fragment]->usesFragColor() &&
             mAttachedShaders[gl::ShaderType::Fragment]->usesMultipleRenderTargets() &&
             data.getClientMajorVersion() < 3);
 }
 
+bool ProgramD3DMetadata::usesSecondaryColor() const
+{
+    return mAttachedShaders[gl::ShaderType::Fragment]->usesSecondaryColor();
+}
+
 bool ProgramD3DMetadata::usesFragDepth() const
 {
     return mAttachedShaders[gl::ShaderType::Fragment]->usesFragDepth();
 }
 
 bool ProgramD3DMetadata::usesPointCoord() const
 {
     return mAttachedShaders[gl::ShaderType::Fragment]->usesPointCoord();
@@ -1155,16 +1176,17 @@ std::unique_ptr<rx::LinkEvent> ProgramD3
     const size_t pixelShaderKeySize = stream->readInt<unsigned int>();
     mPixelShaderKey.resize(pixelShaderKeySize);
     for (size_t pixelShaderKeyIndex = 0; pixelShaderKeyIndex < pixelShaderKeySize;
          pixelShaderKeyIndex++)
     {
         stream->readInt(&mPixelShaderKey[pixelShaderKeyIndex].type);
         stream->readString(&mPixelShaderKey[pixelShaderKeyIndex].name);
         stream->readString(&mPixelShaderKey[pixelShaderKeyIndex].source);
+        stream->readInt(&mPixelShaderKey[pixelShaderKeyIndex].outputLocation);
         stream->readInt(&mPixelShaderKey[pixelShaderKeyIndex].outputIndex);
     }
 
     stream->readString(&mGeometryShaderPreamble);
 
     return std::make_unique<LoadBinaryLinkEvent>(context->getWorkerThreadPool(), context, this,
                                                  stream, infoLog);
 }
@@ -1437,16 +1459,17 @@ void ProgramD3D::save(const gl::Context 
     stream->writeInt(pixelShaderKey.size());
     for (size_t pixelShaderKeyIndex = 0; pixelShaderKeyIndex < pixelShaderKey.size();
          pixelShaderKeyIndex++)
     {
         const PixelShaderOutputVariable &variable = pixelShaderKey[pixelShaderKeyIndex];
         stream->writeInt(variable.type);
         stream->writeString(variable.name);
         stream->writeString(variable.source);
+        stream->writeInt(variable.outputLocation);
         stream->writeInt(variable.outputIndex);
     }
 
     stream->writeString(mGeometryShaderPreamble);
 
     stream->writeInt(mVertexExecutables.size());
     for (size_t vertexExecutableIndex = 0; vertexExecutableIndex < mVertexExecutables.size();
          vertexExecutableIndex++)
@@ -2977,17 +3000,21 @@ void ProgramD3D::updateCachedOutputLayou
     for (size_t colorAttachment = 0; colorAttachment < colorbuffers.size(); ++colorAttachment)
     {
         const gl::FramebufferAttachment *colorbuffer = colorbuffers[colorAttachment];
 
         if (colorbuffer)
         {
             auto binding = colorbuffer->getBinding() == GL_BACK ? GL_COLOR_ATTACHMENT0
                                                                 : colorbuffer->getBinding();
-            mPixelShaderOutputLayoutCache.push_back(binding);
+            size_t maxIndex = binding != GL_NONE ? GetMaxOutputIndex(mPixelShaderKey,
+                                                                     binding - GL_COLOR_ATTACHMENT0)
+                                                 : 0;
+            mPixelShaderOutputLayoutCache.insert(mPixelShaderOutputLayoutCache.end(), maxIndex + 1,
+                                                 binding);
         }
         else
         {
             mPixelShaderOutputLayoutCache.push_back(GL_NONE);
         }
     }
 
     updateCachedPixelExecutableIndex();
--- a/gfx/angle/checkout/src/libANGLE/renderer/d3d/ProgramD3D.h
+++ b/gfx/angle/checkout/src/libANGLE/renderer/d3d/ProgramD3D.h
@@ -117,16 +117,17 @@ class ProgramD3DMetadata final : angle::
 {
   public:
     ProgramD3DMetadata(RendererD3D *renderer,
                        const gl::ShaderMap<const ShaderD3D *> &attachedShaders);
     ~ProgramD3DMetadata();
 
     int getRendererMajorShaderModel() const;
     bool usesBroadcast(const gl::State &data) const;
+    bool usesSecondaryColor() const;
     bool usesFragDepth() const;
     bool usesPointCoord() const;
     bool usesFragCoord() const;
     bool usesPointSize() const;
     bool usesInsertedPointCoordValue() const;
     bool usesViewScale() const;
     bool hasANGLEMultiviewEnabled() const;
     bool usesViewID() const;
--- a/gfx/angle/checkout/src/libANGLE/renderer/d3d/ShaderD3D.cpp
+++ b/gfx/angle/checkout/src/libANGLE/renderer/d3d/ShaderD3D.cpp
@@ -75,16 +75,17 @@ std::string ShaderD3D::getDebugInfo() co
 void ShaderD3D::uncompile()
 {
     // set by compileToHLSL
     mCompilerOutputType = SH_ESSL_OUTPUT;
 
     mUsesMultipleRenderTargets   = false;
     mUsesFragColor               = false;
     mUsesFragData                = false;
+    mUsesSecondaryColor          = false;
     mUsesFragCoord               = false;
     mUsesFrontFacing             = false;
     mUsesPointSize               = false;
     mUsesPointCoord              = false;
     mUsesDepthRange              = false;
     mUsesFragDepth               = false;
     mHasANGLEMultiviewEnabled    = false;
     mUsesViewID                  = false;
@@ -202,16 +203,17 @@ bool ShaderD3D::postTranslateCompile(gl:
 {
     // TODO(jmadill): We shouldn't need to cache this.
     mCompilerOutputType = compiler->getShaderOutputType();
 
     const std::string &translatedSource = mData.getTranslatedSource();
 
     mUsesMultipleRenderTargets = translatedSource.find("GL_USES_MRT") != std::string::npos;
     mUsesFragColor             = translatedSource.find("GL_USES_FRAG_COLOR") != std::string::npos;
+    mUsesSecondaryColor        = translatedSource.find("GL_USES_SECONDARY_COLOR") != std::string::npos;
     mUsesFragData              = translatedSource.find("GL_USES_FRAG_DATA") != std::string::npos;
     mUsesFragCoord             = translatedSource.find("GL_USES_FRAG_COORD") != std::string::npos;
     mUsesFrontFacing           = translatedSource.find("GL_USES_FRONT_FACING") != std::string::npos;
     mUsesPointSize             = translatedSource.find("GL_USES_POINT_SIZE") != std::string::npos;
     mUsesPointCoord            = translatedSource.find("GL_USES_POINT_COORD") != std::string::npos;
     mUsesDepthRange            = translatedSource.find("GL_USES_DEPTH_RANGE") != std::string::npos;
     mUsesFragDepth             = translatedSource.find("GL_USES_FRAG_DEPTH") != std::string::npos;
     mHasANGLEMultiviewEnabled =
--- a/gfx/angle/checkout/src/libANGLE/renderer/d3d/ShaderD3D.h
+++ b/gfx/angle/checkout/src/libANGLE/renderer/d3d/ShaderD3D.h
@@ -61,31 +61,33 @@ class ShaderD3D : public ShaderImpl
     bool useImage2DFunction(const std::string &functionName) const;
     void appendDebugInfo(const std::string &info) const { mDebugInfo += info; }
 
     void generateWorkarounds(angle::CompilerWorkaroundsD3D *workarounds) const;
 
     bool usesMultipleRenderTargets() const { return mUsesMultipleRenderTargets; }
     bool usesFragColor() const { return mUsesFragColor; }
     bool usesFragData() const { return mUsesFragData; }
+    bool usesSecondaryColor() const { return mUsesSecondaryColor; }
     bool usesFragCoord() const { return mUsesFragCoord; }
     bool usesFrontFacing() const { return mUsesFrontFacing; }
     bool usesPointSize() const { return mUsesPointSize; }
     bool usesPointCoord() const { return mUsesPointCoord; }
     bool usesDepthRange() const { return mUsesDepthRange; }
     bool usesFragDepth() const { return mUsesFragDepth; }
     bool usesViewID() const { return mUsesViewID; }
     bool hasANGLEMultiviewEnabled() const { return mHasANGLEMultiviewEnabled; }
 
     ShShaderOutput getCompilerOutputType() const;
 
   private:
     bool mUsesMultipleRenderTargets;
     bool mUsesFragColor;
     bool mUsesFragData;
+    bool mUsesSecondaryColor;
     bool mUsesFragCoord;
     bool mUsesFrontFacing;
     bool mUsesPointSize;
     bool mUsesPointCoord;
     bool mUsesDepthRange;
     bool mUsesFragDepth;
     bool mHasANGLEMultiviewEnabled;
     bool mUsesViewID;
--- a/gfx/angle/checkout/src/libANGLE/renderer/d3d/d3d11/renderer11_utils.cpp
+++ b/gfx/angle/checkout/src/libANGLE/renderer/d3d/d3d11/renderer11_utils.cpp
@@ -1644,16 +1644,18 @@ void GenerateCaps(ID3D11Device *device,
     extensions->copyCompressedTexture            = true;
     extensions->textureStorageMultisample2DArray = true;
     extensions->multiviewMultisample =
         (extensions->multiview2 && extensions->textureStorageMultisample2DArray);
     extensions->copyTexture3d      = true;
     extensions->textureBorderClamp = true;
     extensions->textureMultisample = true;
     extensions->provokingVertex    = true;
+    extensions->blendFuncExtended        = true;
+    extensions->maxDualSourceDrawBuffers = 1;
 
     // D3D11 Feature Level 10_0+ uses SV_IsFrontFace in HLSL to emulate gl_FrontFacing.
     // D3D11 Feature Level 9_3 doesn't support SV_IsFrontFace, and has no equivalent, so can't
     // support gl_FrontFacing.
     limitations->noFrontFacingSupport =
         (renderer11DeviceCaps.featureLevel <= D3D_FEATURE_LEVEL_9_3);
 
     // D3D11 Feature Level 9_3 doesn't support alpha-to-coverage
@@ -1746,16 +1748,28 @@ D3D11_BLEND ConvertBlendFunc(GLenum glBl
             d3dBlend = D3D11_BLEND_BLEND_FACTOR;
             break;
         case GL_ONE_MINUS_CONSTANT_ALPHA:
             d3dBlend = D3D11_BLEND_INV_BLEND_FACTOR;
             break;
         case GL_SRC_ALPHA_SATURATE:
             d3dBlend = D3D11_BLEND_SRC_ALPHA_SAT;
             break;
+        case GL_SRC1_COLOR_EXT:
+            d3dBlend = (isAlpha ? D3D11_BLEND_SRC1_ALPHA : D3D11_BLEND_SRC1_COLOR);
+            break;
+        case GL_SRC1_ALPHA_EXT:
+            d3dBlend = D3D11_BLEND_SRC1_ALPHA;
+            break;
+        case GL_ONE_MINUS_SRC1_COLOR_EXT:
+            d3dBlend = (isAlpha ? D3D11_BLEND_INV_SRC1_ALPHA : D3D11_BLEND_INV_SRC1_COLOR);
+            break;
+        case GL_ONE_MINUS_SRC1_ALPHA_EXT:
+            d3dBlend = D3D11_BLEND_INV_SRC1_ALPHA;
+            break;
         default:
             UNREACHABLE();
     }
 
     return d3dBlend;
 }
 
 D3D11_BLEND_OP ConvertBlendOp(GLenum glBlendOp)
--- a/gfx/angle/cherry_picks.txt
+++ b/gfx/angle/cherry_picks.txt
@@ -1,8 +1,19 @@
+commit 5814bb88b10ef903abcf297f2de72619d853b4e9
+Author: Lee Salzman <lsalzman@mozilla.com>
+Date:   Tue Apr 30 23:42:31 2019 -0400
+
+    add support for EXT_blend_func_extended to D3D11
+    
+    Change-Id: Id66868851a490d0a68a7e76280720825c4844a45
+    Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/1591192
+    Commit-Queue: Geoff Lang <geofflang@chromium.org>
+    Reviewed-by: Shahbaz Youssefi <syoussefi@chromium.org>
+
 commit f2915830eaf4b2970c37d4baecf0dc8030c85dba
 Author: Jan Beich <jbeich@FreeBSD.org>
 Date:   Tue Apr 23 16:25:18 2019 -0700
 
     `environ` needs to be declared for (at least) FreeBSD.
     
     Declaring it unconditionally seems to build fine on all other platforms.
     
--- a/gfx/angle/targets/angle_common/moz.build
+++ b/gfx/angle/targets/angle_common/moz.build
@@ -6,17 +6,17 @@ DEFINES['ANGLE_ENABLE_DEBUG_ANNOTATIONS'
 #DEFINES['ANGLE_IS_64_BIT_CPU'] = True
 #DEFINES['CERT_CHAIN_PARA_HAS_EXTRA_FIELDS'] = True
 #DEFINES['CHROMIUM_BUILD'] = True
 #DEFINES['COMPONENT_BUILD'] = True
 DEFINES['DYNAMIC_ANNOTATIONS_ENABLED'] = '1'
 #DEFINES['FULL_SAFE_BROWSING'] = True
 DEFINES['NOMINMAX'] = True
 #DEFINES['NO_TCMALLOC'] = True
-DEFINES['NTDDI_VERSION'] = 'NTDDI_WIN10_RS2'
+DEFINES['NTDDI_VERSION'] = '0x0A000003'
 #DEFINES['PSAPI_VERSION'] = '2'
 #DEFINES['SAFE_BROWSING_CSD'] = True
 #DEFINES['SAFE_BROWSING_DB_LOCAL'] = True
 DEFINES['UNICODE'] = True
 #DEFINES['USE_AURA'] = '1'
 #DEFINES['WIN32'] = True
 #DEFINES['WIN32_LEAN_AND_MEAN'] = True
 #DEFINES['WINAPI_FAMILY'] = 'WINAPI_FAMILY_DESKTOP_APP'
@@ -40,16 +40,17 @@ LOCAL_INCLUDES += [
     '../../checkout/include/',
     '../../checkout/out/gen/angle/',
     '../../checkout/src/',
     '../../checkout/src/common/third_party/base/',
 ]
 
 #CXXFLAGS += [
 #    '/bigobj',
+#    '/d2FastFail',
 #    '/D__DATE__=',
 #    '/D__TIME__=',
 #    '/D__TIMESTAMP__=',
 #    '/FS',
 #    '/GF',
 #    '/GR-',
 #    '/Gy',
 #    '/MDd',
--- a/gfx/angle/targets/angle_gpu_info_util/moz.build
+++ b/gfx/angle/targets/angle_gpu_info_util/moz.build
@@ -6,17 +6,17 @@ include('../../moz.build.common')
 #DEFINES['CERT_CHAIN_PARA_HAS_EXTRA_FIELDS'] = True
 #DEFINES['CHROMIUM_BUILD'] = True
 #DEFINES['COMPONENT_BUILD'] = True
 DEFINES['DYNAMIC_ANNOTATIONS_ENABLED'] = '1'
 #DEFINES['FULL_SAFE_BROWSING'] = True
 DEFINES['GPU_INFO_USE_SETUPAPI'] = True
 DEFINES['NOMINMAX'] = True
 #DEFINES['NO_TCMALLOC'] = True
-DEFINES['NTDDI_VERSION'] = 'NTDDI_WIN10_RS2'
+DEFINES['NTDDI_VERSION'] = '0x0A000003'
 #DEFINES['PSAPI_VERSION'] = '2'
 #DEFINES['SAFE_BROWSING_CSD'] = True
 #DEFINES['SAFE_BROWSING_DB_LOCAL'] = True
 DEFINES['UNICODE'] = True
 #DEFINES['USE_AURA'] = '1'
 #DEFINES['WIN32'] = True
 #DEFINES['WIN32_LEAN_AND_MEAN'] = True
 #DEFINES['WINAPI_FAMILY'] = 'WINAPI_FAMILY_DESKTOP_APP'
@@ -40,16 +40,17 @@ LOCAL_INCLUDES += [
     '../../checkout/include/',
     '../../checkout/out/gen/angle/',
     '../../checkout/src/',
     '../../checkout/src/common/third_party/base/',
 ]
 
 #CXXFLAGS += [
 #    '/bigobj',
+#    '/d2FastFail',
 #    '/D__DATE__=',
 #    '/D__TIME__=',
 #    '/D__TIMESTAMP__=',
 #    '/FS',
 #    '/GF',
 #    '/GR-',
 #    '/Gy',
 #    '/MDd',
--- a/gfx/angle/targets/angle_image_util/moz.build
+++ b/gfx/angle/targets/angle_image_util/moz.build
@@ -5,17 +5,17 @@ include('../../moz.build.common')
 #DEFINES['ANGLE_IS_64_BIT_CPU'] = True
 #DEFINES['CERT_CHAIN_PARA_HAS_EXTRA_FIELDS'] = True
 #DEFINES['CHROMIUM_BUILD'] = True
 #DEFINES['COMPONENT_BUILD'] = True
 DEFINES['DYNAMIC_ANNOTATIONS_ENABLED'] = '1'
 #DEFINES['FULL_SAFE_BROWSING'] = True
 DEFINES['NOMINMAX'] = True
 #DEFINES['NO_TCMALLOC'] = True
-DEFINES['NTDDI_VERSION'] = 'NTDDI_WIN10_RS2'
+DEFINES['NTDDI_VERSION'] = '0x0A000003'
 #DEFINES['PSAPI_VERSION'] = '2'
 #DEFINES['SAFE_BROWSING_CSD'] = True
 #DEFINES['SAFE_BROWSING_DB_LOCAL'] = True
 DEFINES['UNICODE'] = True
 #DEFINES['USE_AURA'] = '1'
 #DEFINES['WIN32'] = True
 #DEFINES['WIN32_LEAN_AND_MEAN'] = True
 #DEFINES['WINAPI_FAMILY'] = 'WINAPI_FAMILY_DESKTOP_APP'
@@ -39,16 +39,17 @@ LOCAL_INCLUDES += [
     '../../checkout/include/',
     '../../checkout/out/gen/angle/',
     '../../checkout/src/',
     '../../checkout/src/common/third_party/base/',
 ]
 
 #CXXFLAGS += [
 #    '/bigobj',
+#    '/d2FastFail',
 #    '/D__DATE__=',
 #    '/D__TIME__=',
 #    '/D__TIMESTAMP__=',
 #    '/FS',
 #    '/GF',
 #    '/GR-',
 #    '/Gy',
 #    '/MDd',
--- a/gfx/angle/targets/libANGLE/moz.build
+++ b/gfx/angle/targets/libANGLE/moz.build
@@ -11,17 +11,17 @@ DEFINES['ANGLE_GENERATE_SHADER_DEBUG_INF
 #DEFINES['CERT_CHAIN_PARA_HAS_EXTRA_FIELDS'] = True
 #DEFINES['CHROMIUM_BUILD'] = True
 #DEFINES['COMPONENT_BUILD'] = True
 DEFINES['DYNAMIC_ANNOTATIONS_ENABLED'] = '1'
 #DEFINES['FULL_SAFE_BROWSING'] = True
 DEFINES['LIBANGLE_IMPLEMENTATION'] = True
 DEFINES['NOMINMAX'] = True
 #DEFINES['NO_TCMALLOC'] = True
-DEFINES['NTDDI_VERSION'] = 'NTDDI_WIN10_RS2'
+DEFINES['NTDDI_VERSION'] = '0x0A000003'
 #DEFINES['PSAPI_VERSION'] = '2'
 #DEFINES['SAFE_BROWSING_CSD'] = True
 #DEFINES['SAFE_BROWSING_DB_LOCAL'] = True
 DEFINES['UNICODE'] = True
 #DEFINES['USE_AURA'] = '1'
 #DEFINES['WIN32'] = True
 #DEFINES['WIN32_LEAN_AND_MEAN'] = True
 #DEFINES['WINAPI_FAMILY'] = 'WINAPI_FAMILY_DESKTOP_APP'
@@ -45,16 +45,17 @@ LOCAL_INCLUDES += [
     '../../checkout/include/',
     '../../checkout/out/gen/angle/',
     '../../checkout/src/',
     '../../checkout/src/common/third_party/base/',
 ]
 
 #CXXFLAGS += [
 #    '/bigobj',
+#    '/d2FastFail',
 #    '/D__DATE__=',
 #    '/D__TIME__=',
 #    '/D__TIMESTAMP__=',
 #    '/FS',
 #    '/GF',
 #    '/GR-',
 #    '/Gy',
 #    '/MDd',
--- a/gfx/angle/targets/libEGL/moz.build
+++ b/gfx/angle/targets/libEGL/moz.build
@@ -15,17 +15,17 @@ DEFINES['EGLAPI'] = ''
 DEFINES['EGL_EGLEXT_PROTOTYPES'] = True
 DEFINES['EGL_EGL_PROTOTYPES'] = '1'
 #DEFINES['FULL_SAFE_BROWSING'] = True
 DEFINES['GL_GLES_PROTOTYPES'] = '1'
 DEFINES['GL_GLEXT_PROTOTYPES'] = True
 DEFINES['LIBEGL_IMPLEMENTATION'] = True
 DEFINES['NOMINMAX'] = True
 #DEFINES['NO_TCMALLOC'] = True
-DEFINES['NTDDI_VERSION'] = 'NTDDI_WIN10_RS2'
+DEFINES['NTDDI_VERSION'] = '0x0A000003'
 #DEFINES['PSAPI_VERSION'] = '2'
 #DEFINES['SAFE_BROWSING_CSD'] = True
 #DEFINES['SAFE_BROWSING_DB_LOCAL'] = True
 DEFINES['UNICODE'] = True
 #DEFINES['USE_AURA'] = '1'
 #DEFINES['WIN32'] = True
 #DEFINES['WIN32_LEAN_AND_MEAN'] = True
 #DEFINES['WINAPI_FAMILY'] = 'WINAPI_FAMILY_DESKTOP_APP'
@@ -48,16 +48,17 @@ DEFINES['_UNICODE'] = True
 LOCAL_INCLUDES += [
     '../../checkout/include/',
     '../../checkout/out/gen/angle/',
     '../../checkout/src/',
 ]
 
 #CXXFLAGS += [
 #    '/bigobj',
+#    '/d2FastFail',
 #    '/D__DATE__=',
 #    '/D__TIME__=',
 #    '/D__TIMESTAMP__=',
 #    '/FS',
 #    '/GF',
 #    '/GR-',
 #    '/Gy',
 #    '/MDd',
@@ -159,16 +160,17 @@ OS_LIBS += [
     'winmm',
     'winspool',
     'ws2_32',
 ]
 
 #LDFLAGS += [
 #    '/DEBUG',
 #    '/DYNAMICBASE',
+#    '/fastfail',
 #    '/FIXED:NO',
 #    '/ignore:4199',
 #    '/ignore:4221',
 #    '/INCREMENTAL',
 #    '/MACHINE:X64',
 #    '/NXCOMPAT',
 #    '/pdbaltpath:%_PDB%',
 #    '/SUBSYSTEM:CONSOLE,5.02',
--- a/gfx/angle/targets/libGLESv2/moz.build
+++ b/gfx/angle/targets/libGLESv2/moz.build
@@ -16,17 +16,17 @@ DEFINES['EGL_EGL_PROTOTYPES'] = '1'
 DEFINES['GL_API'] = ''
 DEFINES['GL_APICALL'] = ''
 DEFINES['GL_GLES_PROTOTYPES'] = '1'
 DEFINES['GL_GLEXT_PROTOTYPES'] = True
 DEFINES['LIBANGLE_IMPLEMENTATION'] = True
 DEFINES['LIBGLESV2_IMPLEMENTATION'] = True
 DEFINES['NOMINMAX'] = True
 #DEFINES['NO_TCMALLOC'] = True
-DEFINES['NTDDI_VERSION'] = 'NTDDI_WIN10_RS2'
+DEFINES['NTDDI_VERSION'] = '0x0A000003'
 #DEFINES['PSAPI_VERSION'] = '2'
 #DEFINES['SAFE_BROWSING_CSD'] = True
 #DEFINES['SAFE_BROWSING_DB_LOCAL'] = True
 DEFINES['UNICODE'] = True
 #DEFINES['USE_AURA'] = '1'
 #DEFINES['WIN32'] = True
 #DEFINES['WIN32_LEAN_AND_MEAN'] = True
 #DEFINES['WINAPI_FAMILY'] = 'WINAPI_FAMILY_DESKTOP_APP'
@@ -50,16 +50,17 @@ LOCAL_INCLUDES += [
     '../../checkout/include/',
     '../../checkout/out/gen/angle/',
     '../../checkout/src/',
     '../../checkout/src/common/third_party/base/',
 ]
 
 #CXXFLAGS += [
 #    '/bigobj',
+#    '/d2FastFail',
 #    '/D__DATE__=',
 #    '/D__TIME__=',
 #    '/D__TIMESTAMP__=',
 #    '/FS',
 #    '/GF',
 #    '/GR-',
 #    '/Gy',
 #    '/MDd',
@@ -167,16 +168,17 @@ OS_LIBS += [
     'winmm',
     'winspool',
     'ws2_32',
 ]
 
 #LDFLAGS += [
 #    '/DEBUG',
 #    '/DYNAMICBASE',
+#    '/fastfail',
 #    '/FIXED:NO',
 #    '/ignore:4199',
 #    '/ignore:4221',
 #    '/INCREMENTAL',
 #    '/MACHINE:X64',
 #    '/NXCOMPAT',
 #    '/pdbaltpath:%_PDB%',
 #    '/SUBSYSTEM:CONSOLE,5.02',
--- a/gfx/angle/targets/preprocessor/moz.build
+++ b/gfx/angle/targets/preprocessor/moz.build
@@ -5,17 +5,17 @@ include('../../moz.build.common')
 #DEFINES['ANGLE_IS_64_BIT_CPU'] = True
 #DEFINES['CERT_CHAIN_PARA_HAS_EXTRA_FIELDS'] = True
 #DEFINES['CHROMIUM_BUILD'] = True
 #DEFINES['COMPONENT_BUILD'] = True
 DEFINES['DYNAMIC_ANNOTATIONS_ENABLED'] = '1'
 #DEFINES['FULL_SAFE_BROWSING'] = True
 DEFINES['NOMINMAX'] = True
 #DEFINES['NO_TCMALLOC'] = True
-DEFINES['NTDDI_VERSION'] = 'NTDDI_WIN10_RS2'
+DEFINES['NTDDI_VERSION'] = '0x0A000003'
 #DEFINES['PSAPI_VERSION'] = '2'
 #DEFINES['SAFE_BROWSING_CSD'] = True
 #DEFINES['SAFE_BROWSING_DB_LOCAL'] = True
 DEFINES['UNICODE'] = True
 #DEFINES['USE_AURA'] = '1'
 #DEFINES['WIN32'] = True
 #DEFINES['WIN32_LEAN_AND_MEAN'] = True
 #DEFINES['WINAPI_FAMILY'] = 'WINAPI_FAMILY_DESKTOP_APP'
@@ -39,16 +39,17 @@ LOCAL_INCLUDES += [
     '../../checkout/include/',
     '../../checkout/out/gen/angle/',
     '../../checkout/src/',
     '../../checkout/src/common/third_party/base/',
 ]
 
 #CXXFLAGS += [
 #    '/bigobj',
+#    '/d2FastFail',
 #    '/D__DATE__=',
 #    '/D__TIME__=',
 #    '/D__TIMESTAMP__=',
 #    '/FS',
 #    '/GF',
 #    '/GR-',
 #    '/Gy',
 #    '/MDd',
--- a/gfx/angle/targets/translator/moz.build
+++ b/gfx/angle/targets/translator/moz.build
@@ -8,17 +8,17 @@ DEFINES['ANGLE_ENABLE_HLSL'] = True
 #DEFINES['ANGLE_IS_64_BIT_CPU'] = True
 #DEFINES['CERT_CHAIN_PARA_HAS_EXTRA_FIELDS'] = True
 #DEFINES['CHROMIUM_BUILD'] = True
 #DEFINES['COMPONENT_BUILD'] = True
 DEFINES['DYNAMIC_ANNOTATIONS_ENABLED'] = '1'
 #DEFINES['FULL_SAFE_BROWSING'] = True
 DEFINES['NOMINMAX'] = True
 #DEFINES['NO_TCMALLOC'] = True
-DEFINES['NTDDI_VERSION'] = 'NTDDI_WIN10_RS2'
+DEFINES['NTDDI_VERSION'] = '0x0A000003'
 #DEFINES['PSAPI_VERSION'] = '2'
 #DEFINES['SAFE_BROWSING_CSD'] = True
 #DEFINES['SAFE_BROWSING_DB_LOCAL'] = True
 DEFINES['UNICODE'] = True
 #DEFINES['USE_AURA'] = '1'
 #DEFINES['WIN32'] = True
 #DEFINES['WIN32_LEAN_AND_MEAN'] = True
 #DEFINES['WINAPI_FAMILY'] = 'WINAPI_FAMILY_DESKTOP_APP'
@@ -42,16 +42,17 @@ LOCAL_INCLUDES += [
     '../../checkout/include/',
     '../../checkout/out/gen/angle/',
     '../../checkout/src/',
     '../../checkout/src/common/third_party/base/',
 ]
 
 #CXXFLAGS += [
 #    '/bigobj',
+#    '/d2FastFail',
 #    '/D__DATE__=',
 #    '/D__TIME__=',
 #    '/D__TIMESTAMP__=',
 #    '/FS',
 #    '/GF',
 #    '/GR-',
 #    '/Gy',
 #    '/MDd',
--- a/python/mozbuild/mozbuild/artifacts.py
+++ b/python/mozbuild/mozbuild/artifacts.py
@@ -89,23 +89,26 @@ NUM_REVISIONS_TO_QUERY = 500
 MAX_CACHED_TASKS = 400  # Number of pushheads to cache Task Cluster task data for.
 
 # Downloaded artifacts are cached, and a subset of their contents extracted for
 # easy installation.  This is most noticeable on Mac OS X: since mounting and
 # copying from DMG files is very slow, we extract the desired binaries to a
 # separate archive for fast re-installation.
 PROCESSED_SUFFIX = '.processed.jar'
 
-CANDIDATE_TREES = (
-    'mozilla-central',
-    'integration/mozilla-inbound',
-    'releases/mozilla-beta'
-)
 
 class ArtifactJob(object):
+    trust_domain = 'gecko'
+    candidate_trees = [
+        'mozilla-central',
+        'integration/mozilla-inbound',
+        'releases/mozilla-beta',
+    ]
+    try_tree = 'try'
+
     # These are a subset of TEST_HARNESS_BINS in testing/mochitest/Makefile.in.
     # Each item is a pair of (pattern, (src_prefix, dest_prefix), where src_prefix
     # is the prefix of the pattern relevant to its location in the archive, and
     # dest_prefix is the prefix to be added that will yield the final path relative
     # to dist/.
     test_artifact_patterns = {
         ('bin/BadCertServer', ('bin', 'bin')),
         ('bin/GenerateOCSPResponse', ('bin', 'bin')),
@@ -348,59 +351,95 @@ class AndroidArtifactJob(ArtifactJob):
                 writer.add(destpath.encode('utf-8'),
                            gzip.GzipFile(fileobj=reader[filename].uncompressed_data))
 
 
 class LinuxArtifactJob(ArtifactJob):
     package_re = r'public/build/target\.tar\.bz2'
     product = 'firefox'
 
-    package_artifact_patterns = {
-        'firefox/application.ini',
-        'firefox/crashreporter',
-        'firefox/dependentlibs.list',
-        'firefox/firefox',
-        'firefox/firefox-bin',
-        'firefox/minidump-analyzer',
-        'firefox/pingsender',
-        'firefox/platform.ini',
-        'firefox/plugin-container',
-        'firefox/updater',
-        'firefox/**/*.so',
+    _package_artifact_patterns = {
+        '{product}/application.ini',
+        '{product}/crashreporter',
+        '{product}/dependentlibs.list',
+        '{product}/{product}',
+        '{product}/{product}-bin',
+        '{product}/minidump-analyzer',
+        '{product}/pingsender',
+        '{product}/platform.ini',
+        '{product}/plugin-container',
+        '{product}/updater',
+        '{product}/**/*.so',
     }
 
+    @property
+    def package_artifact_patterns(self):
+        return {
+            p.format(product=self.product) for p in self._package_artifact_patterns
+        }
+
     def process_package_artifact(self, filename, processed_filename):
         added_entry = False
 
         with JarWriter(file=processed_filename, compress_level=5) as writer:
             with tarfile.open(filename) as reader:
                 for p, f in UnpackFinder(TarFinder(filename, reader)):
                     if not any(mozpath.match(p, pat) for pat in self.package_artifact_patterns):
                         continue
 
                     # We strip off the relative "firefox/" bit from the path,
                     # but otherwise preserve it.
                     destpath = mozpath.join('bin',
-                                            mozpath.relpath(p, "firefox"))
+                                            mozpath.relpath(p, self.product))
                     self.log(logging.INFO, 'artifact',
                              {'destpath': destpath},
                              'Adding {destpath} to processed archive')
                     writer.add(destpath.encode('utf-8'), f.open(), mode=f.mode)
                     added_entry = True
 
         if not added_entry:
             raise ValueError('Archive format changed! No pattern from "{patterns}" '
                              'matched an archive path.'.format(
                                  patterns=LinuxArtifactJob.package_artifact_patterns))
 
 
 class MacArtifactJob(ArtifactJob):
     package_re = r'public/build/target\.dmg'
     product = 'firefox'
 
+    # These get copied into dist/bin without the path, so "root/a/b/c" -> "dist/bin/c".
+    _paths_no_keep_path = ('Contents/MacOS', [
+        'crashreporter.app/Contents/MacOS/crashreporter',
+        '{product}',
+        '{product}-bin',
+        'libfreebl3.dylib',
+        'liblgpllibs.dylib',
+        # 'liblogalloc.dylib',
+        'libmozglue.dylib',
+        'libnss3.dylib',
+        'libnssckbi.dylib',
+        'libnssdbm3.dylib',
+        'libplugin_child_interpose.dylib',
+        # 'libreplace_jemalloc.dylib',
+        # 'libreplace_malloc.dylib',
+        'libmozavutil.dylib',
+        'libmozavcodec.dylib',
+        'libsoftokn3.dylib',
+        'pingsender',
+        'plugin-container.app/Contents/MacOS/plugin-container',
+        'updater.app/Contents/MacOS/org.mozilla.updater',
+        # 'xpcshell',
+        'XUL',
+    ])
+
+    @property
+    def paths_no_keep_path(self):
+        root, paths = self._paths_no_keep_path
+        return (root, [p.format(product=self.product) for p in paths])
+
     def process_package_artifact(self, filename, processed_filename):
         tempdir = tempfile.mkdtemp()
         oldcwd = os.getcwd()
         try:
             self.log(logging.INFO, 'artifact',
                 {'tempdir': tempdir},
                 'Unpacking DMG into {tempdir}')
             if self._substs['HOST_OS_ARCH'] == 'Linux':
@@ -421,58 +460,33 @@ class MacArtifactJob(ArtifactJob):
             else:
                 mozinstall.install(filename, tempdir)
 
             bundle_dirs = glob.glob(mozpath.join(tempdir, '*.app'))
             if len(bundle_dirs) != 1:
                 raise ValueError('Expected one source bundle, found: {}'.format(bundle_dirs))
             [source] = bundle_dirs
 
-            # These get copied into dist/bin without the path, so "root/a/b/c" -> "dist/bin/c".
-            paths_no_keep_path = ('Contents/MacOS', [
-                'crashreporter.app/Contents/MacOS/crashreporter',
-                'firefox',
-                'firefox-bin',
-                'libfreebl3.dylib',
-                'liblgpllibs.dylib',
-                # 'liblogalloc.dylib',
-                'libmozglue.dylib',
-                'libnss3.dylib',
-                'libnssckbi.dylib',
-                'libnssdbm3.dylib',
-                'libplugin_child_interpose.dylib',
-                # 'libreplace_jemalloc.dylib',
-                # 'libreplace_malloc.dylib',
-                'libmozavutil.dylib',
-                'libmozavcodec.dylib',
-                'libsoftokn3.dylib',
-                'pingsender',
-                'plugin-container.app/Contents/MacOS/plugin-container',
-                'updater.app/Contents/MacOS/org.mozilla.updater',
-                # 'xpcshell',
-                'XUL',
-            ])
-
             # These get copied into dist/bin with the path, so "root/a/b/c" -> "dist/bin/a/b/c".
             paths_keep_path = [
                 ('Contents/MacOS', [
                     'crashreporter.app/Contents/MacOS/minidump-analyzer',
                 ]),
                 ('Contents/Resources', [
                     'browser/components/libbrowsercomps.dylib',
                     'dependentlibs.list',
                     # 'firefox',
                     'gmp-clearkey/0.1/libclearkey.dylib',
                     # 'gmp-fake/1.0/libfake.dylib',
                     # 'gmp-fakeopenh264/1.0/libfakeopenh264.dylib',
                 ]),
             ]
 
             with JarWriter(file=processed_filename, compress_level=5) as writer:
-                root, paths = paths_no_keep_path
+                root, paths = self.paths_no_keep_path
                 finder = UnpackFinder(mozpath.join(source, root))
                 for path in paths:
                     for p, f in finder.find(path):
                         self.log(logging.INFO, 'artifact',
                             {'path': p},
                             'Adding {path} to processed archive')
                         destpath = mozpath.join('bin', os.path.basename(p))
                         writer.add(destpath.encode('utf-8'), f, mode=f.mode)
@@ -495,26 +509,32 @@ class MacArtifactJob(ArtifactJob):
                 self.log(logging.WARN, 'artifact',
                     {'tempdir': tempdir},
                     'Unable to delete {tempdir}')
                 pass
 
 
 class WinArtifactJob(ArtifactJob):
     package_re = r'public/build/target\.(zip|tar\.gz)'
-    package_artifact_patterns = {
-        'firefox/dependentlibs.list',
-        'firefox/platform.ini',
-        'firefox/application.ini',
-        'firefox/**/*.dll',
-        'firefox/*.exe',
-        'firefox/*.tlb',
+    product = 'firefox'
+
+    _package_artifact_patterns = {
+        '{product}/dependentlibs.list',
+        '{product}/platform.ini',
+        '{product}/application.ini',
+        '{product}/**/*.dll',
+        '{product}/*.exe',
+        '{product}/*.tlb',
     }
 
-    product = 'firefox'
+    @property
+    def package_artifact_patterns(self):
+        return {
+            p.format(product=self.product) for p in self._package_artifact_patterns
+        }
 
     # These are a subset of TEST_HARNESS_BINS in testing/mochitest/Makefile.in.
     test_artifact_patterns = {
         ('bin/BadCertServer.exe', ('bin', 'bin')),
         ('bin/GenerateOCSPResponse.exe', ('bin', 'bin')),
         ('bin/OCSPStaplingServer.exe', ('bin', 'bin')),
         ('bin/SymantecSanctionsServer.exe', ('bin', 'bin')),
         ('bin/certutil.exe', ('bin', 'bin')),
@@ -533,46 +553,80 @@ class WinArtifactJob(ArtifactJob):
     def process_package_artifact(self, filename, processed_filename):
         added_entry = False
         with JarWriter(file=processed_filename, compress_level=5) as writer:
             for p, f in UnpackFinder(JarFinder(filename, JarReader(filename))):
                 if not any(mozpath.match(p, pat) for pat in self.package_artifact_patterns):
                     continue
 
                 # strip off the relative "firefox/" bit from the path:
-                basename = mozpath.relpath(p, "firefox")
+                basename = mozpath.relpath(p, self.product)
                 basename = mozpath.join('bin', basename)
                 self.log(logging.INFO, 'artifact',
                     {'basename': basename},
                     'Adding {basename} to processed archive')
                 writer.add(basename.encode('utf-8'), f.open(), mode=f.mode)
                 added_entry = True
 
         if not added_entry:
             raise ValueError('Archive format changed! No pattern from "{patterns}"'
                              'matched an archive path.'.format(
                                  patterns=self.artifact_patterns))
 
 
+class ThunderbirdMixin(object):
+    trust_domain = 'comm'
+    product = 'thunderbird'
+    candidate_trees = [
+        'comm-central',
+    ]
+    try_tree = 'try-comm-central'
+
+
+class LinuxThunderbirdArtifactJob(ThunderbirdMixin, LinuxArtifactJob):
+    pass
+
+
+class MacThunderbirdArtifactJob(ThunderbirdMixin, MacArtifactJob):
+    _paths_no_keep_path = MacArtifactJob._paths_no_keep_path
+    _paths_no_keep_path[1].extend([
+        'libldap60.dylib',
+        'libldif60.dylib',
+        'libprldap60.dylib',
+    ])
+
+
+class WinThunderbirdArtifactJob(ThunderbirdMixin, WinArtifactJob):
+    pass
+
+
 def startswithwhich(s, prefixes):
     for prefix in prefixes:
         if s.startswith(prefix):
             return prefix
 
 
-JOB_DETAILS = {
+MOZ_JOB_DETAILS = {
     j: {
         'android': AndroidArtifactJob,
         'linux': LinuxArtifactJob,
         'macosx': MacArtifactJob,
         'win': WinArtifactJob,
     }[startswithwhich(j, ('android', 'linux', 'macosx', 'win'))]
     for j in JOB_CHOICES
 }
-
+COMM_JOB_DETAILS = {
+    j: {
+        'android': None,
+        'linux': LinuxThunderbirdArtifactJob,
+        'macosx': MacThunderbirdArtifactJob,
+        'win': WinThunderbirdArtifactJob,
+    }[startswithwhich(j, ('android', 'linux', 'macosx', 'win'))]
+    for j in JOB_CHOICES
+}
 
 
 def cachedmethod(cachefunc):
     '''Decorator to wrap a class or instance method with a memoizing callable that
     saves results in a (possibly shared) cache.
     '''
     def decorator(method):
         def wrapper(self, *args, **kwargs):
@@ -691,42 +745,35 @@ class PushheadCache(CacheManager):
 
 class TaskCache(CacheManager):
     '''Map candidate pushheads to Task Cluster task IDs and artifact URLs.'''
 
     def __init__(self, cache_dir, log=None, skip_cache=False):
         CacheManager.__init__(self, cache_dir, 'artifact_url', MAX_CACHED_TASKS, log=log, skip_cache=skip_cache)
 
     @cachedmethod(operator.attrgetter('_cache'))
-    def artifacts(self, tree, job, rev):
-        try:
-            artifact_job_class = JOB_DETAILS[job]
-        except KeyError:
-            self.log(logging.INFO, 'artifact',
-                {'job': job},
-                'Unknown job {job}')
-            raise KeyError("Unknown job")
-
+    def artifacts(self, tree, job, artifact_job_class, rev):
         # Grab the second part of the repo name, which is generally how things
         # are indexed. Eg: 'integration/mozilla-inbound' is indexed as
         # 'mozilla-inbound'
         tree = tree.split('/')[1] if '/' in tree else tree
 
         # PGO builds are now known as "shippable" for all platforms but Android.
         # For macOS and linux32 shippable builds are equivalent to opt builds and
         # replace them on some trees. Additionally, we no longer produce win64
         # opt builds on integration branches.
         if not job.startswith('android-'):
             if job.endswith('-pgo') or job in ('macosx64-opt', 'linux-opt',
                                                'win64-opt'):
                 tree += '.shippable'
             if job.endswith('-pgo'):
                 job = job.replace('-pgo', '-opt')
 
-        namespace = 'gecko.v2.{tree}.revision.{rev}.{product}.{job}'.format(
+        namespace = '{trust_domain}.v2.{tree}.revision.{rev}.{product}.{job}'.format(
+            trust_domain=artifact_job_class.trust_domain,
             rev=rev,
             tree=tree,
             product=artifact_job_class.product,
             job=job,
         )
         self.log(logging.INFO, 'artifact',
                  {'namespace': namespace},
                  'Searching Taskcluster index with namespace: {namespace}')
@@ -756,18 +803,21 @@ class Artifacts(object):
         self._job = job or self._guess_artifact_job()
         self._log = log
         self._hg = hg
         self._git = git
         self._cache_dir = cache_dir
         self._skip_cache = skip_cache
         self._topsrcdir = topsrcdir
 
+        app = self._substs.get('MOZ_BUILD_APP')
+        job_details = COMM_JOB_DETAILS if app == 'comm/mail' else MOZ_JOB_DETAILS
+
         try:
-            cls = JOB_DETAILS[self._job]
+            cls = job_details[self._job]
             self._artifact_job = cls(log=self._log,
                                      download_tests=download_tests,
                                      download_symbols=download_symbols,
                                      download_host_bins=download_host_bins,
                                      substs=self._substs)
         except KeyError:
             self.log(logging.INFO, 'artifact',
                 {'job': self._job},
@@ -823,17 +873,17 @@ class Artifacts(object):
         already have been pushed to multiple repositories. For each repository
         containing `rev`, the pushhead introducing `rev` and the previous
         `count` pushheads from that point are included in the output.
         """
 
         with self._pushhead_cache as pushhead_cache:
             found_pushids = {}
 
-            search_trees = list(CANDIDATE_TREES)
+            search_trees = self._artifact_job.candidate_trees
             for tree in search_trees:
                 self.log(logging.INFO, 'artifact',
                          {'tree': tree,
                           'rev': rev},
                          'Attempting to find a pushhead containing {rev} on {tree}.')
                 try:
                     pushid = pushhead_cache.parent_pushhead_id(tree, rev)
                     found_pushids[tree] = pushid
@@ -947,17 +997,17 @@ see https://developer.mozilla.org/en-US/
         if not count:
             raise Exception('Could not find any candidate pushheads in the last {num} revisions.\n'
                             'Search started with {rev}, which must be known to Mozilla automation.\n\n'
                             'see https://developer.mozilla.org/en-US/docs/Artifact_builds'.format(
                                 rev=last_revs[0], num=NUM_PUSHHEADS_TO_QUERY_PER_PARENT))
 
     def find_pushhead_artifacts(self, task_cache, job, tree, pushhead):
         try:
-            taskId, artifacts = task_cache.artifacts(tree, job, pushhead)
+            taskId, artifacts = task_cache.artifacts(tree, job, self._artifact_job.__class__, pushhead)
         except ValueError:
             return None
 
         urls = []
         for artifact_name in self._artifact_job.find_candidate_artifacts(artifacts):
             # We can easily extract the task ID from the URL.  We can't easily
             # extract the build ID; we use the .ini files embedded in the
             # downloaded artifact for this.
@@ -1093,17 +1143,20 @@ see https://developer.mozilla.org/en-US/
             raise ValueError('revision specification must resolve to exactly one commit')
 
         self.log(logging.INFO, 'artifact',
                  {'revset': revset,
                   'revision': revision},
                  'Will only accept artifacts from a pushhead at {revision} '
                  '(matched revset "{revset}").')
         # Include try in our search to allow pulling from a specific push.
-        pushheads = [(list(CANDIDATE_TREES) + ['try'], revision)]
+        pushheads = [(
+            self._artifact_job.candidate_trees + [self._artifact_job.try_tree],
+            revision
+        )]
         return self._install_from_hg_pushheads(pushheads, distdir)
 
     def install_from_task(self, taskId, distdir):
         artifacts = list_artifacts(taskId)
 
         urls = []
         for artifact_name in self._artifact_job.find_candidate_artifacts(artifacts):
             # We can easily extract the task ID from the URL.  We can't easily
--- a/python/mozbuild/mozbuild/mach_commands.py
+++ b/python/mozbuild/mozbuild/mach_commands.py
@@ -1323,21 +1323,24 @@ class PackageFrontend(MachCommandBase):
         hg = None
         if conditions.is_hg(self):
             hg = self.substs['HG']
 
         git = None
         if conditions.is_git(self):
             git = self.substs['GIT']
 
+        # If we're building Thunderbird, we should be checking for comm-central artifacts.
+        topsrcdir = self.substs.get('commtopsrcdir', self.topsrcdir)
+
         from mozbuild.artifacts import Artifacts
         artifacts = Artifacts(tree, self.substs, self.defines, job,
                               log=self.log, cache_dir=cache_dir,
                               skip_cache=skip_cache, hg=hg, git=git,
-                              topsrcdir=self.topsrcdir,
+                              topsrcdir=topsrcdir,
                               download_tests=download_tests,
                               download_symbols=download_symbols,
                               download_host_bins=download_host_bins)
         return artifacts
 
     @ArtifactSubCommand('artifact', 'install',
         'Install a good pre-built artifact.')
     @CommandArgument('source', metavar='SRC', nargs='?', type=str,
--- a/security/apps/AppTrustDomain.cpp
+++ b/security/apps/AppTrustDomain.cpp
@@ -129,17 +129,18 @@ nsresult AppTrustDomain::SetTrustedRoot(
   // If we're verifying add-ons signed by our production root, we want to make
   // sure a valid intermediate certificate is available for path building.
   // Merely holding this alive in memory makes it available for NSS to find in
   // AppTrustDomain::FindIssuer.
   if (trustedRoot == nsIX509CertDB::AddonsPublicRoot) {
     SECItem intermediateDER = {
         siBuffer,
         const_cast<uint8_t*>(addonsPublicIntermediate),
-        mozilla::ArrayLength(addonsPublicIntermediate),
+        static_cast<unsigned int>(
+            mozilla::ArrayLength(addonsPublicIntermediate)),
     };
     mAddonsIntermediate.reset(CERT_NewTempCertificate(
         CERT_GetDefaultCertDB(), &intermediateDER, nullptr, false, true));
     if (!mAddonsIntermediate) {
       return mozilla::psm::GetXPCOMFromNSSError(PR_GetError());
     }
   }