merge mozilla-inbound to mozilla-central. r=merge a=merge
authorSebastian Hengst <archaeopteryx@coole-files.de>
Fri, 27 Oct 2017 12:45:34 +0300
changeset 388665 ecef003d836730c07ecfcfa25df8e71a88aec99f
parent 388664 d9613617f268ebf9657b22a610ba3117325c5c79 (current diff)
parent 388590 e97bbbe3841f70c4c8b53f85a56524245f2393d3 (diff)
child 388666 b4fcd38ea40f074f248c5ad3e3cbfdff7250afa9
child 388670 ae49d4a5762264ded3aae4006baddc2203b79b94
child 388731 bf990261dea7d278674de8ec24ee8cbf60aa357a
push id96678
push userarchaeopteryx@coole-files.de
push dateFri, 27 Oct 2017 10:02:49 +0000
treeherdermozilla-inbound@b4fcd38ea40f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge, merge
milestone58.0a1
first release with
nightly linux32
ecef003d8367 / 58.0a1 / 20171027100103 / files
nightly linux64
ecef003d8367 / 58.0a1 / 20171027100103 / files
nightly mac
ecef003d8367 / 58.0a1 / 20171027100103 / files
nightly win32
ecef003d8367 / 58.0a1 / 20171027100103 / files
nightly win64
ecef003d8367 / 58.0a1 / 20171027100103 / 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 mozilla-inbound to mozilla-central. r=merge a=merge
accessible/tests/mochitest/events/docload_wnd.html
accessible/tests/mochitest/events/test_docload.html
accessible/tests/mochitest/events/test_docload_aria.html
devtools/client/inspector/fonts/components/App.js
devtools/client/inspector/inspector.js
devtools/client/inspector/layout/components/App.js
dom/base/CustomElementRegistry.cpp
dom/indexedDB/ActorsParent.cpp
dom/tests/mochitest/webcomponents/test_document_register_base_queue.html
layout/painting/nsDisplayList.cpp
moz.build
testing/talos/talos/unittests/__init__.py
testing/talos/talos/unittests/browser_output.ts.txt
testing/talos/talos/unittests/browser_output.tsvg.txt
testing/talos/talos/unittests/profile.tgz
testing/talos/talos/unittests/ps-Acj.out
testing/talos/talos/unittests/test_talosconfig_browser_config.json
testing/talos/talos/unittests/test_talosconfig_test_config.json
testing/talos/talos/unittests/xrestop_output.txt
testing/web-platform/meta/custom-elements/parser/parser-constructs-custom-element-synchronously.html.ini
testing/web-platform/meta/custom-elements/parser/parser-sets-attributes-and-children.html.ini
testing/web-platform/meta/custom-elements/parser/parser-uses-constructed-element.html.ini
testing/web-platform/meta/custom-elements/reactions/HTMLAnchorElement.html.ini
testing/web-platform/meta/custom-elements/reactions/HTMLTableElement.html.ini
testing/web-platform/meta/custom-elements/reactions/HTMLTableRowElement.html.ini
testing/web-platform/meta/custom-elements/reactions/HTMLTableSectionElement.html.ini
testing/web-platform/meta/custom-elements/reactions/Range.html.ini
--- a/accessible/ipc/win/HandlerProvider.cpp
+++ b/accessible/ipc/win/HandlerProvider.cpp
@@ -123,17 +123,18 @@ HandlerProvider::GetAndSerializePayload(
   mSerializer = MakeUnique<mscom::StructToStream>(payload, &IA2Payload_Encode);
 
   // Now that we have serialized payload, we should free any BSTRs that were
   // allocated in BuildIA2Data.
   ClearIA2Data(payload.mData);
 }
 
 HRESULT
-HandlerProvider::GetHandlerPayloadSize(NotNull<DWORD*> aOutPayloadSize)
+HandlerProvider::GetHandlerPayloadSize(NotNull<mscom::IInterceptor*> aInterceptor,
+                                       NotNull<DWORD*> aOutPayloadSize)
 {
   MOZ_ASSERT(mscom::IsCurrentThreadMTA());
 
   if (!IsTargetInterfaceCacheable()) {
     *aOutPayloadSize = mscom::StructToStream::GetEmptySize();
     return S_OK;
   }
 
@@ -146,50 +147,164 @@ HandlerProvider::GetHandlerPayloadSize(N
     *aOutPayloadSize = mscom::StructToStream::GetEmptySize();
     return S_OK;
   }
 
   *aOutPayloadSize = mSerializer->GetSize();
   return S_OK;
 }
 
+template <typename CondFnT, typename ExeFnT>
+class MOZ_RAII ExecuteWhen final
+{
+public:
+  ExecuteWhen(CondFnT& aCondFn, ExeFnT& aExeFn)
+    : mCondFn(aCondFn)
+    , mExeFn(aExeFn)
+  {
+  }
+
+  ~ExecuteWhen()
+  {
+    if (mCondFn()) {
+      mExeFn();
+    }
+  }
+
+  ExecuteWhen(const ExecuteWhen&) = delete;
+  ExecuteWhen(ExecuteWhen&&) = delete;
+  ExecuteWhen& operator=(const ExecuteWhen&) = delete;
+  ExecuteWhen& operator=(ExecuteWhen&&) = delete;
+
+private:
+  CondFnT&  mCondFn;
+  ExeFnT&   mExeFn;
+};
+
 void
 HandlerProvider::BuildIA2Data(IA2Data* aOutIA2Data)
 {
   MOZ_ASSERT(aOutIA2Data);
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(mTargetUnk);
   MOZ_ASSERT(IsTargetInterfaceCacheable());
 
   RefPtr<NEWEST_IA2_INTERFACE>
     target(static_cast<NEWEST_IA2_INTERFACE*>(mTargetUnk.get()));
 
+  HRESULT hr = E_UNEXPECTED;
+
+  auto hasFailed = [&hr]() -> bool {
+    return FAILED(hr);
+  };
+
+  auto cleanup = [this, aOutIA2Data]() -> void {
+    ClearIA2Data(*aOutIA2Data);
+  };
+
+  ExecuteWhen<decltype(hasFailed), decltype(cleanup)> onFail(hasFailed, cleanup);
+
+  const VARIANT kChildIdSelf = {VT_I4};
+  VARIANT varVal;
+
+  hr = target->accLocation(&aOutIA2Data->mLeft, &aOutIA2Data->mTop,
+                           &aOutIA2Data->mWidth, &aOutIA2Data->mHeight,
+                           kChildIdSelf);
+  if (FAILED(hr)) {
+    return;
+  }
+
+  hr = target->get_accRole(kChildIdSelf, &aOutIA2Data->mRole);
+  if (FAILED(hr)) {
+    return;
+  }
+
+  hr = target->get_accState(kChildIdSelf, &varVal);
+  if (FAILED(hr)) {
+    return;
+  }
+
+  aOutIA2Data->mState = varVal.lVal;
+
+  hr = target->get_accKeyboardShortcut(kChildIdSelf,
+                                       &aOutIA2Data->mKeyboardShortcut);
+  if (FAILED(hr)) {
+    return;
+  }
+
+  hr = target->get_accName(kChildIdSelf, &aOutIA2Data->mName);
+  if (FAILED(hr)) {
+    return;
+  }
+
+  hr = target->get_accDescription(kChildIdSelf, &aOutIA2Data->mDescription);
+  if (FAILED(hr)) {
+    return;
+  }
+
+  hr = target->get_accChildCount(&aOutIA2Data->mChildCount);
+  if (FAILED(hr)) {
+    return;
+  }
+
+  hr = target->get_accValue(kChildIdSelf, &aOutIA2Data->mValue);
+  if (FAILED(hr)) {
+    return;
+  }
+
+  hr = target->get_states(&aOutIA2Data->mIA2States);
+  if (FAILED(hr)) {
+    return;
+  }
+
+  hr = target->get_attributes(&aOutIA2Data->mAttributes);
+  if (FAILED(hr)) {
+    return;
+  }
+
+  HWND hwnd;
+  hr = target->get_windowHandle(&hwnd);
+  if (FAILED(hr)) {
+    return;
+  }
+
+  aOutIA2Data->mHwnd = PtrToLong(hwnd);
+
+  hr = target->get_locale(&aOutIA2Data->mIA2Locale);
+  if (FAILED(hr)) {
+    return;
+  }
+
+  hr = target->role(&aOutIA2Data->mIA2Role);
+  if (FAILED(hr)) {
+    return;
+  }
+
   // NB: get_uniqueID should be the final property retrieved in this method,
   // as its presence is used to determine whether the rest of this data
   // retrieval was successful.
-  HRESULT hr = target->get_uniqueID(&aOutIA2Data->mUniqueId);
-  if (FAILED(hr)) {
-    ClearIA2Data(*aOutIA2Data);
-  }
+  hr = target->get_uniqueID(&aOutIA2Data->mUniqueId);
 }
 
 void
 HandlerProvider::ClearIA2Data(IA2Data& aData)
 {
+  ::VariantClear(&aData.mRole);
   ZeroMemory(&aData, sizeof(IA2Data));
 }
 
 bool
 HandlerProvider::IsTargetInterfaceCacheable()
 {
   return MarshalAs(mTargetUnkIid) == NEWEST_IA2_IID;
 }
 
 HRESULT
-HandlerProvider::WriteHandlerPayload(NotNull<IStream*> aStream)
+HandlerProvider::WriteHandlerPayload(NotNull<mscom::IInterceptor*> aInterceptor,
+                                     NotNull<IStream*> aStream)
 {
   MutexAutoLock lock(mMutex);
 
   if (!mSerializer || !(*mSerializer)) {
     // Failed payload serialization is non-fatal
     mscom::StructToStream emptyStruct;
     return emptyStruct.Write(aStream);
   }
--- a/accessible/ipc/win/HandlerProvider.h
+++ b/accessible/ipc/win/HandlerProvider.h
@@ -36,18 +36,20 @@ public:
 
   // IUnknown
   STDMETHODIMP QueryInterface(REFIID riid, void** ppv) override;
   STDMETHODIMP_(ULONG) AddRef() override;
   STDMETHODIMP_(ULONG) Release() override;
 
   // IHandlerProvider
   STDMETHODIMP GetHandler(NotNull<CLSID*> aHandlerClsid) override;
-  STDMETHODIMP GetHandlerPayloadSize(NotNull<DWORD*> aOutPayloadSize) override;
-  STDMETHODIMP WriteHandlerPayload(NotNull<IStream*> aStream) override;
+  STDMETHODIMP GetHandlerPayloadSize(NotNull<mscom::IInterceptor*> aInterceptor,
+                                     NotNull<DWORD*> aOutPayloadSize) override;
+  STDMETHODIMP WriteHandlerPayload(NotNull<mscom::IInterceptor*> aInterceptor,
+                                   NotNull<IStream*> aStream) override;
   STDMETHODIMP_(REFIID) MarshalAs(REFIID aIid) override;
   STDMETHODIMP_(REFIID) GetEffectiveOutParamIid(REFIID aCallIid,
                                                 ULONG aCallMethod) override;
   STDMETHODIMP NewInstance(REFIID aIid,
                            mscom::InterceptorTargetPtr<IUnknown> aTarget,
                            NotNull<mscom::IHandlerProvider**> aOutNewPayload) override;
 
   // IGeckoBackChannel
--- a/accessible/ipc/win/PlatformChild.cpp
+++ b/accessible/ipc/win/PlatformChild.cpp
@@ -44,17 +44,18 @@ static const mozilla::mscom::ArrayData s
    NEWEST_IA2_IID, 1, mozilla::mscom::ArrayData::Flag::eAllocatedByServer}
 };
 
 // Type libraries are thread-neutral, so we can register those from any
 // apartment. OTOH, proxies must be registered from within the apartment where
 // we intend to instantiate them. Therefore RegisterProxy() must be called
 // via EnsureMTA.
 PlatformChild::PlatformChild()
-  : mAccTypelib(mozilla::mscom::RegisterTypelib(L"oleacc.dll",
+  : mIA2Proxy(mozilla::mscom::RegisterProxy(L"ia2marshal.dll"))
+  , mAccTypelib(mozilla::mscom::RegisterTypelib(L"oleacc.dll",
         mozilla::mscom::RegistrationFlags::eUseSystemDirectory))
   , mMiscTypelib(mozilla::mscom::RegisterTypelib(L"Accessible.tlb"))
   , mSdnTypelib(mozilla::mscom::RegisterTypelib(L"AccessibleMarshal.dll"))
 {
   WORD actCtxResourceId = Compatibility::GetActCtxResourceId();
 
   mozilla::mscom::MTADeletePtr<mozilla::mscom::ActivationContextRegion> tmpActCtxMTA;
   mozilla::mscom::EnsureMTA([actCtxResourceId, &tmpActCtxMTA]() -> void {
@@ -67,18 +68,19 @@ PlatformChild::PlatformChild()
 
 
   UniquePtr<mozilla::mscom::RegisteredProxy> customProxy;
   mozilla::mscom::EnsureMTA([&customProxy]() -> void {
     customProxy = Move(mozilla::mscom::RegisterProxy());
   });
   mCustomProxy = Move(customProxy);
 
-  UniquePtr<mozilla::mscom::RegisteredProxy> ia2Proxy;
-  mozilla::mscom::EnsureMTA([&ia2Proxy]() -> void {
-    ia2Proxy = Move(mozilla::mscom::RegisterProxy(L"ia2marshal.dll"));
+  // IA2 needs to be registered in both the main thread's STA as well as the MTA
+  UniquePtr<mozilla::mscom::RegisteredProxy> ia2ProxyMTA;
+  mozilla::mscom::EnsureMTA([&ia2ProxyMTA]() -> void {
+    ia2ProxyMTA = Move(mozilla::mscom::RegisterProxy(L"ia2marshal.dll"));
   });
-  mIA2Proxy = Move(ia2Proxy);
+  mIA2ProxyMTA = Move(ia2ProxyMTA);
 }
 
 } // namespace a11y
 } // namespace mozilla
 
--- a/accessible/ipc/win/PlatformChild.h
+++ b/accessible/ipc/win/PlatformChild.h
@@ -23,16 +23,17 @@ public:
   PlatformChild(PlatformChild&&) = delete;
   PlatformChild& operator=(PlatformChild&) = delete;
   PlatformChild& operator=(PlatformChild&&) = delete;
 
 private:
   mscom::MTADeletePtr<mozilla::mscom::ActivationContextRegion> mActCtxMTA;
   UniquePtr<mozilla::mscom::RegisteredProxy> mCustomProxy;
   UniquePtr<mozilla::mscom::RegisteredProxy> mIA2Proxy;
+  UniquePtr<mozilla::mscom::RegisteredProxy> mIA2ProxyMTA;
   UniquePtr<mozilla::mscom::RegisteredProxy> mAccTypelib;
   UniquePtr<mozilla::mscom::RegisteredProxy> mMiscTypelib;
   UniquePtr<mozilla::mscom::RegisteredProxy> mSdnTypelib;
 };
 
 } // namespace mozilla
 } // namespace a11y
 
--- a/accessible/ipc/win/handler/AccessibleHandler.cpp
+++ b/accessible/ipc/win/handler/AccessibleHandler.cpp
@@ -363,29 +363,39 @@ AccessibleHandler::Invoke(DISPID dispIdM
   if (FAILED(hr)) {
     return hr;
   }
 
   return mDispatch->Invoke(dispIdMember, riid, lcid, wFlags, pDispParams,
                            pVarResult, pExcepInfo, puArgErr);
 }
 
+inline static BSTR
+CopyBSTR(BSTR aSrc)
+{
+  return ::SysAllocStringLen(aSrc, ::SysStringLen(aSrc));
+}
+
 #define BEGIN_CACHE_ACCESS \
   { \
     HRESULT hr; \
     if (FAILED(hr = MaybeUpdateCachedData())) { \
       return hr; \
     } \
   }
 
-static BSTR
-CopyBSTR(BSTR aSrc)
-{
-  return SysAllocStringLen(aSrc, SysStringLen(aSrc));
-}
+#define GET_FIELD(member, assignTo) \
+  { \
+    assignTo = mCachedData.mData.member; \
+  }
+
+#define GET_BSTR(member, assignTo) \
+  { \
+    assignTo = CopyBSTR(mCachedData.mData.member); \
+  }
 
 HRESULT
 AccessibleHandler::get_accParent(IDispatch **ppdispParent)
 {
   HRESULT hr = ResolveIA2();
   if (FAILED(hr)) {
     return hr;
   }
@@ -393,21 +403,28 @@ AccessibleHandler::get_accParent(IDispat
 }
 
 HRESULT
 AccessibleHandler::get_accChildCount(long *pcountChildren)
 {
   if (!pcountChildren) {
     return E_INVALIDARG;
   }
-  HRESULT hr = ResolveIA2();
-  if (FAILED(hr)) {
-    return hr;
+
+  if (!HasPayload()) {
+    HRESULT hr = ResolveIA2();
+    if (FAILED(hr)) {
+      return hr;
+    }
+    return mIA2PassThru->get_accChildCount(pcountChildren);
   }
-  return mIA2PassThru->get_accChildCount(pcountChildren);
+
+  BEGIN_CACHE_ACCESS;
+  GET_FIELD(mChildCount, *pcountChildren);
+  return S_OK;
 }
 
 HRESULT
 AccessibleHandler::get_accChild(VARIANT varChild, IDispatch **ppdispChild)
 {
   if (!ppdispChild) {
     return E_INVALIDARG;
   }
@@ -425,75 +442,110 @@ AccessibleHandler::get_accChild(VARIANT 
 }
 
 HRESULT
 AccessibleHandler::get_accName(VARIANT varChild, BSTR *pszName)
 {
   if (!pszName) {
     return E_INVALIDARG;
   }
-  HRESULT hr = ResolveIA2();
-  if (FAILED(hr)) {
-    return hr;
+
+  if (varChild.lVal != CHILDID_SELF || !HasPayload()) {
+    HRESULT hr = ResolveIA2();
+    if (FAILED(hr)) {
+      return hr;
+    }
+    return mIA2PassThru->get_accName(varChild, pszName);
   }
-  return mIA2PassThru->get_accName(varChild, pszName);
+
+  BEGIN_CACHE_ACCESS;
+  GET_BSTR(mName, *pszName);
+  return S_OK;
 }
 
 HRESULT
 AccessibleHandler::get_accValue(VARIANT varChild, BSTR *pszValue)
 {
   if (!pszValue) {
     return E_INVALIDARG;
   }
-  HRESULT hr = ResolveIA2();
-  if (FAILED(hr)) {
-    return hr;
+
+  if (varChild.lVal != CHILDID_SELF || !HasPayload()) {
+    HRESULT hr = ResolveIA2();
+    if (FAILED(hr)) {
+      return hr;
+    }
+    return mIA2PassThru->get_accValue(varChild, pszValue);
   }
-  return mIA2PassThru->get_accValue(varChild, pszValue);
+
+  BEGIN_CACHE_ACCESS;
+  GET_BSTR(mValue, *pszValue);
+  return S_OK;
 }
 
 HRESULT
 AccessibleHandler::get_accDescription(VARIANT varChild, BSTR *pszDescription)
 {
   if (!pszDescription) {
     return E_INVALIDARG;
   }
-  HRESULT hr = ResolveIA2();
-  if (FAILED(hr)) {
-    return hr;
+
+  if (varChild.lVal != CHILDID_SELF || !HasPayload()) {
+    HRESULT hr = ResolveIA2();
+    if (FAILED(hr)) {
+      return hr;
+    }
+    return mIA2PassThru->get_accDescription(varChild, pszDescription);
   }
-  return mIA2PassThru->get_accDescription(varChild, pszDescription);
+
+  BEGIN_CACHE_ACCESS;
+  GET_BSTR(mDescription, *pszDescription);
+  return S_OK;
 }
 
 
 HRESULT
 AccessibleHandler::get_accRole(VARIANT varChild, VARIANT *pvarRole)
 {
   if (!pvarRole) {
     return E_INVALIDARG;
   }
-  HRESULT hr = ResolveIA2();
-  if (FAILED(hr)) {
-    return hr;
+
+  if (varChild.lVal != CHILDID_SELF || !HasPayload()) {
+    HRESULT hr = ResolveIA2();
+    if (FAILED(hr)) {
+      return hr;
+    }
+    return mIA2PassThru->get_accRole(varChild, pvarRole);
   }
-  return mIA2PassThru->get_accRole(varChild, pvarRole);
+
+  BEGIN_CACHE_ACCESS;
+  return ::VariantCopy(pvarRole, &mCachedData.mData.mRole);
 }
 
 
 HRESULT
 AccessibleHandler::get_accState(VARIANT varChild, VARIANT *pvarState)
 {
   if (!pvarState) {
     return E_INVALIDARG;
   }
-  HRESULT hr = ResolveIA2();
-  if (FAILED(hr)) {
-    return hr;
+
+  if (varChild.lVal != CHILDID_SELF || !HasPayload()) {
+    HRESULT hr = ResolveIA2();
+    if (FAILED(hr)) {
+      return hr;
+    }
+    return mIA2PassThru->get_accState(varChild, pvarState);
   }
-  return mIA2PassThru->get_accState(varChild, pvarState);
+
+  pvarState->vt = VT_I4;
+  BEGIN_CACHE_ACCESS;
+  GET_FIELD(mState, pvarState->lVal);
+  return S_OK;
 }
 
 HRESULT
 AccessibleHandler::get_accHelp(VARIANT varChild, BSTR *pszHelp)
 {
   // This matches what AccessibleWrap does
   if (!pszHelp) {
     return E_INVALIDARG;
@@ -517,21 +569,28 @@ AccessibleHandler::get_accHelpTopic(BSTR
 
 HRESULT
 AccessibleHandler::get_accKeyboardShortcut(VARIANT varChild,
                                            BSTR *pszKeyboardShortcut)
 {
   if (!pszKeyboardShortcut) {
     return E_INVALIDARG;
   }
-  HRESULT hr = ResolveIA2();
-  if (FAILED(hr)) {
-    return hr;
+
+  if (varChild.lVal != CHILDID_SELF || !HasPayload()) {
+    HRESULT hr = ResolveIA2();
+    if (FAILED(hr)) {
+      return hr;
+    }
+    return mIA2PassThru->get_accKeyboardShortcut(varChild, pszKeyboardShortcut);
   }
-  return mIA2PassThru->get_accKeyboardShortcut(varChild, pszKeyboardShortcut);
+
+  BEGIN_CACHE_ACCESS;
+  GET_BSTR(mKeyboardShortcut, *pszKeyboardShortcut);
+  return S_OK;
 }
 
 HRESULT
 AccessibleHandler::get_accFocus(VARIANT *pvarChild)
 {
   HRESULT hr = ResolveIA2();
   if (FAILED(hr)) {
     return hr;
@@ -551,43 +610,63 @@ AccessibleHandler::get_accSelection(VARI
 
 HRESULT
 AccessibleHandler::get_accDefaultAction(VARIANT varChild,
                                         BSTR *pszDefaultAction)
 {
   if (!pszDefaultAction) {
     return E_INVALIDARG;
   }
-  HRESULT hr = ResolveIA2();
-  if (FAILED(hr)) {
-    return hr;
+
+  if (varChild.lVal != CHILDID_SELF || !HasPayload()) {
+    HRESULT hr = ResolveIA2();
+    if (FAILED(hr)) {
+      return hr;
+    }
+    return mIA2PassThru->get_accDefaultAction(varChild, pszDefaultAction);
   }
-  return mIA2PassThru->get_accDefaultAction(varChild, pszDefaultAction);
+
+  BEGIN_CACHE_ACCESS;
+  GET_BSTR(mDefaultAction, *pszDefaultAction);
+  return S_OK;
 }
 
 HRESULT
 AccessibleHandler::accSelect(long flagsSelect, VARIANT varChild)
 {
   HRESULT hr = ResolveIA2();
   if (FAILED(hr)) {
     return hr;
   }
   return mIA2PassThru->accSelect(flagsSelect, varChild);
 }
 
 HRESULT
 AccessibleHandler::accLocation(long *pxLeft, long *pyTop, long *pcxWidth,
                                long *pcyHeight, VARIANT varChild)
 {
-  HRESULT hr = ResolveIA2();
-  if (FAILED(hr)) {
-    return hr;
+  if (varChild.lVal != CHILDID_SELF || !HasPayload()) {
+    HRESULT hr = ResolveIA2();
+    if (FAILED(hr)) {
+      return hr;
+    }
+    return mIA2PassThru->accLocation(pxLeft, pyTop, pcxWidth, pcyHeight,
+                                     varChild);
   }
-  return mIA2PassThru->accLocation(pxLeft, pyTop, pcxWidth, pcyHeight,
-                                   varChild);
+
+  if (!pxLeft || !pyTop || !pcxWidth || !pcyHeight) {
+    return E_INVALIDARG;
+  }
+
+  BEGIN_CACHE_ACCESS;
+  GET_FIELD(mLeft, *pxLeft);
+  GET_FIELD(mTop, *pyTop);
+  GET_FIELD(mWidth, *pcxWidth);
+  GET_FIELD(mHeight, *pcyHeight);
+  return S_OK;
 }
 
 HRESULT
 AccessibleHandler::accNavigate(long navDir, VARIANT varStart,
                                VARIANT *pvarEndUpAt)
 {
   HRESULT hr = ResolveIA2();
   if (FAILED(hr)) {
@@ -664,21 +743,28 @@ AccessibleHandler::get_relations(long ma
 }
 
 HRESULT
 AccessibleHandler::role(long* role)
 {
   if (!role) {
     return E_INVALIDARG;
   }
-  HRESULT hr = ResolveIA2();
-  if (FAILED(hr)) {
-    return hr;
+
+  if (!HasPayload()) {
+    HRESULT hr = ResolveIA2();
+    if (FAILED(hr)) {
+      return hr;
+    }
+    return mIA2PassThru->role(role);
   }
-  return mIA2PassThru->role(role);
+
+  BEGIN_CACHE_ACCESS;
+  GET_FIELD(mIA2Role, *role);
+  return S_OK;
 }
 
 HRESULT
 AccessibleHandler::scrollTo(IA2ScrollType scrollType)
 {
   HRESULT hr = ResolveIA2();
   if (FAILED(hr)) {
     return hr;
@@ -710,21 +796,28 @@ AccessibleHandler::get_groupPosition(lon
 }
 
 HRESULT
 AccessibleHandler::get_states(AccessibleStates* states)
 {
   if (!states) {
     return E_INVALIDARG;
   }
-  HRESULT hr = ResolveIA2();
-  if (FAILED(hr)) {
-    return hr;
+
+  if (!HasPayload()) {
+    HRESULT hr = ResolveIA2();
+    if (FAILED(hr)) {
+      return hr;
+    }
+    return mIA2PassThru->get_states(states);
   }
-  return mIA2PassThru->get_states(states);
+
+  BEGIN_CACHE_ACCESS;
+  GET_FIELD(mIA2States, *states);
+  return S_OK;
 }
 
 HRESULT
 AccessibleHandler::get_extendedRole(BSTR* extendedRole)
 {
   // This matches ia2Accessible
   if (!extendedRole) {
     return E_INVALIDARG;
@@ -800,21 +893,30 @@ AccessibleHandler::get_uniqueID(long* un
 }
 
 HRESULT
 AccessibleHandler::get_windowHandle(HWND* windowHandle)
 {
   if (!windowHandle) {
     return E_INVALIDARG;
   }
-  HRESULT hr = ResolveIA2();
-  if (FAILED(hr)) {
-    return hr;
+
+  if (!HasPayload()) {
+    HRESULT hr = ResolveIA2();
+    if (FAILED(hr)) {
+      return hr;
+    }
+    return mIA2PassThru->get_windowHandle(windowHandle);
   }
-  return mIA2PassThru->get_windowHandle(windowHandle);
+
+  BEGIN_CACHE_ACCESS;
+  long hwnd = 0;
+  GET_FIELD(mHwnd, hwnd);
+  *windowHandle = reinterpret_cast<HWND>(uintptr_t(hwnd));
+  return S_OK;
 }
 
 HRESULT
 AccessibleHandler::get_indexInParent(long* indexInParent)
 {
   HRESULT hr = ResolveIA2();
   if (FAILED(hr)) {
     return hr;
@@ -823,35 +925,50 @@ AccessibleHandler::get_indexInParent(lon
 }
 
 HRESULT
 AccessibleHandler::get_locale(IA2Locale* locale)
 {
   if (!locale) {
     return E_INVALIDARG;
   }
-  HRESULT hr = ResolveIA2();
-  if (FAILED(hr)) {
-    return hr;
+
+  if (!HasPayload()) {
+    HRESULT hr = ResolveIA2();
+    if (FAILED(hr)) {
+      return hr;
+    }
+    return mIA2PassThru->get_locale(locale);
   }
-  return mIA2PassThru->get_locale(locale);
+
+  BEGIN_CACHE_ACCESS;
+  GET_BSTR(mIA2Locale.language, locale->language);
+  GET_BSTR(mIA2Locale.country, locale->country);
+  GET_BSTR(mIA2Locale.variant, locale->variant);
   return S_OK;
 }
 
 HRESULT
 AccessibleHandler::get_attributes(BSTR* attributes)
 {
   if (!attributes) {
     return E_INVALIDARG;
   }
-  HRESULT hr = ResolveIA2();
-  if (FAILED(hr)) {
-    return hr;
+
+  if (!HasPayload()) {
+    HRESULT hr = ResolveIA2();
+    if (FAILED(hr)) {
+      return hr;
+    }
+    return mIA2PassThru->get_attributes(attributes);
   }
-  return mIA2PassThru->get_attributes(attributes);
+
+  BEGIN_CACHE_ACCESS;
+  GET_BSTR(mAttributes, *attributes);
+  return S_OK;
 }
 
 HRESULT
 AccessibleHandler::get_attribute(BSTR name, VARIANT* attribute)
 {
   // We could extract these individually from cached mAttributes.
   // Consider it if traffic warrants it
   HRESULT hr = ResolveIA2();
--- a/accessible/ipc/win/handler/HandlerData.idl
+++ b/accessible/ipc/win/handler/HandlerData.idl
@@ -9,17 +9,34 @@
 
 import "ocidl.idl";
 import "ServProv.idl";
 
 import "AccessibleText.idl";
 
 typedef struct _IA2Data
 {
-  long mUniqueId;
+  VARIANT           mRole;
+  long              mState;
+  long              mChildCount;
+  long              mIA2Role;
+  AccessibleStates  mIA2States;
+  long              mLeft;
+  long              mTop;
+  long              mWidth;
+  long              mHeight;
+  long              mHwnd;
+  BSTR              mKeyboardShortcut;
+  BSTR              mName;
+  BSTR              mDescription;
+  BSTR              mDefaultAction;
+  BSTR              mValue;
+  BSTR              mAttributes;
+  IA2Locale         mIA2Locale;
+  long              mUniqueId;
 } IA2Data;
 
 interface IGeckoBackChannel;
 
 // We define different CLSIDs and IIDs depending on channel and officiality.
 // This prevents handlers from installing overtop one another when multiple
 // channels are present. Note that we do not do this for all UUIDs in this IDL,
 // just the ones that are written to the registry (coclass and interfaces that
--- a/accessible/tests/mochitest/events/a11y.ini
+++ b/accessible/tests/mochitest/events/a11y.ini
@@ -1,11 +1,10 @@
 [DEFAULT]
 support-files =
-  docload_wnd.html
   focus.html
   scroll.html
   !/accessible/tests/mochitest/*.js
   !/accessible/tests/mochitest/letters.gif
 
 [test_aria_alert.html]
 [test_aria_menu.html]
 [test_aria_objattr.html]
@@ -14,18 +13,16 @@ support-files =
 [test_attrs.html]
 [test_bug1322593.html]
 [test_bug1322593-2.html]
 [test_caretmove.html]
 [test_caretmove.xul]
 [test_coalescence.html]
 [test_contextmenu.html]
 [test_descrchange.html]
-[test_docload.html]
-[test_docload_aria.html]
 [test_dragndrop.html]
 [test_flush.html]
 [test_focus_aria_activedescendant.html]
 [test_focus_autocomplete.xul]
 # Disabled on Linux and Windows due to frequent failures - bug 695019, bug 890795
 skip-if = os == 'win' || os == 'linux'
 [test_focus_canvas.html]
 [test_focus_contextmenu.xul]
new file mode 100644
--- /dev/null
+++ b/accessible/tests/mochitest/events/docload/a11y.ini
@@ -0,0 +1,11 @@
+[DEFAULT]
+support-files =
+  docload_wnd.html
+  !/accessible/tests/mochitest/*.js
+
+[test_docload_aria.html]
+[test_docload_busy.html]
+[test_docload_embedded.html]
+[test_docload_iframe.html]
+[test_docload_root.html]
+[test_docload_shutdown.html]
rename from accessible/tests/mochitest/events/docload_wnd.html
rename to accessible/tests/mochitest/events/docload/docload_wnd.html
new file mode 100644
--- /dev/null
+++ b/accessible/tests/mochitest/events/docload/test_docload_aria.html
@@ -0,0 +1,76 @@
+<html>
+
+<head>
+  <title>Accessible events testing for ARIA document</title>
+
+  <link rel="stylesheet" type="text/css"
+        href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+  <script type="application/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+  <script type="application/javascript"
+          src="../../common.js"></script>
+  <script type="application/javascript"
+          src="../../role.js"></script>
+  <script type="application/javascript"
+          src="../../states.js"></script>
+  <script type="application/javascript"
+          src="../../events.js"></script>
+
+  <script type="application/javascript">
+    // //////////////////////////////////////////////////////////////////////////
+    // Invokers
+
+    function showARIADialog(aID) {
+      this.dialogNode = getNode(aID);
+
+      this.eventSeq = [
+        new invokerChecker(EVENT_DOCUMENT_LOAD_COMPLETE, this.dialogNode)
+      ];
+
+      this.invoke = function showARIADialog_invoke() {
+        this.dialogNode.style.display = "block";
+      };
+
+      this.getID = function showARIADialog_getID() {
+        return "show ARIA dialog";
+      };
+    }
+
+    // //////////////////////////////////////////////////////////////////////////
+    // Do tests
+
+    var gQueue = null;
+
+    function doTests() {
+      gQueue = new eventQueue();
+
+      gQueue.push(new showARIADialog("dialog"));
+      gQueue.push(new showARIADialog("document"));
+
+      gQueue.invoke(); // Will call SimpleTest.finish();
+    }
+
+    SimpleTest.waitForExplicitFinish();
+    addA11yLoadEvent(doTests);
+  </script>
+</head>
+
+<body>
+
+  <a target="_blank"
+     href="https://bugzilla.mozilla.org/show_bug.cgi?id=759833"
+     title="ARIA documents should fire document loading events">
+    Mozilla Bug 759833
+  </a>
+
+  <p id="display"></p>
+  <div id="content" style="display: none"></div>
+  <pre id="test">
+  </pre>
+
+  <div role="dialog" id="dialog" style="display: none;">It's a dialog</div>
+  <div role="document" id="document" style="display: none;">It's a document</div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/accessible/tests/mochitest/events/docload/test_docload_busy.html
@@ -0,0 +1,84 @@
+<html>
+
+<head>
+  <title>Accessible events testing for document</title>
+
+  <link rel="stylesheet" type="text/css"
+        href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+  <script type="application/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+  <script type="application/javascript"
+          src="../../common.js"></script>
+  <script type="application/javascript"
+          src="../../role.js"></script>
+  <script type="application/javascript"
+          src="../../states.js"></script>
+  <script type="application/javascript"
+          src="../../events.js"></script>
+
+  <script type="application/javascript">
+    // //////////////////////////////////////////////////////////////////////////
+    // Invokers
+
+    function makeIFrameVisible(aID) {
+      this.DOMNode = getNode(aID);
+
+      this.eventSeq = [
+        new invokerChecker(EVENT_REORDER, this.DOMNode.parentNode),
+        {
+          type: EVENT_STATE_CHANGE,
+          get target() {
+            return getAccessible("iframe").firstChild;
+          },
+          match(aEvent) {
+            // The document shouldn't have busy state (the DOM document was
+            // loaded before its accessible was created). Do this test lately to
+            // make sure the content of document accessible was created
+            // initially, prior to this the document accessible keeps busy
+            // state. The initial creation happens asynchronously after document
+            // creation, there are no events we could use to catch it.
+            let { state, isEnabled } = aEvent.QueryInterface(nsIAccessibleStateChangeEvent);
+            return state & STATE_BUSY && !isEnabled;
+          }
+        }
+      ];
+
+      this.invoke = () => (this.DOMNode.style.visibility = "visible");
+
+      this.getID = () =>
+        "The accessible for DOM document loaded before it's shown shouldn't have busy state.";
+    }
+
+
+    // //////////////////////////////////////////////////////////////////////////
+    // Do tests
+
+    function doTests() {
+      const gQueue = new eventQueue();
+      gQueue.push(new makeIFrameVisible("iframe"));
+      gQueue.invoke(); // Will call SimpleTest.finish();
+    }
+
+    SimpleTest.waitForExplicitFinish();
+    addA11yLoadEvent(doTests);
+  </script>
+</head>
+
+<body>
+
+  <a target="_blank"
+     href="https://bugzilla.mozilla.org/show_bug.cgi?id=658185"
+     title="The DOM document loaded before it's shown shouldn't have busy state">
+    Mozilla Bug 658185
+  </a>
+
+  <p id="display"></p>
+  <div id="content" style="display: none"></div>
+  <pre id="test">
+  </pre>
+
+  <div id="testContainer"><iframe id="iframe" src="about:" style="visibility: hidden;"></iframe></div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/accessible/tests/mochitest/events/docload/test_docload_embedded.html
@@ -0,0 +1,86 @@
+<html>
+
+<head>
+  <title>Accessible events testing for document</title>
+
+  <link rel="stylesheet" type="text/css"
+        href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+  <script type="application/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+  <script type="application/javascript"
+          src="../../common.js"></script>
+  <script type="application/javascript"
+          src="../../role.js"></script>
+  <script type="application/javascript"
+          src="../../events.js"></script>
+
+  <script type="application/javascript">
+    // //////////////////////////////////////////////////////////////////////////
+    // Invokers
+
+    function changeIframeSrc(aIdentifier, aURL) {
+      this.DOMNode = getNode(aIdentifier);
+
+      function getIframeDoc() {
+        return getAccessible(getNode(aIdentifier).contentDocument);
+      }
+
+      this.eventSeq = [
+        new invokerChecker(EVENT_REORDER, getAccessible(this.DOMNode)),
+        new asyncInvokerChecker(EVENT_DOCUMENT_LOAD_COMPLETE, getIframeDoc)
+      ];
+
+      this.invoke = () => (this.DOMNode.src = aURL);
+
+      this.finalCheck = () =>
+        testAccessibleTree(this.DOMNode, {
+          role: ROLE_INTERNAL_FRAME,
+          children: [
+            {
+              role: ROLE_DOCUMENT,
+              name: aURL == "about:" ? "About:" : aURL
+            }
+          ]
+        });
+
+      this.getID = () => `change iframe src on ${aURL}`;
+    }
+
+    // //////////////////////////////////////////////////////////////////////////
+    // Do tests
+
+    function doTests() {
+      const gQueue = new eventQueue();
+      gQueue.push(new changeIframeSrc("iframe", "about:"));
+      gQueue.push(new changeIframeSrc("iframe", "about:buildconfig"));
+      gQueue.invoke(); // Will call SimpleTest.finish();
+    }
+
+    SimpleTest.waitForExplicitFinish();
+    addA11yLoadEvent(doTests);
+  </script>
+</head>
+
+<body>
+
+  <a target="_blank"
+     href="https://bugzilla.mozilla.org/show_bug.cgi?id=420845"
+     title="Fire event_reorder on any embedded frames/iframes whos document has just loaded">
+    Mozilla Bug 420845
+  </a>
+  <a target="_blank"
+     href="https://bugzilla.mozilla.org/show_bug.cgi?id=754165"
+     title="Fire document load events on iframes too">
+    Mozilla Bug 754165
+  </a>
+
+  <p id="display"></p>
+  <div id="content" style="display: none"></div>
+  <pre id="test">
+  </pre>
+
+  <div id="testContainer"><iframe id="iframe"></iframe></div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/accessible/tests/mochitest/events/docload/test_docload_iframe.html
@@ -0,0 +1,100 @@
+<html>
+
+<head>
+  <title>Accessible events testing for document</title>
+
+  <link rel="stylesheet" type="text/css"
+        href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+  <script type="application/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+  <script type="application/javascript"
+          src="../../common.js"></script>
+  <script type="application/javascript"
+          src="../../role.js"></script>
+  <script type="application/javascript"
+          src="../../states.js"></script>
+  <script type="application/javascript"
+          src="../../events.js"></script>
+
+  <script type="application/javascript">
+    // //////////////////////////////////////////////////////////////////////////
+    // Invokers
+
+    const kHide = 1;
+    const kShow = 2;
+    const kRemove = 3;
+
+    function morphIFrame(aIdentifier, aAction) {
+      this.DOMNode = getNode(aIdentifier);
+      this.IFrameContainerDOMNode = this.DOMNode.parentNode;
+
+      this.eventSeq = [
+        new invokerChecker(aAction === kShow ? EVENT_SHOW : EVENT_HIDE, this.DOMNode),
+        new invokerChecker(EVENT_REORDER, this.IFrameContainerDOMNode)
+      ];
+
+      this.invoke = () => {
+        if (aAction === kRemove) {
+          this.IFrameContainerDOMNode.removeChild(this.DOMNode);
+        } else {
+          this.DOMNode.style.display = aAction === kHide ? "none" : "block";
+        }
+      };
+
+      this.finalCheck = () =>
+        testAccessibleTree(this.IFrameContainerDOMNode, {
+          role: ROLE_SECTION,
+          children: (aAction == kHide || aAction == kRemove) ? [ ] :
+            [
+              {
+                role: ROLE_INTERNAL_FRAME,
+                children: [
+                  { role: ROLE_DOCUMENT }
+                ]
+              }
+            ]
+        });
+
+      this.getID = () => {
+        if (aAction === kRemove) {
+          return "remove iframe";
+        }
+
+        return `change display style of iframe to ${aAction === kHide ? "none" : "block"}`;
+      };
+    }
+
+    // //////////////////////////////////////////////////////////////////////////
+    // Do tests
+
+    function doTests() {
+      const gQueue = new eventQueue(EVENT_REORDER);
+      gQueue.push(new morphIFrame("iframe", kHide));
+      gQueue.push(new morphIFrame("iframe", kShow));
+      gQueue.push(new morphIFrame("iframe", kRemove));
+      gQueue.invoke(); // Will call SimpleTest.finish();
+    }
+
+    SimpleTest.waitForExplicitFinish();
+    addA11yLoadEvent(doTests);
+  </script>
+</head>
+
+<body>
+
+  <a target="_blank"
+     href="https://bugzilla.mozilla.org/show_bug.cgi?id=566103"
+     title="Reorganize accessible document handling">
+    Mozilla Bug 566103
+  </a>
+
+  <p id="display"></p>
+  <div id="content" style="display: none"></div>
+  <pre id="test">
+  </pre>
+
+  <div id="testContainer"><iframe id="iframe"></iframe></div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/accessible/tests/mochitest/events/docload/test_docload_root.html
@@ -0,0 +1,135 @@
+<html>
+
+<head>
+  <title>Accessible events testing for document</title>
+
+  <link rel="stylesheet" type="text/css"
+        href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+  <script type="application/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+  <script type="application/javascript"
+          src="../../common.js"></script>
+  <script type="application/javascript"
+          src="../../role.js"></script>
+
+  <script type="application/javascript">
+    // Front end stuff sometimes likes to stuff things in the hidden window(s)
+    // in which case there's accessibles for that content.
+    Components.utils.import("resource://gre/modules/Services.jsm");
+
+    // Force the creation of an accessible for the hidden window's document.
+    let doc = Services.appShell.hiddenDOMWindow.document;
+    gAccService.getAccessibleFor(doc);
+
+    // The private hidden window will be lazily created that's why we need to do
+    // it here *before* loading '../../events.js' or else we'll have a duplicate
+    // reorder event.
+    let privateDoc = Services.appShell.hiddenPrivateDOMWindow.document;
+
+    // Force the creation of an accessible for the private hidden window's doc.
+    gAccService.getAccessibleFor(privateDoc);
+  </script>
+
+  <script type="application/javascript"
+          src="../../events.js"></script>
+
+  <script type="application/javascript">
+    // //////////////////////////////////////////////////////////////////////////
+    // Invokers
+
+    let gDialog;
+    let gDialogDoc;
+    let gRootAcc;
+
+    function openDialogWnd(aURL) {
+      // Get application root accessible.
+      let docAcc = getAccessible(document);
+      while (docAcc) {
+        gRootAcc = docAcc;
+        try {
+          docAcc = docAcc.parent;
+        } catch (e) {
+          ok(false, `Can't get parent for ${prettyName(docAcc)}`);
+          throw e;
+        }
+      }
+
+      this.eventSeq = [ new invokerChecker(EVENT_REORDER, gRootAcc) ];
+
+      this.invoke = () => (gDialog = window.openDialog(aURL));
+
+      this.finalCheck = () => {
+        const accTree = {
+          role: ROLE_APP_ROOT,
+          children: [
+            {
+              role: ROLE_CHROME_WINDOW
+            },
+            {
+              role: ROLE_CHROME_WINDOW
+            },
+            {
+              role: ROLE_CHROME_WINDOW
+            },
+            {
+              role: ROLE_CHROME_WINDOW
+            }
+          ]
+        };
+
+        testAccessibleTree(gRootAcc, accTree)
+
+        gDialogDoc = gDialog.document;
+        ok(isAccessibleInCache(gDialogDoc),
+          `The document accessible for '${aURL}' is not in cache!`);
+      };
+
+      this.getID = () => `open dialog '${aURL}'`;
+    }
+
+    function closeDialogWnd() {
+      this.eventSeq = [ new invokerChecker(EVENT_FOCUS, getAccessible(document)) ];
+
+      this.invoke = () => gDialog.close();
+
+      this.finalCheck = () => {
+        ok(!isAccessibleInCache(gDialogDoc),
+          `The document accessible for dialog is in cache still!`);
+
+        gDialog = gDialogDoc = gRootAcc = null;
+      };
+
+      this.getID = () => "close dialog";
+    }
+
+    // //////////////////////////////////////////////////////////////////////////
+    // Do tests
+
+    function doTests() {
+      const gQueue = new eventQueue();
+      gQueue.push(new openDialogWnd("about:"));
+      gQueue.push(new closeDialogWnd());
+      gQueue.invoke(); // Will call SimpleTest.finish();
+    }
+
+    SimpleTest.waitForExplicitFinish();
+    addA11yLoadEvent(doTests);
+  </script>
+</head>
+
+<body>
+
+  <a target="_blank"
+     href="https://bugzilla.mozilla.org/show_bug.cgi?id=506206"
+     title="Fire event_reorder application root accessible">
+    Mozilla Bug 506206
+  </a>
+
+  <p id="display"></p>
+  <div id="content" style="display: none"></div>
+  <pre id="test">
+  </pre>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/accessible/tests/mochitest/events/docload/test_docload_shutdown.html
@@ -0,0 +1,157 @@
+<html>
+
+<head>
+  <title>Accessible events testing for document</title>
+
+  <link rel="stylesheet" type="text/css"
+        href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+  <script type="application/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+  <script type="application/javascript"
+          src="../../common.js"></script>
+  <script type="application/javascript"
+          src="../../role.js"></script>
+
+  <script type="application/javascript">
+    // Front end stuff sometimes likes to stuff things in the hidden window(s)
+    // in which case there's accessibles for that content.
+    Components.utils.import("resource://gre/modules/Services.jsm");
+
+    // Force the creation of an accessible for the hidden window's document.
+    let doc = Services.appShell.hiddenDOMWindow.document;
+    gAccService.getAccessibleFor(doc);
+
+    // The private hidden window will be lazily created that's why we need to do
+    // it here *before* loading '../../events.js' or else we'll have a duplicate
+    // reorder event.
+    let privateDoc = Services.appShell.hiddenPrivateDOMWindow.document;
+
+    // Force the creation of an accessible for the private hidden window's doc.
+    gAccService.getAccessibleFor(privateDoc);
+  </script>
+
+  <script type="application/javascript"
+          src="../../events.js"></script>
+
+  <script type="application/javascript">
+    // //////////////////////////////////////////////////////////////////////////
+    // Invokers
+
+    let gDialog;
+    let gDialogDoc;
+    let gRootAcc;
+    let gIframeDoc
+
+    function openWndShutdownDoc(aURL) {
+      // Get application root accessible.
+      let docAcc = getAccessible(document);
+      while (docAcc) {
+        gRootAcc = docAcc;
+        try {
+          docAcc = docAcc.parent;
+        } catch (e) {
+          ok(false, `Can't get parent for ${prettyName(docAcc)}`);
+          throw e;
+        }
+      }
+
+      this.eventSeq = [
+        new invokerChecker(EVENT_REORDER, gRootAcc),
+        {
+          type: EVENT_HIDE,
+          get target() {
+            gDialogDoc = gDialog.document;
+            const iframe = gDialogDoc.getElementById("iframe");
+            gIframeDoc = iframe.contentDocument;
+            return iframe;
+          },
+          get targetDescr() {
+            return "inner iframe of docload_wnd.html document";
+          }
+        }
+      ];
+
+
+      this.invoke = () => gDialog = window.openDialog(aURL);
+
+      this.finalCheck = () => {
+        const accTree = {
+          role: ROLE_APP_ROOT,
+          children: [
+            {
+              role: ROLE_CHROME_WINDOW
+            },
+            {
+              role: ROLE_CHROME_WINDOW
+            },
+            {
+              role: ROLE_CHROME_WINDOW
+            },
+            {
+              role: ROLE_CHROME_WINDOW
+            }
+          ]
+        };
+
+        testAccessibleTree(gRootAcc, accTree)
+        // After timeout after event hide for iframe was handled the document
+        // accessible for iframe's document should no longer be in cache.
+        ok(!isAccessibleInCache(gIframeDoc),
+          "The document accessible for iframe is in cache still after iframe hide!");
+        ok(isAccessibleInCache(gDialogDoc),
+          `The document accessible for '${aURL}' is not in cache!`);
+      };
+
+      this.getID = () => `open dialog '${aURL}'`;
+    }
+
+    function closeWndShutdownDoc() {
+      this.eventSeq = [ new invokerChecker(EVENT_FOCUS, getAccessible(document)) ];
+
+      this.invoke = () => gDialog.close();
+
+      this.finalCheck = () => {
+        ok(!isAccessibleInCache(gDialogDoc),
+          "The document accessible for dialog is in cache still!");
+        // After the window is closed all alive subdocument accessibles should
+        // be shut down.
+        ok(!isAccessibleInCache(gIframeDoc),
+          "The document accessible for iframe is in cache still!");
+
+        gDialog = gDialogDoc = gRootAcc = gIframeDoc = null;
+      };
+
+      this.getID = () => "close dialog";
+    }
+
+    // //////////////////////////////////////////////////////////////////////////
+    // Do tests
+
+    function doTests() {
+      const gQueue = new eventQueue();
+      gQueue.push(new openWndShutdownDoc("../../events/docload/docload_wnd.html"));
+      gQueue.push(new closeWndShutdownDoc());
+      gQueue.invoke(); // Will call SimpleTest.finish();
+    }
+
+    SimpleTest.waitForExplicitFinish();
+    addA11yLoadEvent(doTests);
+  </script>
+</head>
+
+<body>
+
+  <a target="_blank"
+     href="https://bugzilla.mozilla.org/show_bug.cgi?id=571459"
+     title="Shutdown document accessible when presshell goes away">
+    Mozilla Bug 571459
+   </a>
+
+  <p id="display"></p>
+  <div id="content" style="display: none"></div>
+  <pre id="test">
+  </pre>
+</body>
+</html>
deleted file mode 100644
--- a/accessible/tests/mochitest/events/test_docload.html
+++ /dev/null
@@ -1,337 +0,0 @@
-<html>
-
-<head>
-  <title>Accessible events testing for document</title>
-
-  <link rel="stylesheet" type="text/css"
-        href="chrome://mochikit/content/tests/SimpleTest/test.css" />
-
-  <script type="application/javascript"
-          src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
-
-  <script type="application/javascript"
-          src="../common.js"></script>
-  <script type="application/javascript"
-          src="../role.js"></script>
-  <script type="application/javascript"
-          src="../states.js"></script>
-
-  <script type="application/javascript">
-    // Front end stuff sometimes likes to stuff things in the hidden window(s)
-    // in which case there's accessibles for that content.
-    Components.utils.import("resource://gre/modules/Services.jsm");
-
-    // Force the creation of an accessible for the hidden window's document.
-    var doc = Services.appShell.hiddenDOMWindow.document;
-    gAccService.getAccessibleFor(doc);
-
-    // The private hidden window will be lazily created that's why we need to do
-    // it here *before* loading '../events.js' or else we'll have a duplicate
-    // reorder event.
-    var privateDoc = Services.appShell.hiddenPrivateDOMWindow.document;
-
-    // Force the creation of an accessible for the private hidden window's doc.
-    gAccService.getAccessibleFor(privateDoc);
-  </script>
-
-  <script type="application/javascript"
-          src="../events.js"></script>
-
-  <script type="application/javascript">
-    // //////////////////////////////////////////////////////////////////////////
-    // Invokers
-
-    function changeIframeSrc(aIdentifier, aURL) {
-      this.DOMNode = getNode(aIdentifier);
-
-      this.eventSeq = [
-        new invokerChecker(EVENT_REORDER, getAccessible(this.DOMNode)),
-        new asyncInvokerChecker(EVENT_DOCUMENT_LOAD_COMPLETE, getIframeDoc)
-      ];
-
-      this.invoke = function changeIframeSrc_invoke() {
-        this.DOMNode.src = aURL;
-      };
-
-      this.finalCheck = function changeIframeSrc_finalCheck() {
-        var accTree = {
-          role: ROLE_INTERNAL_FRAME,
-          children: [
-            {
-              role: ROLE_DOCUMENT,
-              name: aURL == "about:" ? "About:" : aURL
-            }
-          ]
-        };
-
-        testAccessibleTree(this.DOMNode, accTree);
-      };
-
-      this.getID = function changeIframeSrc_getID() {
-        return "change iframe src on " + aURL;
-      };
-
-      function getIframeDoc() {
-        return getAccessible(getNode(aIdentifier).contentDocument);
-      }
-    }
-
-    const kHide = 1;
-    const kShow = 2;
-    const kRemove = 3;
-    function morphIFrame(aIdentifier, aAction) {
-      this.DOMNode = getNode(aIdentifier);
-      this.IFrameContainerDOMNode = this.DOMNode.parentNode;
-
-      this.eventSeq = [];
-
-      var checker = null;
-      if (aAction == kShow)
-        checker = new invokerChecker(EVENT_SHOW, this.DOMNode);
-      else
-        checker = new invokerChecker(EVENT_HIDE, this.DOMNode);
-      this.eventSeq.push(checker);
-
-      var reorderChecker =
-        new invokerChecker(EVENT_REORDER, this.IFrameContainerDOMNode);
-      this.eventSeq.push(reorderChecker);
-
-      this.invoke = function morphIFrame_invoke() {
-        if (aAction == kHide) {
-          this.DOMNode.style.display = "none";
-        } else if (aAction == kShow) {
-          this.DOMNode.style.display = "block";
-        } else {
-          this.IFrameContainerDOMNode.removeChild(this.DOMNode);
-        }
-      };
-
-      this.finalCheck = function morphIFrame_finalCheck() {
-        var accTree = {
-          role: ROLE_SECTION,
-          children: (aAction == kHide || aAction == kRemove) ? [ ] :
-            [
-              {
-                role: ROLE_INTERNAL_FRAME,
-                children: [
-                  { role: ROLE_DOCUMENT }
-                ]
-              }
-            ]
-        };
-
-        testAccessibleTree(this.IFrameContainerDOMNode, accTree);
-      };
-
-      this.getID = function morphIFrame_getID() {
-        if (aAction == kRemove)
-          return "remove iframe";
-
-        return "change display style of iframe to " +
-          ((aAction == kHide) ? "none" : "block");
-      };
-    }
-
-    function makeIFrameVisible(aID) {
-      this.DOMNode = getNode(aID);
-
-      this.eventSeq = [
-        new invokerChecker(EVENT_REORDER, this.DOMNode.parentNode)
-      ];
-
-      this.invoke = function makeIFrameVisible_invoke() {
-        this.DOMNode.style.visibility = "visible";
-      };
-
-      this.getID = function makeIFrameVisible_getID() {
-        return "The accessible for DOM document loaded before it's shown shouldn't have busy state.";
-      };
-    }
-
-    function openDialogWnd(aURL) {
-      // Get application root accessible.
-      var docAcc = getAccessible(document);
-      while (docAcc) {
-        this.mRootAcc = docAcc;
-        try {
-          docAcc = docAcc.parent;
-        } catch (e) {
-          ok(false, "Can't get parent for " + prettyName(docAcc));
-          throw e;
-        }
-      }
-
-      this.eventSeq = [
-        new invokerChecker(EVENT_REORDER, this.mRootAcc)
-      ];
-
-      this.invoke = function openDialogWnd_invoke() {
-        this.mDialog = window.openDialog(aURL);
-      };
-
-      this.finalCheck = function openDialogWnd_finalCheck() {
-        this.finalCheckImpl();
-      };
-
-      this.finalCheckImpl = function openDialogWnd_finalCheckImpl() {
-        var accTree = {
-          role: ROLE_APP_ROOT,
-          children: [
-            {
-              role: ROLE_CHROME_WINDOW
-            },
-            {
-              role: ROLE_CHROME_WINDOW
-            },
-            {
-              role: ROLE_CHROME_WINDOW
-            },
-            {
-              role: ROLE_CHROME_WINDOW
-            }
-          ]
-        };
-
-        testAccessibleTree(this.mRootAcc, accTree);
-
-        var dlgDoc = this.mDialog.document;
-        ok(isAccessibleInCache(dlgDoc),
-           "The document accessible for '" + aURL + "' is not in cache!");
-
-        this.mDialog.close();
-
-        // close() is asynchronous.
-        SimpleTest.executeSoon(function() {
-          ok(!isAccessibleInCache(dlgDoc),
-             "The document accessible for '" + aURL + "' is in cache still!");
-        });
-      };
-
-      this.getID = function openDialogWnd_getID() {
-        return "open dialog '" + aURL + "'";
-      };
-    }
-
-    function openWndShutdownDoc() {
-      this.__proto__ =
-        new openDialogWnd("../events/docload_wnd.html");
-
-      var thisObj = this;
-      var docChecker = {
-        type: EVENT_HIDE,
-        get target() {
-          var iframe = this.invoker.mDialog.document.getElementById("iframe");
-          this.invoker.iframeDoc = iframe.contentDocument;
-          return iframe;
-        },
-        get targetDescr() {
-          return "inner iframe of docload_wnd.html document";
-        },
-        invoker: thisObj
-      };
-
-      this.eventSeq.push(docChecker);
-
-      this.finalCheck = function openWndShutdownDoc_finalCheck() {
-        // After timeout after event hide for iframe was handled the document
-        // accessible for iframe's document is in cache still.
-        ok(!isAccessibleInCache(this.iframeDoc),
-            "The document accessible for iframe is in cache still after iframe hide!");
-
-        this.finalCheckImpl();
-
-        // After the window is closed all alive subdocument accessibles should
-        // be shut down.
-        ok(!isAccessibleInCache(this.iframeDoc),
-           "The document accessible for iframe is in cache still!");
-      };
-    }
-
-    // //////////////////////////////////////////////////////////////////////////
-    // Do tests
-
-    var gQueue = null;
-
-    // Debug stuff.
-    // gA11yEventDumpID = "eventdump";
-    // gA11yEventDumpToConsole = true;
-
-    function doTests() {
-      gQueue = new eventQueue();
-
-      gQueue.push(new changeIframeSrc("iframe", "about:"));
-      gQueue.push(new changeIframeSrc("iframe", "about:buildconfig"));
-      gQueue.push(new morphIFrame("iframe", kHide));
-      gQueue.push(new morphIFrame("iframe", kShow));
-      gQueue.push(new morphIFrame("iframe", kRemove));
-      gQueue.push(new makeIFrameVisible("iframe2"));
-      gQueue.push(new openDialogWnd("about:"));
-      gQueue.push(new openWndShutdownDoc());
-
-      gQueue.onFinish = doLastCallTests;
-
-      gQueue.invoke(); // Will call SimpleTest.finish();
-    }
-
-    function doLastCallTests() {
-      // ////////////////////////////////////////////////////////////////////////
-      // makeIFrameVisible() test, part2
-
-      // The document shouldn't have busy state (the DOM document was loaded
-      // before its accessible was created). Do this test lately to make sure
-      // the content of document accessible was created initially, prior to this
-      // the document accessible keeps busy state. The initial creation happens
-      // asynchronously after document creation, there are no events we could
-      // use to catch it.
-      var iframeDoc = getAccessible("iframe2").firstChild;
-      testStates(iframeDoc, 0, 0, STATE_BUSY);
-    }
-
-    SimpleTest.waitForExplicitFinish();
-    addA11yLoadEvent(doTests);
-  </script>
-</head>
-
-<body>
-
-  <a target="_blank"
-     href="https://bugzilla.mozilla.org/show_bug.cgi?id=420845"
-     title="Fire event_reorder on any embedded frames/iframes whos document has just loaded">
-    Mozilla Bug 420845
-  </a><br>
-  <a target="_blank"
-     href="https://bugzilla.mozilla.org/show_bug.cgi?id=506206"
-     title="Fire event_reorder application root accessible">
-    Mozilla Bug 506206
-  </a><br>
-  <a target="_blank"
-     href="https://bugzilla.mozilla.org/show_bug.cgi?id=566103"
-     title="Reorganize accessible document handling">
-    Mozilla Bug 566103
-  </a><br>
-  <a target="_blank"
-     href="https://bugzilla.mozilla.org/show_bug.cgi?id=571459"
-     title="Shutdown document accessible when presshell goes away">
-    Mozilla Bug 571459
-  </a>
-  <a target="_blank"
-     href="https://bugzilla.mozilla.org/show_bug.cgi?id=658185"
-     title="The DOM document loaded before it's shown shouldn't have busy state">
-    Mozilla Bug 658185
-  </a>
-  <a target="_blank"
-     href="https://bugzilla.mozilla.org/show_bug.cgi?id=754165"
-     title="Fire document load events on iframes too">
-    Mozilla Bug 754165
-  </a>
-
-  <p id="display"></p>
-  <div id="content" style="display: none"></div>
-  <pre id="test">
-  </pre>
-
-  <div id="testContainer"><iframe id="iframe"></iframe></div>
-  <div id="testContainer2"><iframe id="iframe2" src="about:" style="visibility: hidden;"></iframe></div>
-  <div id="eventdump"></div>
-</body>
-</html>
deleted file mode 100644
--- a/accessible/tests/mochitest/events/test_docload_aria.html
+++ /dev/null
@@ -1,79 +0,0 @@
-<html>
-
-<head>
-  <title>Accessible events testing for ARIA document</title>
-
-  <link rel="stylesheet" type="text/css"
-        href="chrome://mochikit/content/tests/SimpleTest/test.css" />
-
-  <script type="application/javascript"
-          src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
-
-  <script type="application/javascript"
-          src="../common.js"></script>
-  <script type="application/javascript"
-          src="../role.js"></script>
-  <script type="application/javascript"
-          src="../states.js"></script>
-  <script type="application/javascript"
-          src="../events.js"></script>
-
-  <script type="application/javascript">
-    // //////////////////////////////////////////////////////////////////////////
-    // Invokers
-
-    function showARIADialog(aID) {
-      this.dialogNode = getNode(aID);
-
-      this.eventSeq = [
-        new invokerChecker(EVENT_DOCUMENT_LOAD_COMPLETE, this.dialogNode)
-      ];
-
-      this.invoke = function showARIADialog_invoke() {
-        this.dialogNode.style.display = "block";
-      };
-
-      this.getID = function showARIADialog_getID() {
-        return "show ARIA dialog";
-      };
-    }
-
-    // //////////////////////////////////////////////////////////////////////////
-    // Do tests
-
-    var gQueue = null;
-
-    // Debug stuff.
-    // gA11yEventDumpToConsole = true;
-
-    function doTests() {
-      gQueue = new eventQueue();
-
-      gQueue.push(new showARIADialog("dialog"));
-      gQueue.push(new showARIADialog("document"));
-
-      gQueue.invoke(); // Will call SimpleTest.finish();
-    }
-
-    SimpleTest.waitForExplicitFinish();
-    addA11yLoadEvent(doTests);
-  </script>
-</head>
-
-<body>
-
-  <a target="_blank"
-     href="https://bugzilla.mozilla.org/show_bug.cgi?id=759833"
-     title="ARIA documents should fire document loading events">
-    Mozilla Bug 759833
-  </a>
-
-  <p id="display"></p>
-  <div id="content" style="display: none"></div>
-  <pre id="test">
-  </pre>
-
-  <div role="dialog" id="dialog" style="display: none;">It's a dialog</div>
-  <div role="document" id="document" style="display: none;">It's a document</div>
-</body>
-</html>
--- a/accessible/tests/mochitest/moz.build
+++ b/accessible/tests/mochitest/moz.build
@@ -8,16 +8,17 @@ A11Y_MANIFESTS += [
     'a11y.ini',
     'actions/a11y.ini',
     'aom/a11y.ini',
     'attributes/a11y.ini',
     'bounds/a11y.ini',
     'editabletext/a11y.ini',
     'elm/a11y.ini',
     'events/a11y.ini',
+    'events/docload/a11y.ini',
     'focus/a11y.ini',
     'hittest/a11y.ini',
     'hyperlink/a11y.ini',
     'hypertext/a11y.ini',
     'jsat/a11y.ini',
     'name/a11y.ini',
     'pivot/a11y.ini',
     'relations/a11y.ini',
--- a/accessible/windows/ia2/ia2Accessible.cpp
+++ b/accessible/windows/ia2/ia2Accessible.cpp
@@ -37,16 +37,20 @@ template<typename String> static void Es
 STDMETHODIMP
 ia2Accessible::QueryInterface(REFIID iid, void** ppv)
 {
   if (!ppv)
     return E_INVALIDARG;
 
   *ppv = nullptr;
 
+  // NOTE: If any new versions of IAccessible2 are added here, they should
+  // also be added to the IA2 Handler in
+  // /accessible/ipc/win/handler/AccessibleHandler.cpp
+
   if (IID_IAccessible2_3 == iid)
     *ppv = static_cast<IAccessible2_3*>(this);
   else if (IID_IAccessible2_2 == iid)
     *ppv = static_cast<IAccessible2_2*>(this);
   else if (IID_IAccessible2 == iid)
     *ppv = static_cast<IAccessible2*>(this);
 
   if (*ppv) {
--- a/accessible/windows/msaa/AccessibleWrap.cpp
+++ b/accessible/windows/msaa/AccessibleWrap.cpp
@@ -38,16 +38,17 @@
 #include "nsIServiceManager.h"
 #include "nsNameSpaceManager.h"
 #include "nsTextFormatter.h"
 #include "nsView.h"
 #include "nsViewManager.h"
 #include "nsEventMap.h"
 #include "nsArrayUtils.h"
 #include "mozilla/Preferences.h"
+#include "mozilla/ReverseIterator.h"
 #include "nsIXULRuntime.h"
 #include "mozilla/mscom/AsyncInvoker.h"
 
 #include "oleacc.h"
 
 using namespace mozilla;
 using namespace mozilla::a11y;
 
@@ -1131,16 +1132,45 @@ AccessibleWrap::GetNativeInterface(void*
 
 void
 AccessibleWrap::SetID(uint32_t aID)
 {
   MOZ_ASSERT(XRE_IsParentProcess() && IsProxy());
   mID = aID;
 }
 
+static bool
+IsHandlerInvalidationNeeded(uint32_t aEvent)
+{
+  // We want to return true for any events that would indicate that something
+  // in the handler cache is out of date.
+  switch (aEvent) {
+    case EVENT_OBJECT_STATECHANGE:
+    case EVENT_OBJECT_LOCATIONCHANGE:
+    case EVENT_OBJECT_NAMECHANGE:
+    case EVENT_OBJECT_DESCRIPTIONCHANGE:
+    case EVENT_OBJECT_VALUECHANGE:
+    case IA2_EVENT_ACTION_CHANGED:
+    case IA2_EVENT_DOCUMENT_LOAD_COMPLETE:
+    case IA2_EVENT_DOCUMENT_LOAD_STOPPED:
+    case IA2_EVENT_DOCUMENT_ATTRIBUTE_CHANGED:
+    case IA2_EVENT_DOCUMENT_CONTENT_CHANGED:
+    case IA2_EVENT_PAGE_CHANGED:
+    case IA2_EVENT_TEXT_ATTRIBUTE_CHANGED:
+    case IA2_EVENT_TEXT_CHANGED:
+    case IA2_EVENT_TEXT_INSERTED:
+    case IA2_EVENT_TEXT_REMOVED:
+    case IA2_EVENT_TEXT_UPDATED:
+    case IA2_EVENT_OBJECT_ATTRIBUTE_CHANGED:
+      return true;
+    default:
+      return false;
+  }
+}
+
 void
 AccessibleWrap::FireWinEvent(Accessible* aTarget, uint32_t aEventType)
 {
   MOZ_ASSERT(XRE_IsParentProcess());
   static_assert(sizeof(gWinEventMap)/sizeof(gWinEventMap[0]) == nsIAccessibleEvent::EVENT_LAST_ENTRY,
                 "MSAA event map skewed");
 
   NS_ASSERTION(aEventType > 0 && aEventType < ArrayLength(gWinEventMap), "invalid event type");
@@ -1153,16 +1183,20 @@ AccessibleWrap::FireWinEvent(Accessible*
   if (!childID)
     return; // Can't fire an event without a child ID
 
   HWND hwnd = GetHWNDFor(aTarget);
   if (!hwnd) {
     return;
   }
 
+  if (IsHandlerInvalidationNeeded(winEvent)) {
+    InvalidateHandlers();
+  }
+
   // Fire MSAA event for client area window.
   ::NotifyWinEvent(winEvent, hwnd, OBJID_CLIENT, childID);
 
   // JAWS announces collapsed combobox navigation based on focus events.
   if (aEventType == nsIAccessibleEvent::EVENT_SELECTION &&
       Compatibility::IsJAWS()) {
     roles::Role role = aTarget->IsProxy() ? aTarget->Proxy()->Role() :
       aTarget->Role();
@@ -1654,16 +1688,48 @@ AccessibleWrap::SetHandlerControl(DWORD 
   HandlerControllerData ctrlData(aPid, Move(aCtrl));
   if (sHandlerControllers->Contains(ctrlData)) {
     return;
   }
 
   sHandlerControllers->AppendElement(Move(ctrlData));
 }
 
+/* static */
+void
+AccessibleWrap::InvalidateHandlers()
+{
+  static const HRESULT kErrorServerDied =
+    HRESULT_FROM_WIN32(RPC_S_SERVER_UNAVAILABLE);
+
+  MOZ_ASSERT(XRE_IsParentProcess());
+  MOZ_ASSERT(NS_IsMainThread());
+
+  if (!sHandlerControllers || sHandlerControllers->IsEmpty()) {
+    return;
+  }
+
+  // We iterate in reverse so that we may safely remove defunct elements while
+  // executing the loop.
+  for (auto& controller : Reversed(*sHandlerControllers)) {
+    MOZ_ASSERT(controller.mPid);
+    MOZ_ASSERT(controller.mCtrl);
+
+    ASYNC_INVOKER_FOR(IHandlerControl) invoker(controller.mCtrl,
+                                               Some(controller.mIsProxy));
+
+    HRESULT hr = ASYNC_INVOKE(invoker, Invalidate);
+
+    if (hr == CO_E_OBJNOTCONNECTED || hr == kErrorServerDied) {
+      sHandlerControllers->RemoveElement(controller);
+    } else {
+      NS_WARN_IF(FAILED(hr));
+    }
+  }
+}
 
 bool
 AccessibleWrap::DispatchTextChangeToHandler(bool aIsInsert,
                                             const nsString& aText,
                                             int32_t aStart, uint32_t aLen)
 {
   MOZ_ASSERT(XRE_IsParentProcess());
   MOZ_ASSERT(NS_IsMainThread());
--- a/accessible/windows/msaa/AccessibleWrap.h
+++ b/accessible/windows/msaa/AccessibleWrap.h
@@ -194,16 +194,18 @@ public:
   static const uint32_t kNoID = 0;
   void SetID(uint32_t aID);
 
   static uint32_t GetContentProcessIdFor(dom::ContentParentId aIPCContentId);
   static void ReleaseContentProcessIdFor(dom::ContentParentId aIPCContentId);
 
   static void SetHandlerControl(DWORD aPid, RefPtr<IHandlerControl> aCtrl);
 
+  static void InvalidateHandlers();
+
   bool DispatchTextChangeToHandler(bool aIsInsert, const nsString& aText,
                                    int32_t aStart, uint32_t aLen);
 
   static void AssignChildIDTo(NotNull<sdnAccessible*> aSdnAcc);
   static void ReleaseChildID(NotNull<sdnAccessible*> aSdnAcc);
 
 protected:
   virtual ~AccessibleWrap();
--- a/browser/base/content/test/newtab/browser.ini
+++ b/browser/base/content/test/newtab/browser.ini
@@ -26,17 +26,17 @@ skip-if = (os == "mac" || os == "win") &
 [browser_newtab_disable.js]
 [browser_newtab_drag_drop.js]
 [browser_newtab_drag_drop_ext.js]
 # temporary until determine why more intermittent on VM
 subsuite = clipboard
 [browser_newtab_drop_preview.js]
 [browser_newtab_focus.js]
 [browser_newtab_fullscreen_focus.js]
-skip-if = os == "mac" && debug # bug 1394963
+skip-if = os == "mac" # bug 1394963
 [browser_newtab_perwindow_private_browsing.js]
 [browser_newtab_reflow_load.js]
 support-files =
   content-reflows.js
 [browser_newtab_search.js]
 support-files =
   searchEngineNoLogo.xml
   searchEngineFavicon.xml
--- a/browser/base/content/test/performance/browser_urlbar_keyed_search_reflows.js
+++ b/browser/base/content/test/performance/browser_urlbar_keyed_search_reflows.js
@@ -55,16 +55,28 @@ const EXPECTED_REFLOWS_FIRST_OPEN = [
       "_reuseAcItem@chrome://global/content/bindings/autocomplete.xml",
       "_appendCurrentResult@chrome://global/content/bindings/autocomplete.xml",
       "_invalidate@chrome://global/content/bindings/autocomplete.xml",
       "invalidate@chrome://global/content/bindings/autocomplete.xml"
     ],
     times: 60, // This number should only ever go down - never up.
   },
 
+  {
+    stack: [
+      "_handleOverflow@chrome://global/content/bindings/autocomplete.xml",
+      "handleOverUnderflow@chrome://global/content/bindings/autocomplete.xml",
+      "_openAutocompletePopup@chrome://browser/content/urlbarBindings.xml",
+      "openAutocompletePopup@chrome://browser/content/urlbarBindings.xml",
+      "openPopup@chrome://global/content/bindings/autocomplete.xml",
+      "set_popupOpen@chrome://global/content/bindings/autocomplete.xml",
+    ],
+    times: 6, // This number should only ever go down - never up.
+  },
+
   // Bug 1359989
   {
     stack: [
       "openPopup@chrome://global/content/bindings/popup.xml",
       "_openAutocompletePopup@chrome://browser/content/urlbarBindings.xml",
       "openAutocompletePopup@chrome://browser/content/urlbarBindings.xml",
       "openPopup@chrome://global/content/bindings/autocomplete.xml",
       "set_popupOpen@chrome://global/content/bindings/autocomplete.xml",
--- a/browser/base/content/test/performance/browser_urlbar_search_reflows.js
+++ b/browser/base/content/test/performance/browser_urlbar_search_reflows.js
@@ -55,16 +55,28 @@ const EXPECTED_REFLOWS_FIRST_OPEN = [
       "_reuseAcItem@chrome://global/content/bindings/autocomplete.xml",
       "_appendCurrentResult@chrome://global/content/bindings/autocomplete.xml",
       "_invalidate@chrome://global/content/bindings/autocomplete.xml",
       "invalidate@chrome://global/content/bindings/autocomplete.xml"
     ],
     times: 60, // This number should only ever go down - never up.
   },
 
+  {
+    stack: [
+      "_handleOverflow@chrome://global/content/bindings/autocomplete.xml",
+      "handleOverUnderflow@chrome://global/content/bindings/autocomplete.xml",
+      "_openAutocompletePopup@chrome://browser/content/urlbarBindings.xml",
+      "openAutocompletePopup@chrome://browser/content/urlbarBindings.xml",
+      "openPopup@chrome://global/content/bindings/autocomplete.xml",
+      "set_popupOpen@chrome://global/content/bindings/autocomplete.xml",
+    ],
+    times: 6, // This number should only ever go down - never up.
+  },
+
   // Bug 1359989
   {
     stack: [
       "openPopup@chrome://global/content/bindings/popup.xml",
       "_openAutocompletePopup@chrome://browser/content/urlbarBindings.xml",
       "openAutocompletePopup@chrome://browser/content/urlbarBindings.xml",
       "openPopup@chrome://global/content/bindings/autocomplete.xml",
       "set_popupOpen@chrome://global/content/bindings/autocomplete.xml",
--- a/browser/base/content/urlbarBindings.xml
+++ b/browser/base/content/urlbarBindings.xml
@@ -1810,44 +1810,33 @@ file, You can obtain one at http://mozil
 
       <!-- This is set either to undefined or to a new object containing
            { start, end } margin values in pixels. These are used to align the
            results to the input field. -->
       <property name="margins"
                 onget="return this._margins;">
         <setter>
           <![CDATA[
-          if (val == this._margins) {
-            return val;
-          }
-
-          if (val && this._margins && val.start == this._margins.start &&
-                                      val.end == this._margins.end) {
-            return val;
-          }
-
           this._margins = val;
 
           if (val) {
             let paddingInCSS =
                 3   // .autocomplete-richlistbox padding-left/right
               + 6   // .ac-site-icon margin-inline-start
               + 16  // .ac-site-icon width
               + 6;  // .ac-site-icon margin-inline-end
             let actualVal = Math.round(val.start) - paddingInCSS;
             let actualValEnd = Math.round(val.end);
             this.style.setProperty("--item-padding-start", actualVal + "px");
             this.style.setProperty("--item-padding-end", actualValEnd + "px");
           } else {
             this.style.removeProperty("--item-padding-start");
             this.style.removeProperty("--item-padding-end");
           }
-          for (let item of this.richlistbox.childNodes) {
-            item.handleOverUnderflow();
-          }
+
           return val;
           ]]>
         </setter>
       </property>
 
       <method name="openAutocompletePopup">
         <parameter name="aInput"/>
         <parameter name="aElement"/>
@@ -1899,16 +1888,17 @@ file, You can obtain one at http://mozil
           }
 
           // Keep the popup items' site icons aligned with the urlbar's identity
           // icon if it's not too far from the edge of the window.  We define
           // "too far" as "more than 30% of the window's width AND more than
           // 250px".  Do this *before* adding any items because when the new
           // value of the margins are different from the previous value, over-
           // and underflow must be handled for each item already in the popup.
+          let needsHandleOverUnderflow = false;
           let boundToCheck = popupDirection == "rtl" ? "right" : "left";
           let inputRect = this.DOMWindowUtils.getBoundsWithoutFlushing(aInput);
           let startOffset = Math.abs(inputRect[boundToCheck] - documentRect[boundToCheck]);
           let alignSiteIcons = startOffset / width <= 0.3 || startOffset <= 250;
           if (alignSiteIcons) {
             // Calculate the end margin if we have a start margin.
             let boundToCheckEnd = popupDirection == "rtl" ? "left" : "right";
             let endOffset = Math.abs(inputRect[boundToCheckEnd] -
@@ -1917,27 +1907,30 @@ file, You can obtain one at http://mozil
               // Provide more space when aligning would result in an unbalanced
               // margin. This allows the location bar to be moved to the start
               // of the navigation toolbar to reclaim space for results.
               endOffset = startOffset;
             }
             let identityIcon = document.getElementById("identity-icon");
             let identityRect =
               this.DOMWindowUtils.getBoundsWithoutFlushing(identityIcon);
-            if (popupDirection == "rtl") {
-              this.margins = { start: documentRect.right - identityRect.right,
-                               end: endOffset };
-            } else {
-              this.margins = { start: identityRect.left,
-                               end: endOffset };
+            let start = popupDirection == "rtl" ?
+                        documentRect.right - identityRect.right :
+                        identityRect.left;
+            if (!this.margins || start != this.margins.start ||
+                                 endOffset != this.margins.end ||
+                                 width != this.margins.width) {
+              this.margins = { start, end: endOffset, width };
+              needsHandleOverUnderflow = true;
             }
-          } else {
+          } else if (this.margins) {
             // Reset the alignment so that the site icons are positioned
             // according to whatever's in the CSS.
             this.margins = undefined;
+            needsHandleOverUnderflow = true;
           }
 
           // Now that the margins have been set, start adding items (via
           // _invalidate).
           this.mInput = aInput;
           aInput.controller.setInitiallySelectedIndex(this._isFirstResultHeuristic ? 0 : -1);
           this.view = aInput.controller.QueryInterface(Components.interfaces.nsITreeView);
           this._invalidate();
@@ -1965,16 +1958,24 @@ file, You can obtain one at http://mozil
           // Position the popup below the navbar.  To get the y-coordinate,
           // which is an offset from the bottom of the input, subtract the
           // bottom of the navbar from the buttom of the input.
           let yOffset = Math.round(
             this.DOMWindowUtils.getBoundsWithoutFlushing(document.getElementById("nav-bar")).bottom -
             this.DOMWindowUtils.getBoundsWithoutFlushing(aInput).bottom);
 
           this.openPopup(aElement, "after_start", 0, yOffset, false, false);
+
+          // Do this immediately after we've requested the popup to open. This
+          // will cause sync reflows but prevents flickering.
+          if (needsHandleOverUnderflow) {
+            for (let item of this.richlistbox.childNodes) {
+              item.handleOverUnderflow();
+            }
+          }
         ]]></body>
       </method>
 
       <method name="_showSearchSuggestionsNotification">
         <parameter name="whichNotification"/>
         <parameter name="popupDirection"/>
         <body>
           <![CDATA[
--- a/browser/extensions/pdfjs/README.mozilla
+++ b/browser/extensions/pdfjs/README.mozilla
@@ -1,5 +1,5 @@
 This is the PDF.js project output, https://github.com/mozilla/pdf.js
 
-Current extension version is: 1.9.659
+Current extension version is: 1.10.88
 
-Taken from upstream commit: 3ac4baff
+Taken from upstream commit: c62a1938
--- a/browser/extensions/pdfjs/content/build/pdf.js
+++ b/browser/extensions/pdfjs/content/build/pdf.js
@@ -1985,17 +1985,17 @@ function getDocument(src, pdfDataRangeTr
     });
   }).catch(task._capability.reject);
   return task;
 }
 function _fetchDocument(worker, source, pdfDataRangeTransport, docId) {
   if (worker.destroyed) {
     return Promise.reject(new Error('Worker was destroyed'));
   }
-  let apiVersion = '1.9.659';
+  let apiVersion = '1.10.88';
   source.disableAutoFetch = (0, _dom_utils.getDefaultSetting)('disableAutoFetch');
   source.disableStream = (0, _dom_utils.getDefaultSetting)('disableStream');
   source.chunkedViewerLoading = !!pdfDataRangeTransport;
   if (pdfDataRangeTransport) {
     source.length = pdfDataRangeTransport.length;
     source.initialData = pdfDataRangeTransport.initialData;
   }
   return worker.messageHandler.sendWithPromise('GetDocRequest', {
@@ -3306,18 +3306,18 @@ var _UnsupportedManager = function Unsup
       for (var i = 0, ii = listeners.length; i < ii; i++) {
         listeners[i](featureId);
       }
     }
   };
 }();
 var version, build;
 {
-  exports.version = version = '1.9.659';
-  exports.build = build = '3ac4baff';
+  exports.version = version = '1.10.88';
+  exports.build = build = 'c62a1938';
 }
 exports.getDocument = getDocument;
 exports.LoopbackPort = LoopbackPort;
 exports.PDFDataRangeTransport = PDFDataRangeTransport;
 exports.PDFWorker = PDFWorker;
 exports.PDFDocumentProxy = PDFDocumentProxy;
 exports.PDFPageProxy = PDFPageProxy;
 exports.setPDFNetworkStreamClass = setPDFNetworkStreamClass;
@@ -5046,18 +5046,18 @@ exports.SVGGraphics = SVGGraphics;
 
 /***/ }),
 /* 9 */
 /***/ (function(module, exports, __w_pdfjs_require__) {
 
 "use strict";
 
 
-var pdfjsVersion = '1.9.659';
-var pdfjsBuild = '3ac4baff';
+var pdfjsVersion = '1.10.88';
+var pdfjsBuild = 'c62a1938';
 var pdfjsSharedUtil = __w_pdfjs_require__(0);
 var pdfjsDisplayGlobal = __w_pdfjs_require__(13);
 var pdfjsDisplayAPI = __w_pdfjs_require__(3);
 var pdfjsDisplayTextLayer = __w_pdfjs_require__(7);
 var pdfjsDisplayAnnotationLayer = __w_pdfjs_require__(6);
 var pdfjsDisplayDOMUtils = __w_pdfjs_require__(1);
 var pdfjsDisplaySVG = __w_pdfjs_require__(8);
 ;
@@ -8172,18 +8172,18 @@ var _svg = __w_pdfjs_require__(8);
 
 function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
 
 if (!_global_scope2.default.PDFJS) {
   _global_scope2.default.PDFJS = {};
 }
 var PDFJS = _global_scope2.default.PDFJS;
 {
-  PDFJS.version = '1.9.659';
-  PDFJS.build = '3ac4baff';
+  PDFJS.version = '1.10.88';
+  PDFJS.build = 'c62a1938';
 }
 PDFJS.pdfBug = false;
 if (PDFJS.verbosity !== undefined) {
   (0, _util.setVerbosityLevel)(PDFJS.verbosity);
 }
 delete PDFJS.verbosity;
 Object.defineProperty(PDFJS, 'verbosity', {
   get() {
--- a/browser/extensions/pdfjs/content/build/pdf.worker.js
+++ b/browser/extensions/pdfjs/content/build/pdf.worker.js
@@ -1686,27 +1686,25 @@ exports.isStream = isStream;
 /***/ (function(module, exports, __w_pdfjs_require__) {
 
 "use strict";
 
 
 Object.defineProperty(exports, "__esModule", {
   value: true
 });
-exports.LZWStream = exports.StringStream = exports.StreamsSequenceStream = exports.Stream = exports.RunLengthStream = exports.PredictorStream = exports.NullStream = exports.JpxStream = exports.JpegStream = exports.Jbig2Stream = exports.FlateStream = exports.DecodeStream = exports.DecryptStream = exports.CCITTFaxStream = exports.AsciiHexStream = exports.Ascii85Stream = undefined;
+exports.LZWStream = exports.StringStream = exports.StreamsSequenceStream = exports.Stream = exports.RunLengthStream = exports.PredictorStream = exports.NullStream = exports.JpxStream = exports.JpegStream = exports.FlateStream = exports.DecodeStream = exports.DecryptStream = exports.AsciiHexStream = exports.Ascii85Stream = undefined;
 
 var _util = __w_pdfjs_require__(0);
 
 var _primitives = __w_pdfjs_require__(1);
 
-var _jbig = __w_pdfjs_require__(24);
-
-var _jpg = __w_pdfjs_require__(25);
-
-var _jpx = __w_pdfjs_require__(10);
+var _jpg = __w_pdfjs_require__(24);
+
+var _jpx = __w_pdfjs_require__(9);
 
 var Stream = function StreamClosure() {
   function Stream(arrayBuffer, start, length, dict) {
     this.bytes = arrayBuffer instanceof Uint8Array ? arrayBuffer : new Uint8Array(arrayBuffer);
     this.start = start || 0;
     this.pos = this.start;
     this.end = start + length || this.bytes.length;
     this.dict = dict;
@@ -1778,21 +1776,17 @@ var Stream = function StreamClosure() {
     makeSubStream: function Stream_makeSubStream(start, length, dict) {
       return new Stream(this.bytes.buffer, start, length, dict);
     }
   };
   return Stream;
 }();
 var StringStream = function StringStreamClosure() {
   function StringStream(str) {
-    var length = str.length;
-    var bytes = new Uint8Array(length);
-    for (var n = 0; n < length; ++n) {
-      bytes[n] = str.charCodeAt(n);
-    }
+    let bytes = (0, _util.stringToBytes)(str);
     Stream.call(this, bytes);
   }
   StringStream.prototype = Stream.prototype;
   return StringStream;
 }();
 var DecodeStream = function DecodeStreamClosure() {
   var emptyBuffer = new Uint8Array(0);
   function DecodeStream(maybeMinBufferLength) {
@@ -2482,64 +2476,16 @@ var JpxStream = function JpxStreamClosur
       }
       this.buffer = data;
     }
     this.bufferLength = this.buffer.length;
     this.eof = true;
   };
   return JpxStream;
 }();
-var Jbig2Stream = function Jbig2StreamClosure() {
-  function Jbig2Stream(stream, maybeLength, dict, params) {
-    this.stream = stream;
-    this.maybeLength = maybeLength;
-    this.dict = dict;
-    this.params = params;
-    DecodeStream.call(this, maybeLength);
-  }
-  Jbig2Stream.prototype = Object.create(DecodeStream.prototype);
-  Object.defineProperty(Jbig2Stream.prototype, 'bytes', {
-    get: function Jbig2Stream_bytes() {
-      return (0, _util.shadow)(this, 'bytes', this.stream.getBytes(this.maybeLength));
-    },
-    configurable: true
-  });
-  Jbig2Stream.prototype.ensureBuffer = function Jbig2Stream_ensureBuffer(req) {
-    if (this.bufferLength) {
-      return;
-    }
-    var jbig2Image = new _jbig.Jbig2Image();
-    var chunks = [];
-    if ((0, _primitives.isDict)(this.params)) {
-      var globalsStream = this.params.get('JBIG2Globals');
-      if ((0, _primitives.isStream)(globalsStream)) {
-        var globals = globalsStream.getBytes();
-        chunks.push({
-          data: globals,
-          start: 0,
-          end: globals.length
-        });
-      }
-    }
-    chunks.push({
-      data: this.bytes,
-      start: 0,
-      end: this.bytes.length
-    });
-    var data = jbig2Image.parseChunks(chunks);
-    var dataLength = data.length;
-    for (var i = 0; i < dataLength; i++) {
-      data[i] ^= 0xFF;
-    }
-    this.buffer = data;
-    this.bufferLength = dataLength;
-    this.eof = true;
-  };
-  return Jbig2Stream;
-}();
 var DecryptStream = function DecryptStreamClosure() {
   function DecryptStream(str, maybeLength, decrypt) {
     this.str = str;
     this.dict = str.dict;
     this.decrypt = decrypt;
     this.nextChunk = null;
     this.initialized = false;
     DecodeStream.call(this, maybeLength);
@@ -2721,539 +2667,16 @@ var RunLengthStream = function RunLength
       for (var i = 0; i < n; i++) {
         buffer[bufferLength++] = b;
       }
     }
     this.bufferLength = bufferLength;
   };
   return RunLengthStream;
 }();
-var CCITTFaxStream = function CCITTFaxStreamClosure() {
-  var ccittEOL = -2;
-  var ccittEOF = -1;
-  var twoDimPass = 0;
-  var twoDimHoriz = 1;
-  var twoDimVert0 = 2;
-  var twoDimVertR1 = 3;
-  var twoDimVertL1 = 4;
-  var twoDimVertR2 = 5;
-  var twoDimVertL2 = 6;
-  var twoDimVertR3 = 7;
-  var twoDimVertL3 = 8;
-  var twoDimTable = [[-1, -1], [-1, -1], [7, twoDimVertL3], [7, twoDimVertR3], [6, twoDimVertL2], [6, twoDimVertL2], [6, twoDimVertR2], [6, twoDimVertR2], [4, twoDimPass], [4, twoDimPass], [4, twoDimPass], [4, twoDimPass], [4, twoDimPass], [4, twoDimPass], [4, twoDimPass], [4, twoDimPass], [3, twoDimHoriz], [3, twoDimHoriz], [3, twoDimHoriz], [3, twoDimHoriz], [3, twoDimHoriz], [3, twoDimHoriz], [3, twoDimHoriz], [3, twoDimHoriz], [3, twoDimHoriz], [3, twoDimHoriz], [3, twoDimHoriz], [3, twoDimHoriz], [3, twoDimHoriz], [3, twoDimHoriz], [3, twoDimHoriz], [3, twoDimHoriz], [3, twoDimVertL1], [3, twoDimVertL1], [3, twoDimVertL1], [3, twoDimVertL1], [3, twoDimVertL1], [3, twoDimVertL1], [3, twoDimVertL1], [3, twoDimVertL1], [3, twoDimVertL1], [3, twoDimVertL1], [3, twoDimVertL1], [3, twoDimVertL1], [3, twoDimVertL1], [3, twoDimVertL1], [3, twoDimVertL1], [3, twoDimVertL1], [3, twoDimVertR1], [3, twoDimVertR1], [3, twoDimVertR1], [3, twoDimVertR1], [3, twoDimVertR1], [3, twoDimVertR1], [3, twoDimVertR1], [3, twoDimVertR1], [3, twoDimVertR1], [3, twoDimVertR1], [3, twoDimVertR1], [3, twoDimVertR1], [3, twoDimVertR1], [3, twoDimVertR1], [3, twoDimVertR1], [3, twoDimVertR1], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0]];
-  var whiteTable1 = [[-1, -1], [12, ccittEOL], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [11, 1792], [11, 1792], [12, 1984], [12, 2048], [12, 2112], [12, 2176], [12, 2240], [12, 2304], [11, 1856], [11, 1856], [11, 1920], [11, 1920], [12, 2368], [12, 2432], [12, 2496], [12, 2560]];
-  var whiteTable2 = [[-1, -1], [-1, -1], [-1, -1], [-1, -1], [8, 29], [8, 29], [8, 30], [8, 30], [8, 45], [8, 45], [8, 46], [8, 46], [7, 22], [7, 22], [7, 22], [7, 22], [7, 23], [7, 23], [7, 23], [7, 23], [8, 47], [8, 47], [8, 48], [8, 48], [6, 13], [6, 13], [6, 13], [6, 13], [6, 13], [6, 13], [6, 13], [6, 13], [7, 20], [7, 20], [7, 20], [7, 20], [8, 33], [8, 33], [8, 34], [8, 34], [8, 35], [8, 35], [8, 36], [8, 36], [8, 37], [8, 37], [8, 38], [8, 38], [7, 19], [7, 19], [7, 19], [7, 19], [8, 31], [8, 31], [8, 32], [8, 32], [6, 1], [6, 1], [6, 1], [6, 1], [6, 1], [6, 1], [6, 1], [6, 1], [6, 12], [6, 12], [6, 12], [6, 12], [6, 12], [6, 12], [6, 12], [6, 12], [8, 53], [8, 53], [8, 54], [8, 54], [7, 26], [7, 26], [7, 26], [7, 26], [8, 39], [8, 39], [8, 40], [8, 40], [8, 41], [8, 41], [8, 42], [8, 42], [8, 43], [8, 43], [8, 44], [8, 44], [7, 21], [7, 21], [7, 21], [7, 21], [7, 28], [7, 28], [7, 28], [7, 28], [8, 61], [8, 61], [8, 62], [8, 62], [8, 63], [8, 63], [8, 0], [8, 0], [8, 320], [8, 320], [8, 384], [8, 384], [5, 10], [5, 10], [5, 10], [5, 10], [5, 10], [5, 10], [5, 10], [5, 10], [5, 10], [5, 10], [5, 10], [5, 10], [5, 10], [5, 10], [5, 10], [5, 10], [5, 11], [5, 11], [5, 11], [5, 11], [5, 11], [5, 11], [5, 11], [5, 11], [5, 11], [5, 11], [5, 11], [5, 11], [5, 11], [5, 11], [5, 11], [5, 11], [7, 27], [7, 27], [7, 27], [7, 27], [8, 59], [8, 59], [8, 60], [8, 60], [9, 1472], [9, 1536], [9, 1600], [9, 1728], [7, 18], [7, 18], [7, 18], [7, 18], [7, 24], [7, 24], [7, 24], [7, 24], [8, 49], [8, 49], [8, 50], [8, 50], [8, 51], [8, 51], [8, 52], [8, 52], [7, 25], [7, 25], [7, 25], [7, 25], [8, 55], [8, 55], [8, 56], [8, 56], [8, 57], [8, 57], [8, 58], [8, 58], [6, 192], [6, 192], [6, 192], [6, 192], [6, 192], [6, 192], [6, 192], [6, 192], [6, 1664], [6, 1664], [6, 1664], [6, 1664], [6, 1664], [6, 1664], [6, 1664], [6, 1664], [8, 448], [8, 448], [8, 512], [8, 512], [9, 704], [9, 768], [8, 640], [8, 640], [8, 576], [8, 576], [9, 832], [9, 896], [9, 960], [9, 1024], [9, 1088], [9, 1152], [9, 1216], [9, 1280], [9, 1344], [9, 1408], [7, 256], [7, 256], [7, 256], [7, 256], [4, 2], [4, 2], [4, 2], [4, 2], [4, 2], [4, 2], [4, 2], [4, 2], [4, 2], [4, 2], [4, 2], [4, 2], [4, 2], [4, 2], [4, 2], [4, 2], [4, 2], [4, 2], [4, 2], [4, 2], [4, 2], [4, 2], [4, 2], [4, 2], [4, 2], [4, 2], [4, 2], [4, 2], [4, 2], [4, 2], [4, 2], [4, 2], [4, 3], [4, 3], [4, 3], [4, 3], [4, 3], [4, 3], [4, 3], [4, 3], [4, 3], [4, 3], [4, 3], [4, 3], [4, 3], [4, 3], [4, 3], [4, 3], [4, 3], [4, 3], [4, 3], [4, 3], [4, 3], [4, 3], [4, 3], [4, 3], [4, 3], [4, 3], [4, 3], [4, 3], [4, 3], [4, 3], [4, 3], [4, 3], [5, 128], [5, 128], [5, 128], [5, 128], [5, 128], [5, 128], [5, 128], [5, 128], [5, 128], [5, 128], [5, 128], [5, 128], [5, 128], [5, 128], [5, 128], [5, 128], [5, 8], [5, 8], [5, 8], [5, 8], [5, 8], [5, 8], [5, 8], [5, 8], [5, 8], [5, 8], [5, 8], [5, 8], [5, 8], [5, 8], [5, 8], [5, 8], [5, 9], [5, 9], [5, 9], [5, 9], [5, 9], [5, 9], [5, 9], [5, 9], [5, 9], [5, 9], [5, 9], [5, 9], [5, 9], [5, 9], [5, 9], [5, 9], [6, 16], [6, 16], [6, 16], [6, 16], [6, 16], [6, 16], [6, 16], [6, 16], [6, 17], [6, 17], [6, 17], [6, 17], [6, 17], [6, 17], [6, 17], [6, 17], [4, 4], [4, 4], [4, 4], [4, 4], [4, 4], [4, 4], [4, 4], [4, 4], [4, 4], [4, 4], [4, 4], [4, 4], [4, 4], [4, 4], [4, 4], [4, 4], [4, 4], [4, 4], [4, 4], [4, 4], [4, 4], [4, 4], [4, 4], [4, 4], [4, 4], [4, 4], [4, 4], [4, 4], [4, 4], [4, 4], [4, 4], [4, 4], [4, 5], [4, 5], [4, 5], [4, 5], [4, 5], [4, 5], [4, 5], [4, 5], [4, 5], [4, 5], [4, 5], [4, 5], [4, 5], [4, 5], [4, 5], [4, 5], [4, 5], [4, 5], [4, 5], [4, 5], [4, 5], [4, 5], [4, 5], [4, 5], [4, 5], [4, 5], [4, 5], [4, 5], [4, 5], [4, 5], [4, 5], [4, 5], [6, 14], [6, 14], [6, 14], [6, 14], [6, 14], [6, 14], [6, 14], [6, 14], [6, 15], [6, 15], [6, 15], [6, 15], [6, 15], [6, 15], [6, 15], [6, 15], [5, 64], [5, 64], [5, 64], [5, 64], [5, 64], [5, 64], [5, 64], [5, 64], [5, 64], [5, 64], [5, 64], [5, 64], [5, 64], [5, 64], [5, 64], [5, 64], [4, 6], [4, 6], [4, 6], [4, 6], [4, 6], [4, 6], [4, 6], [4, 6], [4, 6], [4, 6], [4, 6], [4, 6], [4, 6], [4, 6], [4, 6], [4, 6], [4, 6], [4, 6], [4, 6], [4, 6], [4, 6], [4, 6], [4, 6], [4, 6], [4, 6], [4, 6], [4, 6], [4, 6], [4, 6], [4, 6], [4, 6], [4, 6], [4, 7], [4, 7], [4, 7], [4, 7], [4, 7], [4, 7], [4, 7], [4, 7], [4, 7], [4, 7], [4, 7], [4, 7], [4, 7], [4, 7], [4, 7], [4, 7], [4, 7], [4, 7], [4, 7], [4, 7], [4, 7], [4, 7], [4, 7], [4, 7], [4, 7], [4, 7], [4, 7], [4, 7], [4, 7], [4, 7], [4, 7], [4, 7]];
-  var blackTable1 = [[-1, -1], [-1, -1], [12, ccittEOL], [12, ccittEOL], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [11, 1792], [11, 1792], [11, 1792], [11, 1792], [12, 1984], [12, 1984], [12, 2048], [12, 2048], [12, 2112], [12, 2112], [12, 2176], [12, 2176], [12, 2240], [12, 2240], [12, 2304], [12, 2304], [11, 1856], [11, 1856], [11, 1856], [11, 1856], [11, 1920], [11, 1920], [11, 1920], [11, 1920], [12, 2368], [12, 2368], [12, 2432], [12, 2432], [12, 2496], [12, 2496], [12, 2560], [12, 2560], [10, 18], [10, 18], [10, 18], [10, 18], [10, 18], [10, 18], [10, 18], [10, 18], [12, 52], [12, 52], [13, 640], [13, 704], [13, 768], [13, 832], [12, 55], [12, 55], [12, 56], [12, 56], [13, 1280], [13, 1344], [13, 1408], [13, 1472], [12, 59], [12, 59], [12, 60], [12, 60], [13, 1536], [13, 1600], [11, 24], [11, 24], [11, 24], [11, 24], [11, 25], [11, 25], [11, 25], [11, 25], [13, 1664], [13, 1728], [12, 320], [12, 320], [12, 384], [12, 384], [12, 448], [12, 448], [13, 512], [13, 576], [12, 53], [12, 53], [12, 54], [12, 54], [13, 896], [13, 960], [13, 1024], [13, 1088], [13, 1152], [13, 1216], [10, 64], [10, 64], [10, 64], [10, 64], [10, 64], [10, 64], [10, 64], [10, 64]];
-  var blackTable2 = [[8, 13], [8, 13], [8, 13], [8, 13], [8, 13], [8, 13], [8, 13], [8, 13], [8, 13], [8, 13], [8, 13], [8, 13], [8, 13], [8, 13], [8, 13], [8, 13], [11, 23], [11, 23], [12, 50], [12, 51], [12, 44], [12, 45], [12, 46], [12, 47], [12, 57], [12, 58], [12, 61], [12, 256], [10, 16], [10, 16], [10, 16], [10, 16], [10, 17], [10, 17], [10, 17], [10, 17], [12, 48], [12, 49], [12, 62], [12, 63], [12, 30], [12, 31], [12, 32], [12, 33], [12, 40], [12, 41], [11, 22], [11, 22], [8, 14], [8, 14], [8, 14], [8, 14], [8, 14], [8, 14], [8, 14], [8, 14], [8, 14], [8, 14], [8, 14], [8, 14], [8, 14], [8, 14], [8, 14], [8, 14], [7, 10], [7, 10], [7, 10], [7, 10], [7, 10], [7, 10], [7, 10], [7, 10], [7, 10], [7, 10], [7, 10], [7, 10], [7, 10], [7, 10], [7, 10], [7, 10], [7, 10], [7, 10], [7, 10], [7, 10], [7, 10], [7, 10], [7, 10], [7, 10], [7, 10], [7, 10], [7, 10], [7, 10], [7, 10], [7, 10], [7, 10], [7, 10], [7, 11], [7, 11], [7, 11], [7, 11], [7, 11], [7, 11], [7, 11], [7, 11], [7, 11], [7, 11], [7, 11], [7, 11], [7, 11], [7, 11], [7, 11], [7, 11], [7, 11], [7, 11], [7, 11], [7, 11], [7, 11], [7, 11], [7, 11], [7, 11], [7, 11], [7, 11], [7, 11], [7, 11], [7, 11], [7, 11], [7, 11], [7, 11], [9, 15], [9, 15], [9, 15], [9, 15], [9, 15], [9, 15], [9, 15], [9, 15], [12, 128], [12, 192], [12, 26], [12, 27], [12, 28], [12, 29], [11, 19], [11, 19], [11, 20], [11, 20], [12, 34], [12, 35], [12, 36], [12, 37], [12, 38], [12, 39], [11, 21], [11, 21], [12, 42], [12, 43], [10, 0], [10, 0], [10, 0], [10, 0], [7, 12], [7, 12], [7, 12], [7, 12], [7, 12], [7, 12], [7, 12], [7, 12], [7, 12], [7, 12], [7, 12], [7, 12], [7, 12], [7, 12], [7, 12], [7, 12], [7, 12], [7, 12], [7, 12], [7, 12], [7, 12], [7, 12], [7, 12], [7, 12], [7, 12], [7, 12], [7, 12], [7, 12], [7, 12], [7, 12], [7, 12], [7, 12]];
-  var blackTable3 = [[-1, -1], [-1, -1], [-1, -1], [-1, -1], [6, 9], [6, 8], [5, 7], [5, 7], [4, 6], [4, 6], [4, 6], [4, 6], [4, 5], [4, 5], [4, 5], [4, 5], [3, 1], [3, 1], [3, 1], [3, 1], [3, 1], [3, 1], [3, 1], [3, 1], [3, 4], [3, 4], [3, 4], [3, 4], [3, 4], [3, 4], [3, 4], [3, 4], [2, 3], [2, 3], [2, 3], [2, 3], [2, 3], [2, 3], [2, 3], [2, 3], [2, 3], [2, 3], [2, 3], [2, 3], [2, 3], [2, 3], [2, 3], [2, 3], [2, 2], [2, 2], [2, 2], [2, 2], [2, 2], [2, 2], [2, 2], [2, 2], [2, 2], [2, 2], [2, 2], [2, 2], [2, 2], [2, 2], [2, 2], [2, 2]];
-  function CCITTFaxStream(str, maybeLength, params) {
-    this.str = str;
-    this.dict = str.dict;
-    if (!(0, _primitives.isDict)(params)) {
-      params = _primitives.Dict.empty;
-    }
-    this.encoding = params.get('K') || 0;
-    this.eoline = params.get('EndOfLine') || false;
-    this.byteAlign = params.get('EncodedByteAlign') || false;
-    this.columns = params.get('Columns') || 1728;
-    this.rows = params.get('Rows') || 0;
-    var eoblock = params.get('EndOfBlock');
-    if (eoblock === null || eoblock === undefined) {
-      eoblock = true;
-    }
-    this.eoblock = eoblock;
-    this.black = params.get('BlackIs1') || false;
-    this.codingLine = new Uint32Array(this.columns + 1);
-    this.refLine = new Uint32Array(this.columns + 2);
-    this.codingLine[0] = this.columns;
-    this.codingPos = 0;
-    this.row = 0;
-    this.nextLine2D = this.encoding < 0;
-    this.inputBits = 0;
-    this.inputBuf = 0;
-    this.outputBits = 0;
-    this.rowsDone = false;
-    var code1;
-    while ((code1 = this.lookBits(12)) === 0) {
-      this.eatBits(1);
-    }
-    if (code1 === 1) {
-      this.eatBits(12);
-    }
-    if (this.encoding > 0) {
-      this.nextLine2D = !this.lookBits(1);
-      this.eatBits(1);
-    }
-    DecodeStream.call(this, maybeLength);
-  }
-  CCITTFaxStream.prototype = Object.create(DecodeStream.prototype);
-  CCITTFaxStream.prototype.readBlock = function CCITTFaxStream_readBlock() {
-    while (!this.eof) {
-      var c = this.lookChar();
-      this.ensureBuffer(this.bufferLength + 1);
-      this.buffer[this.bufferLength++] = c;
-    }
-  };
-  CCITTFaxStream.prototype.addPixels = function ccittFaxStreamAddPixels(a1, blackPixels) {
-    var codingLine = this.codingLine;
-    var codingPos = this.codingPos;
-    if (a1 > codingLine[codingPos]) {
-      if (a1 > this.columns) {
-        (0, _util.info)('row is wrong length');
-        this.err = true;
-        a1 = this.columns;
-      }
-      if (codingPos & 1 ^ blackPixels) {
-        ++codingPos;
-      }
-      codingLine[codingPos] = a1;
-    }
-    this.codingPos = codingPos;
-  };
-  CCITTFaxStream.prototype.addPixelsNeg = function ccittFaxStreamAddPixelsNeg(a1, blackPixels) {
-    var codingLine = this.codingLine;
-    var codingPos = this.codingPos;
-    if (a1 > codingLine[codingPos]) {
-      if (a1 > this.columns) {
-        (0, _util.info)('row is wrong length');
-        this.err = true;
-        a1 = this.columns;
-      }
-      if (codingPos & 1 ^ blackPixels) {
-        ++codingPos;
-      }
-      codingLine[codingPos] = a1;
-    } else if (a1 < codingLine[codingPos]) {
-      if (a1 < 0) {
-        (0, _util.info)('invalid code');
-        this.err = true;
-        a1 = 0;
-      }
-      while (codingPos > 0 && a1 < codingLine[codingPos - 1]) {
-        --codingPos;
-      }
-      codingLine[codingPos] = a1;
-    }
-    this.codingPos = codingPos;
-  };
-  CCITTFaxStream.prototype.lookChar = function CCITTFaxStream_lookChar() {
-    var refLine = this.refLine;
-    var codingLine = this.codingLine;
-    var columns = this.columns;
-    var refPos, blackPixels, bits, i;
-    if (this.outputBits === 0) {
-      if (this.rowsDone) {
-        this.eof = true;
-      }
-      if (this.eof) {
-        return null;
-      }
-      this.err = false;
-      var code1, code2, code3;
-      if (this.nextLine2D) {
-        for (i = 0; codingLine[i] < columns; ++i) {
-          refLine[i] = codingLine[i];
-        }
-        refLine[i++] = columns;
-        refLine[i] = columns;
-        codingLine[0] = 0;
-        this.codingPos = 0;
-        refPos = 0;
-        blackPixels = 0;
-        while (codingLine[this.codingPos] < columns) {
-          code1 = this.getTwoDimCode();
-          switch (code1) {
-            case twoDimPass:
-              this.addPixels(refLine[refPos + 1], blackPixels);
-              if (refLine[refPos + 1] < columns) {
-                refPos += 2;
-              }
-              break;
-            case twoDimHoriz:
-              code1 = code2 = 0;
-              if (blackPixels) {
-                do {
-                  code1 += code3 = this.getBlackCode();
-                } while (code3 >= 64);
-                do {
-                  code2 += code3 = this.getWhiteCode();
-                } while (code3 >= 64);
-              } else {
-                do {
-                  code1 += code3 = this.getWhiteCode();
-                } while (code3 >= 64);
-                do {
-                  code2 += code3 = this.getBlackCode();
-                } while (code3 >= 64);
-              }
-              this.addPixels(codingLine[this.codingPos] + code1, blackPixels);
-              if (codingLine[this.codingPos] < columns) {
-                this.addPixels(codingLine[this.codingPos] + code2, blackPixels ^ 1);
-              }
-              while (refLine[refPos] <= codingLine[this.codingPos] && refLine[refPos] < columns) {
-                refPos += 2;
-              }
-              break;
-            case twoDimVertR3:
-              this.addPixels(refLine[refPos] + 3, blackPixels);
-              blackPixels ^= 1;
-              if (codingLine[this.codingPos] < columns) {
-                ++refPos;
-                while (refLine[refPos] <= codingLine[this.codingPos] && refLine[refPos] < columns) {
-                  refPos += 2;
-                }
-              }
-              break;
-            case twoDimVertR2:
-              this.addPixels(refLine[refPos] + 2, blackPixels);
-              blackPixels ^= 1;
-              if (codingLine[this.codingPos] < columns) {
-                ++refPos;
-                while (refLine[refPos] <= codingLine[this.codingPos] && refLine[refPos] < columns) {
-                  refPos += 2;
-                }
-              }
-              break;
-            case twoDimVertR1:
-              this.addPixels(refLine[refPos] + 1, blackPixels);
-              blackPixels ^= 1;
-              if (codingLine[this.codingPos] < columns) {
-                ++refPos;
-                while (refLine[refPos] <= codingLine[this.codingPos] && refLine[refPos] < columns) {
-                  refPos += 2;
-                }
-              }
-              break;
-            case twoDimVert0:
-              this.addPixels(refLine[refPos], blackPixels);
-              blackPixels ^= 1;
-              if (codingLine[this.codingPos] < columns) {
-                ++refPos;
-                while (refLine[refPos] <= codingLine[this.codingPos] && refLine[refPos] < columns) {
-                  refPos += 2;
-                }
-              }
-              break;
-            case twoDimVertL3:
-              this.addPixelsNeg(refLine[refPos] - 3, blackPixels);
-              blackPixels ^= 1;
-              if (codingLine[this.codingPos] < columns) {
-                if (refPos > 0) {
-                  --refPos;
-                } else {
-                  ++refPos;
-                }
-                while (refLine[refPos] <= codingLine[this.codingPos] && refLine[refPos] < columns) {
-                  refPos += 2;
-                }
-              }
-              break;
-            case twoDimVertL2:
-              this.addPixelsNeg(refLine[refPos] - 2, blackPixels);
-              blackPixels ^= 1;
-              if (codingLine[this.codingPos] < columns) {
-                if (refPos > 0) {
-                  --refPos;
-                } else {
-                  ++refPos;
-                }
-                while (refLine[refPos] <= codingLine[this.codingPos] && refLine[refPos] < columns) {
-                  refPos += 2;
-                }
-              }
-              break;
-            case twoDimVertL1:
-              this.addPixelsNeg(refLine[refPos] - 1, blackPixels);
-              blackPixels ^= 1;
-              if (codingLine[this.codingPos] < columns) {
-                if (refPos > 0) {
-                  --refPos;
-                } else {
-                  ++refPos;
-                }
-                while (refLine[refPos] <= codingLine[this.codingPos] && refLine[refPos] < columns) {
-                  refPos += 2;
-                }
-              }
-              break;
-            case ccittEOF:
-              this.addPixels(columns, 0);
-              this.eof = true;
-              break;
-            default:
-              (0, _util.info)('bad 2d code');
-              this.addPixels(columns, 0);
-              this.err = true;
-          }
-        }
-      } else {
-        codingLine[0] = 0;
-        this.codingPos = 0;
-        blackPixels = 0;
-        while (codingLine[this.codingPos] < columns) {
-          code1 = 0;
-          if (blackPixels) {
-            do {
-              code1 += code3 = this.getBlackCode();
-            } while (code3 >= 64);
-          } else {
-            do {
-              code1 += code3 = this.getWhiteCode();
-            } while (code3 >= 64);
-          }
-          this.addPixels(codingLine[this.codingPos] + code1, blackPixels);
-          blackPixels ^= 1;
-        }
-      }
-      var gotEOL = false;
-      if (this.byteAlign) {
-        this.inputBits &= ~7;
-      }
-      if (!this.eoblock && this.row === this.rows - 1) {
-        this.rowsDone = true;
-      } else {
-        code1 = this.lookBits(12);
-        if (this.eoline) {
-          while (code1 !== ccittEOF && code1 !== 1) {
-            this.eatBits(1);
-            code1 = this.lookBits(12);
-          }
-        } else {
-          while (code1 === 0) {
-            this.eatBits(1);
-            code1 = this.lookBits(12);
-          }
-        }
-        if (code1 === 1) {
-          this.eatBits(12);
-          gotEOL = true;
-        } else if (code1 === ccittEOF) {
-          this.eof = true;
-        }
-      }
-      if (!this.eof && this.encoding > 0 && !this.rowsDone) {
-        this.nextLine2D = !this.lookBits(1);
-        this.eatBits(1);
-      }
-      if (this.eoblock && gotEOL && this.byteAlign) {
-        code1 = this.lookBits(12);
-        if (code1 === 1) {
-          this.eatBits(12);
-          if (this.encoding > 0) {
-            this.lookBits(1);
-            this.eatBits(1);
-          }
-          if (this.encoding >= 0) {
-            for (i = 0; i < 4; ++i) {
-              code1 = this.lookBits(12);
-              if (code1 !== 1) {
-                (0, _util.info)('bad rtc code: ' + code1);
-              }
-              this.eatBits(12);
-              if (this.encoding > 0) {
-                this.lookBits(1);
-                this.eatBits(1);
-              }
-            }
-          }
-          this.eof = true;
-        }
-      } else if (this.err && this.eoline) {
-        while (true) {
-          code1 = this.lookBits(13);
-          if (code1 === ccittEOF) {
-            this.eof = true;
-            return null;
-          }
-          if (code1 >> 1 === 1) {
-            break;
-          }
-          this.eatBits(1);
-        }
-        this.eatBits(12);
-        if (this.encoding > 0) {
-          this.eatBits(1);
-          this.nextLine2D = !(code1 & 1);
-        }
-      }
-      if (codingLine[0] > 0) {
-        this.outputBits = codingLine[this.codingPos = 0];
-      } else {
-        this.outputBits = codingLine[this.codingPos = 1];
-      }
-      this.row++;
-    }
-    var c;
-    if (this.outputBits >= 8) {
-      c = this.codingPos & 1 ? 0 : 0xFF;
-      this.outputBits -= 8;
-      if (this.outputBits === 0 && codingLine[this.codingPos] < columns) {
-        this.codingPos++;
-        this.outputBits = codingLine[this.codingPos] - codingLine[this.codingPos - 1];
-      }
-    } else {
-      bits = 8;
-      c = 0;
-      do {
-        if (this.outputBits > bits) {
-          c <<= bits;
-          if (!(this.codingPos & 1)) {
-            c |= 0xFF >> 8 - bits;
-          }
-          this.outputBits -= bits;
-          bits = 0;
-        } else {
-          c <<= this.outputBits;
-          if (!(this.codingPos & 1)) {
-            c |= 0xFF >> 8 - this.outputBits;
-          }
-          bits -= this.outputBits;
-          this.outputBits = 0;
-          if (codingLine[this.codingPos] < columns) {
-            this.codingPos++;
-            this.outputBits = codingLine[this.codingPos] - codingLine[this.codingPos - 1];
-          } else if (bits > 0) {
-            c <<= bits;
-            bits = 0;
-          }
-        }
-      } while (bits);
-    }
-    if (this.black) {
-      c ^= 0xFF;
-    }
-    return c;
-  };
-  CCITTFaxStream.prototype.findTableCode = function ccittFaxStreamFindTableCode(start, end, table, limit) {
-    var limitValue = limit || 0;
-    for (var i = start; i <= end; ++i) {
-      var code = this.lookBits(i);
-      if (code === ccittEOF) {
-        return [true, 1, false];
-      }
-      if (i < end) {
-        code <<= end - i;
-      }
-      if (!limitValue || code >= limitValue) {
-        var p = table[code - limitValue];
-        if (p[0] === i) {
-          this.eatBits(i);
-          return [true, p[1], true];
-        }
-      }
-    }
-    return [false, 0, false];
-  };
-  CCITTFaxStream.prototype.getTwoDimCode = function ccittFaxStreamGetTwoDimCode() {
-    var code = 0;
-    var p;
-    if (this.eoblock) {
-      code = this.lookBits(7);
-      p = twoDimTable[code];
-      if (p && p[0] > 0) {
-        this.eatBits(p[0]);
-        return p[1];
-      }
-    } else {
-      var result = this.findTableCode(1, 7, twoDimTable);
-      if (result[0] && result[2]) {
-        return result[1];
-      }
-    }
-    (0, _util.info)('Bad two dim code');
-    return ccittEOF;
-  };
-  CCITTFaxStream.prototype.getWhiteCode = function ccittFaxStreamGetWhiteCode() {
-    var code = 0;
-    var p;
-    if (this.eoblock) {
-      code = this.lookBits(12);
-      if (code === ccittEOF) {
-        return 1;
-      }
-      if (code >> 5 === 0) {
-        p = whiteTable1[code];
-      } else {
-        p = whiteTable2[code >> 3];
-      }
-      if (p[0] > 0) {
-        this.eatBits(p[0]);
-        return p[1];
-      }
-    } else {
-      var result = this.findTableCode(1, 9, whiteTable2);
-      if (result[0]) {
-        return result[1];
-      }
-      result = this.findTableCode(11, 12, whiteTable1);
-      if (result[0]) {
-        return result[1];
-      }
-    }
-    (0, _util.info)('bad white code');
-    this.eatBits(1);
-    return 1;
-  };
-  CCITTFaxStream.prototype.getBlackCode = function ccittFaxStreamGetBlackCode() {
-    var code, p;
-    if (this.eoblock) {
-      code = this.lookBits(13);
-      if (code === ccittEOF) {
-        return 1;
-      }
-      if (code >> 7 === 0) {
-        p = blackTable1[code];
-      } else if (code >> 9 === 0 && code >> 7 !== 0) {
-        p = blackTable2[(code >> 1) - 64];
-      } else {
-        p = blackTable3[code >> 7];
-      }
-      if (p[0] > 0) {
-        this.eatBits(p[0]);
-        return p[1];
-      }
-    } else {
-      var result = this.findTableCode(2, 6, blackTable3);
-      if (result[0]) {
-        return result[1];
-      }
-      result = this.findTableCode(7, 12, blackTable2, 64);
-      if (result[0]) {
-        return result[1];
-      }
-      result = this.findTableCode(10, 13, blackTable1);
-      if (result[0]) {
-        return result[1];
-      }
-    }
-    (0, _util.info)('bad black code');
-    this.eatBits(1);
-    return 1;
-  };
-  CCITTFaxStream.prototype.lookBits = function CCITTFaxStream_lookBits(n) {
-    var c;
-    while (this.inputBits < n) {
-      if ((c = this.str.getByte()) === -1) {
-        if (this.inputBits === 0) {
-          return ccittEOF;
-        }
-        return this.inputBuf << n - this.inputBits & 0xFFFF >> 16 - n;
-      }
-      this.inputBuf = this.inputBuf << 8 | c;
-      this.inputBits += 8;
-    }
-    return this.inputBuf >> this.inputBits - n & 0xFFFF >> 16 - n;
-  };
-  CCITTFaxStream.prototype.eatBits = function CCITTFaxStream_eatBits(n) {
-    if ((this.inputBits -= n) < 0) {
-      this.inputBits = 0;
-    }
-  };
-  return CCITTFaxStream;
-}();
 var LZWStream = function LZWStreamClosure() {
   function LZWStream(str, maybeLength, earlyChange) {
     this.str = str;
     this.dict = str.dict;
     this.cachedData = 0;
     this.bitsCached = 0;
     var maxLzwDictionarySize = 4096;
     var lzwState = {
@@ -3369,21 +2792,19 @@ var NullStream = function NullStreamClos
   function NullStream() {
     Stream.call(this, new Uint8Array(0));
   }
   NullStream.prototype = Stream.prototype;
   return NullStream;
 }();
 exports.Ascii85Stream = Ascii85Stream;
 exports.AsciiHexStream = AsciiHexStream;
-exports.CCITTFaxStream = CCITTFaxStream;
 exports.DecryptStream = DecryptStream;
 exports.DecodeStream = DecodeStream;
 exports.FlateStream = FlateStream;
-exports.Jbig2Stream = Jbig2Stream;
 exports.JpegStream = JpegStream;
 exports.JpxStream = JpxStream;
 exports.NullStream = NullStream;
 exports.PredictorStream = PredictorStream;
 exports.RunLengthStream = RunLengthStream;
 exports.Stream = Stream;
 exports.StreamsSequenceStream = StreamsSequenceStream;
 exports.StringStream = StringStream;
@@ -4370,16 +3791,20 @@ Object.defineProperty(exports, "__esModu
 exports.Parser = exports.Linearization = exports.Lexer = undefined;
 
 var _stream = __w_pdfjs_require__(2);
 
 var _util = __w_pdfjs_require__(0);
 
 var _primitives = __w_pdfjs_require__(1);
 
+var _ccitt_stream = __w_pdfjs_require__(25);
+
+var _jbig2_stream = __w_pdfjs_require__(27);
+
 var MAX_LENGTH_TO_CACHE = 1000;
 var Parser = function ParserClosure() {
   function Parser(lexer, allowStreams, xref, recoveryMode) {
     this.lexer = lexer;
     this.allowStreams = allowStreams;
     this.xref = xref;
     this.recoveryMode = recoveryMode || false;
     this.imageCache = Object.create(null);
@@ -4865,25 +4290,25 @@ var Parser = function ParserClosure() {
           return new _stream.Ascii85Stream(stream, maybeLength);
         }
         if (name === 'ASCIIHexDecode' || name === 'AHx') {
           xrefStreamStats[_util.StreamType.AHX] = true;
           return new _stream.AsciiHexStream(stream, maybeLength);
         }
         if (name === 'CCITTFaxDecode' || name === 'CCF') {
           xrefStreamStats[_util.StreamType.CCF] = true;
-          return new _stream.CCITTFaxStream(stream, maybeLength, params);
+          return new _ccitt_stream.CCITTFaxStream(stream, maybeLength, params);
         }
         if (name === 'RunLengthDecode' || name === 'RL') {
           xrefStreamStats[_util.StreamType.RL] = true;
           return new _stream.RunLengthStream(stream, maybeLength);
         }
         if (name === 'JBIG2Decode') {
           xrefStreamStats[_util.StreamType.JBIG] = true;
-          return new _stream.Jbig2Stream(stream, maybeLength, stream.dict, params);
+          return new _jbig2_stream.Jbig2Stream(stream, maybeLength, stream.dict, params);
         }
         (0, _util.warn)('filter "' + name + '" not supported yet');
         return stream;
       } catch (ex) {
         if (ex instanceof _util.MissingDataException) {
           throw ex;
         }
         (0, _util.warn)('Invalid stream: \"' + ex + '\"');
@@ -11835,361 +11260,21 @@ exports.FileSpec = FileSpec;
 /***/ (function(module, exports, __w_pdfjs_require__) {
 
 "use strict";
 
 
 Object.defineProperty(exports, "__esModule", {
   value: true
 });
-var ArithmeticDecoder = function ArithmeticDecoderClosure() {
-  var QeTable = [{
-    qe: 0x5601,
-    nmps: 1,
-    nlps: 1,
-    switchFlag: 1
-  }, {
-    qe: 0x3401,
-    nmps: 2,
-    nlps: 6,
-    switchFlag: 0
-  }, {
-    qe: 0x1801,
-    nmps: 3,
-    nlps: 9,
-    switchFlag: 0
-  }, {
-    qe: 0x0AC1,
-    nmps: 4,
-    nlps: 12,
-    switchFlag: 0
-  }, {
-    qe: 0x0521,
-    nmps: 5,
-    nlps: 29,
-    switchFlag: 0
-  }, {
-    qe: 0x0221,
-    nmps: 38,
-    nlps: 33,
-    switchFlag: 0
-  }, {
-    qe: 0x5601,
-    nmps: 7,
-    nlps: 6,
-    switchFlag: 1
-  }, {
-    qe: 0x5401,
-    nmps: 8,
-    nlps: 14,
-    switchFlag: 0
-  }, {
-    qe: 0x4801,
-    nmps: 9,
-    nlps: 14,
-    switchFlag: 0
-  }, {
-    qe: 0x3801,
-    nmps: 10,
-    nlps: 14,
-    switchFlag: 0
-  }, {
-    qe: 0x3001,
-    nmps: 11,
-    nlps: 17,
-    switchFlag: 0
-  }, {
-    qe: 0x2401,
-    nmps: 12,
-    nlps: 18,
-    switchFlag: 0
-  }, {
-    qe: 0x1C01,
-    nmps: 13,
-    nlps: 20,
-    switchFlag: 0
-  }, {
-    qe: 0x1601,
-    nmps: 29,
-    nlps: 21,
-    switchFlag: 0
-  }, {
-    qe: 0x5601,
-    nmps: 15,
-    nlps: 14,
-    switchFlag: 1
-  }, {
-    qe: 0x5401,
-    nmps: 16,
-    nlps: 14,
-    switchFlag: 0
-  }, {
-    qe: 0x5101,
-    nmps: 17,
-    nlps: 15,
-    switchFlag: 0
-  }, {
-    qe: 0x4801,
-    nmps: 18,
-    nlps: 16,
-    switchFlag: 0
-  }, {
-    qe: 0x3801,
-    nmps: 19,
-    nlps: 17,
-    switchFlag: 0
-  }, {
-    qe: 0x3401,
-    nmps: 20,
-    nlps: 18,
-    switchFlag: 0
-  }, {
-    qe: 0x3001,
-    nmps: 21,
-    nlps: 19,
-    switchFlag: 0
-  }, {
-    qe: 0x2801,
-    nmps: 22,
-    nlps: 19,
-    switchFlag: 0
-  }, {
-    qe: 0x2401,
-    nmps: 23,
-    nlps: 20,
-    switchFlag: 0
-  }, {
-    qe: 0x2201,
-    nmps: 24,
-    nlps: 21,
-    switchFlag: 0
-  }, {
-    qe: 0x1C01,
-    nmps: 25,
-    nlps: 22,
-    switchFlag: 0
-  }, {
-    qe: 0x1801,
-    nmps: 26,
-    nlps: 23,
-    switchFlag: 0
-  }, {
-    qe: 0x1601,
-    nmps: 27,
-    nlps: 24,
-    switchFlag: 0
-  }, {
-    qe: 0x1401,
-    nmps: 28,
-    nlps: 25,
-    switchFlag: 0
-  }, {
-    qe: 0x1201,
-    nmps: 29,
-    nlps: 26,
-    switchFlag: 0
-  }, {
-    qe: 0x1101,
-    nmps: 30,
-    nlps: 27,
-    switchFlag: 0
-  }, {
-    qe: 0x0AC1,
-    nmps: 31,
-    nlps: 28,
-    switchFlag: 0
-  }, {
-    qe: 0x09C1,
-    nmps: 32,
-    nlps: 29,
-    switchFlag: 0
-  }, {
-    qe: 0x08A1,
-    nmps: 33,
-    nlps: 30,
-    switchFlag: 0
-  }, {
-    qe: 0x0521,
-    nmps: 34,
-    nlps: 31,
-    switchFlag: 0
-  }, {
-    qe: 0x0441,
-    nmps: 35,
-    nlps: 32,
-    switchFlag: 0
-  }, {
-    qe: 0x02A1,
-    nmps: 36,
-    nlps: 33,
-    switchFlag: 0
-  }, {
-    qe: 0x0221,
-    nmps: 37,
-    nlps: 34,
-    switchFlag: 0
-  }, {
-    qe: 0x0141,
-    nmps: 38,
-    nlps: 35,
-    switchFlag: 0
-  }, {
-    qe: 0x0111,
-    nmps: 39,
-    nlps: 36,
-    switchFlag: 0
-  }, {
-    qe: 0x0085,
-    nmps: 40,
-    nlps: 37,
-    switchFlag: 0
-  }, {
-    qe: 0x0049,
-    nmps: 41,
-    nlps: 38,
-    switchFlag: 0
-  }, {
-    qe: 0x0025,
-    nmps: 42,
-    nlps: 39,
-    switchFlag: 0
-  }, {
-    qe: 0x0015,
-    nmps: 43,
-    nlps: 40,
-    switchFlag: 0
-  }, {
-    qe: 0x0009,
-    nmps: 44,
-    nlps: 41,
-    switchFlag: 0
-  }, {
-    qe: 0x0005,
-    nmps: 45,
-    nlps: 42,
-    switchFlag: 0
-  }, {
-    qe: 0x0001,
-    nmps: 45,
-    nlps: 43,
-    switchFlag: 0
-  }, {
-    qe: 0x5601,
-    nmps: 46,
-    nlps: 46,
-    switchFlag: 0
-  }];
-  function ArithmeticDecoder(data, start, end) {
-    this.data = data;
-    this.bp = start;
-    this.dataEnd = end;
-    this.chigh = data[start];
-    this.clow = 0;
-    this.byteIn();
-    this.chigh = this.chigh << 7 & 0xFFFF | this.clow >> 9 & 0x7F;
-    this.clow = this.clow << 7 & 0xFFFF;
-    this.ct -= 7;
-    this.a = 0x8000;
-  }
-  ArithmeticDecoder.prototype = {
-    byteIn: function ArithmeticDecoder_byteIn() {
-      var data = this.data;
-      var bp = this.bp;
-      if (data[bp] === 0xFF) {
-        var b1 = data[bp + 1];
-        if (b1 > 0x8F) {
-          this.clow += 0xFF00;
-          this.ct = 8;
-        } else {
-          bp++;
-          this.clow += data[bp] << 9;
-          this.ct = 7;
-          this.bp = bp;
-        }
-      } else {
-        bp++;
-        this.clow += bp < this.dataEnd ? data[bp] << 8 : 0xFF00;
-        this.ct = 8;
-        this.bp = bp;
-      }
-      if (this.clow > 0xFFFF) {
-        this.chigh += this.clow >> 16;
-        this.clow &= 0xFFFF;
-      }
-    },
-    readBit: function ArithmeticDecoder_readBit(contexts, pos) {
-      var cx_index = contexts[pos] >> 1,
-          cx_mps = contexts[pos] & 1;
-      var qeTableIcx = QeTable[cx_index];
-      var qeIcx = qeTableIcx.qe;
-      var d;
-      var a = this.a - qeIcx;
-      if (this.chigh < qeIcx) {
-        if (a < qeIcx) {
-          a = qeIcx;
-          d = cx_mps;
-          cx_index = qeTableIcx.nmps;
-        } else {
-          a = qeIcx;
-          d = 1 ^ cx_mps;
-          if (qeTableIcx.switchFlag === 1) {
-            cx_mps = d;
-          }
-          cx_index = qeTableIcx.nlps;
-        }
-      } else {
-        this.chigh -= qeIcx;
-        if ((a & 0x8000) !== 0) {
-          this.a = a;
-          return cx_mps;
-        }
-        if (a < qeIcx) {
-          d = 1 ^ cx_mps;
-          if (qeTableIcx.switchFlag === 1) {
-            cx_mps = d;
-          }
-          cx_index = qeTableIcx.nlps;
-        } else {
-          d = cx_mps;
-          cx_index = qeTableIcx.nmps;
-        }
-      }
-      do {
-        if (this.ct === 0) {
-          this.byteIn();
-        }
-        a <<= 1;
-        this.chigh = this.chigh << 1 & 0xFFFF | this.clow >> 15 & 1;
-        this.clow = this.clow << 1 & 0xFFFF;
-        this.ct--;
-      } while ((a & 0x8000) === 0);
-      this.a = a;
-      contexts[pos] = cx_index << 1 | cx_mps;
-      return d;
-    }
-  };
-  return ArithmeticDecoder;
-}();
-exports.ArithmeticDecoder = ArithmeticDecoder;
-
-/***/ }),
-/* 10 */
-/***/ (function(module, exports, __w_pdfjs_require__) {
-
-"use strict";
-
-
-Object.defineProperty(exports, "__esModule", {
-  value: true
-});
 exports.JpxImage = undefined;
 
 var _util = __w_pdfjs_require__(0);
 
-var _arithmetic_decoder = __w_pdfjs_require__(9);
+var _arithmetic_decoder = __w_pdfjs_require__(10);
 
 let JpxError = function JpxErrorClosure() {
   function JpxError(msg) {
     this.message = 'JPX error: ' + msg;
   }
   JpxError.prototype = new Error();
   JpxError.prototype.name = 'JpxError';
   JpxError.constructor = JpxError;
@@ -14093,16 +13178,356 @@ var JpxImage = function JpxImageClosure(
     };
     return ReversibleTransform;
   }();
   return JpxImage;
 }();
 exports.JpxImage = JpxImage;
 
 /***/ }),
+/* 10 */
+/***/ (function(module, exports, __w_pdfjs_require__) {
+
+"use strict";
+
+
+Object.defineProperty(exports, "__esModule", {
+  value: true
+});
+var ArithmeticDecoder = function ArithmeticDecoderClosure() {
+  var QeTable = [{
+    qe: 0x5601,
+    nmps: 1,
+    nlps: 1,
+    switchFlag: 1
+  }, {
+    qe: 0x3401,
+    nmps: 2,
+    nlps: 6,
+    switchFlag: 0
+  }, {
+    qe: 0x1801,
+    nmps: 3,
+    nlps: 9,
+    switchFlag: 0
+  }, {
+    qe: 0x0AC1,
+    nmps: 4,
+    nlps: 12,
+    switchFlag: 0
+  }, {
+    qe: 0x0521,
+    nmps: 5,
+    nlps: 29,
+    switchFlag: 0
+  }, {
+    qe: 0x0221,
+    nmps: 38,
+    nlps: 33,
+    switchFlag: 0
+  }, {
+    qe: 0x5601,
+    nmps: 7,
+    nlps: 6,
+    switchFlag: 1
+  }, {
+    qe: 0x5401,
+    nmps: 8,
+    nlps: 14,
+    switchFlag: 0
+  }, {
+    qe: 0x4801,
+    nmps: 9,
+    nlps: 14,
+    switchFlag: 0
+  }, {
+    qe: 0x3801,
+    nmps: 10,
+    nlps: 14,
+    switchFlag: 0
+  }, {
+    qe: 0x3001,
+    nmps: 11,
+    nlps: 17,
+    switchFlag: 0
+  }, {
+    qe: 0x2401,
+    nmps: 12,
+    nlps: 18,
+    switchFlag: 0
+  }, {
+    qe: 0x1C01,
+    nmps: 13,
+    nlps: 20,
+    switchFlag: 0
+  }, {
+    qe: 0x1601,
+    nmps: 29,
+    nlps: 21,
+    switchFlag: 0
+  }, {
+    qe: 0x5601,
+    nmps: 15,
+    nlps: 14,
+    switchFlag: 1
+  }, {
+    qe: 0x5401,
+    nmps: 16,
+    nlps: 14,
+    switchFlag: 0
+  }, {
+    qe: 0x5101,
+    nmps: 17,
+    nlps: 15,
+    switchFlag: 0
+  }, {
+    qe: 0x4801,
+    nmps: 18,
+    nlps: 16,
+    switchFlag: 0
+  }, {
+    qe: 0x3801,
+    nmps: 19,
+    nlps: 17,
+    switchFlag: 0
+  }, {
+    qe: 0x3401,
+    nmps: 20,
+    nlps: 18,
+    switchFlag: 0
+  }, {
+    qe: 0x3001,
+    nmps: 21,
+    nlps: 19,
+    switchFlag: 0
+  }, {
+    qe: 0x2801,
+    nmps: 22,
+    nlps: 19,
+    switchFlag: 0
+  }, {
+    qe: 0x2401,
+    nmps: 23,
+    nlps: 20,
+    switchFlag: 0
+  }, {
+    qe: 0x2201,
+    nmps: 24,
+    nlps: 21,
+    switchFlag: 0
+  }, {
+    qe: 0x1C01,
+    nmps: 25,
+    nlps: 22,
+    switchFlag: 0
+  }, {
+    qe: 0x1801,
+    nmps: 26,
+    nlps: 23,
+    switchFlag: 0
+  }, {
+    qe: 0x1601,
+    nmps: 27,
+    nlps: 24,
+    switchFlag: 0
+  }, {
+    qe: 0x1401,
+    nmps: 28,
+    nlps: 25,
+    switchFlag: 0
+  }, {
+    qe: 0x1201,
+    nmps: 29,
+    nlps: 26,
+    switchFlag: 0
+  }, {
+    qe: 0x1101,
+    nmps: 30,
+    nlps: 27,
+    switchFlag: 0
+  }, {
+    qe: 0x0AC1,
+    nmps: 31,
+    nlps: 28,
+    switchFlag: 0
+  }, {
+    qe: 0x09C1,
+    nmps: 32,
+    nlps: 29,
+    switchFlag: 0
+  }, {
+    qe: 0x08A1,
+    nmps: 33,
+    nlps: 30,
+    switchFlag: 0
+  }, {
+    qe: 0x0521,
+    nmps: 34,
+    nlps: 31,
+    switchFlag: 0
+  }, {
+    qe: 0x0441,
+    nmps: 35,
+    nlps: 32,
+    switchFlag: 0
+  }, {
+    qe: 0x02A1,
+    nmps: 36,
+    nlps: 33,
+    switchFlag: 0
+  }, {
+    qe: 0x0221,
+    nmps: 37,
+    nlps: 34,
+    switchFlag: 0
+  }, {
+    qe: 0x0141,
+    nmps: 38,
+    nlps: 35,
+    switchFlag: 0
+  }, {
+    qe: 0x0111,
+    nmps: 39,
+    nlps: 36,
+    switchFlag: 0
+  }, {
+    qe: 0x0085,
+    nmps: 40,
+    nlps: 37,
+    switchFlag: 0
+  }, {
+    qe: 0x0049,
+    nmps: 41,
+    nlps: 38,
+    switchFlag: 0
+  }, {
+    qe: 0x0025,
+    nmps: 42,
+    nlps: 39,
+    switchFlag: 0
+  }, {
+    qe: 0x0015,
+    nmps: 43,
+    nlps: 40,
+    switchFlag: 0
+  }, {
+    qe: 0x0009,
+    nmps: 44,
+    nlps: 41,
+    switchFlag: 0
+  }, {
+    qe: 0x0005,
+    nmps: 45,
+    nlps: 42,
+    switchFlag: 0
+  }, {
+    qe: 0x0001,
+    nmps: 45,
+    nlps: 43,
+    switchFlag: 0
+  }, {
+    qe: 0x5601,
+    nmps: 46,
+    nlps: 46,
+    switchFlag: 0
+  }];
+  function ArithmeticDecoder(data, start, end) {
+    this.data = data;
+    this.bp = start;
+    this.dataEnd = end;
+    this.chigh = data[start];
+    this.clow = 0;
+    this.byteIn();
+    this.chigh = this.chigh << 7 & 0xFFFF | this.clow >> 9 & 0x7F;
+    this.clow = this.clow << 7 & 0xFFFF;
+    this.ct -= 7;
+    this.a = 0x8000;
+  }
+  ArithmeticDecoder.prototype = {
+    byteIn: function ArithmeticDecoder_byteIn() {
+      var data = this.data;
+      var bp = this.bp;
+      if (data[bp] === 0xFF) {
+        var b1 = data[bp + 1];
+        if (b1 > 0x8F) {
+          this.clow += 0xFF00;
+          this.ct = 8;
+        } else {
+          bp++;
+          this.clow += data[bp] << 9;
+          this.ct = 7;
+          this.bp = bp;
+        }
+      } else {
+        bp++;
+        this.clow += bp < this.dataEnd ? data[bp] << 8 : 0xFF00;
+        this.ct = 8;
+        this.bp = bp;
+      }
+      if (this.clow > 0xFFFF) {
+        this.chigh += this.clow >> 16;
+        this.clow &= 0xFFFF;
+      }
+    },
+    readBit: function ArithmeticDecoder_readBit(contexts, pos) {
+      var cx_index = contexts[pos] >> 1,
+          cx_mps = contexts[pos] & 1;
+      var qeTableIcx = QeTable[cx_index];
+      var qeIcx = qeTableIcx.qe;
+      var d;
+      var a = this.a - qeIcx;
+      if (this.chigh < qeIcx) {
+        if (a < qeIcx) {
+          a = qeIcx;
+          d = cx_mps;
+          cx_index = qeTableIcx.nmps;
+        } else {
+          a = qeIcx;
+          d = 1 ^ cx_mps;
+          if (qeTableIcx.switchFlag === 1) {
+            cx_mps = d;
+          }
+          cx_index = qeTableIcx.nlps;
+        }
+      } else {
+        this.chigh -= qeIcx;
+        if ((a & 0x8000) !== 0) {
+          this.a = a;
+          return cx_mps;
+        }
+        if (a < qeIcx) {
+          d = 1 ^ cx_mps;
+          if (qeTableIcx.switchFlag === 1) {
+            cx_mps = d;
+          }
+          cx_index = qeTableIcx.nlps;
+        } else {
+          d = cx_mps;
+          cx_index = qeTableIcx.nmps;
+        }
+      }
+      do {
+        if (this.ct === 0) {
+          this.byteIn();
+        }
+        a <<= 1;
+        this.chigh = this.chigh << 1 & 0xFFFF | this.clow >> 15 & 1;
+        this.clow = this.clow << 1 & 0xFFFF;
+        this.ct--;
+      } while ((a & 0x8000) === 0);
+      this.a = a;
+      contexts[pos] = cx_index << 1 | cx_mps;
+      return d;
+    }
+  };
+  return ArithmeticDecoder;
+}();
+exports.ArithmeticDecoder = ArithmeticDecoder;
+
+/***/ }),
 /* 11 */
 /***/ (function(module, exports, __w_pdfjs_require__) {
 
 "use strict";
 
 
 Object.defineProperty(exports, "__esModule", {
   value: true
@@ -15699,47 +15124,47 @@ exports.calculateSHA512 = calculateSHA51
 
 Object.defineProperty(exports, "__esModule", {
   value: true
 });
 exports.PartialEvaluator = exports.OperatorList = undefined;
 
 var _util = __w_pdfjs_require__(0);
 
-var _cmap = __w_pdfjs_require__(26);
+var _cmap = __w_pdfjs_require__(29);
 
 var _stream = __w_pdfjs_require__(2);
 
 var _primitives = __w_pdfjs_require__(1);
 
-var _fonts = __w_pdfjs_require__(27);
+var _fonts = __w_pdfjs_require__(30);
 
 var _encodings = __w_pdfjs_require__(4);
 
 var _unicode = __w_pdfjs_require__(15);
 
 var _standard_fonts = __w_pdfjs_require__(14);
 
-var _pattern = __w_pdfjs_require__(31);
+var _pattern = __w_pdfjs_require__(34);
 
 var _parser = __w_pdfjs_require__(5);
 
-var _bidi = __w_pdfjs_require__(32);
+var _bidi = __w_pdfjs_require__(35);
 
 var _colorspace = __w_pdfjs_require__(3);
 
 var _glyphlist = __w_pdfjs_require__(6);
 
-var _metrics = __w_pdfjs_require__(33);
+var _metrics = __w_pdfjs_require__(36);
 
 var _function = __w_pdfjs_require__(16);
 
-var _murmurhash = __w_pdfjs_require__(35);
-
-var _image = __w_pdfjs_require__(36);
+var _murmurhash = __w_pdfjs_require__(38);
+
+var _image = __w_pdfjs_require__(39);
 
 var PartialEvaluator = function PartialEvaluatorClosure() {
   const DefaultPartialEvaluatorOptions = {
     forceDataSchema: false,
     maxImageSize: -1,
     disableFontFace: false,
     nativeImageDecoderSupport: _util.NativeImageDecoding.DECODE,
     ignoreErrors: false,
@@ -18750,17 +18175,17 @@ exports.PartialEvaluator = PartialEvalua
 
 Object.defineProperty(exports, "__esModule", {
   value: true
 });
 exports.CFFCompiler = exports.CFFPrivateDict = exports.CFFTopDict = exports.CFFCharset = exports.CFFIndex = exports.CFFStrings = exports.CFFHeader = exports.CFF = exports.CFFParser = exports.CFFStandardStrings = undefined;
 
 var _util = __w_pdfjs_require__(0);
 
-var _charsets = __w_pdfjs_require__(28);
+var _charsets = __w_pdfjs_require__(31);
 
 var _encodings = __w_pdfjs_require__(4);
 
 var MAX_SUBR_NESTING = 10;
 var CFFStandardStrings = ['.notdef', 'space', 'exclam', 'quotedbl', 'numbersign', 'dollar', 'percent', 'ampersand', 'quoteright', 'parenleft', 'parenright', 'asterisk', 'plus', 'comma', 'hyphen', 'period', 'slash', 'zero', 'one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine', 'colon', 'semicolon', 'less', 'equal', 'greater', 'question', 'at', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'bracketleft', 'backslash', 'bracketright', 'asciicircum', 'underscore', 'quoteleft', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'braceleft', 'bar', 'braceright', 'asciitilde', 'exclamdown', 'cent', 'sterling', 'fraction', 'yen', 'florin', 'section', 'currency', 'quotesingle', 'quotedblleft', 'guillemotleft', 'guilsinglleft', 'guilsinglright', 'fi', 'fl', 'endash', 'dagger', 'daggerdbl', 'periodcentered', 'paragraph', 'bullet', 'quotesinglbase', 'quotedblbase', 'quotedblright', 'guillemotright', 'ellipsis', 'perthousand', 'questiondown', 'grave', 'acute', 'circumflex', 'tilde', 'macron', 'breve', 'dotaccent', 'dieresis', 'ring', 'cedilla', 'hungarumlaut', 'ogonek', 'caron', 'emdash', 'AE', 'ordfeminine', 'Lslash', 'Oslash', 'OE', 'ordmasculine', 'ae', 'dotlessi', 'lslash', 'oslash', 'oe', 'germandbls', 'onesuperior', 'logicalnot', 'mu', 'trademark', 'Eth', 'onehalf', 'plusminus', 'Thorn', 'onequarter', 'divide', 'brokenbar', 'degree', 'thorn', 'threequarters', 'twosuperior', 'registered', 'minus', 'eth', 'multiply', 'threesuperior', 'copyright', 'Aacute', 'Acircumflex', 'Adieresis', 'Agrave', 'Aring', 'Atilde', 'Ccedilla', 'Eacute', 'Ecircumflex', 'Edieresis', 'Egrave', 'Iacute', 'Icircumflex', 'Idieresis', 'Igrave', 'Ntilde', 'Oacute', 'Ocircumflex', 'Odieresis', 'Ograve', 'Otilde', 'Scaron', 'Uacute', 'Ucircumflex', 'Udieresis', 'Ugrave', 'Yacute', 'Ydieresis', 'Zcaron', 'aacute', 'acircumflex', 'adieresis', 'agrave', 'aring', 'atilde', 'ccedilla', 'eacute', 'ecircumflex', 'edieresis', 'egrave', 'iacute', 'icircumflex', 'idieresis', 'igrave', 'ntilde', 'oacute', 'ocircumflex', 'odieresis', 'ograve', 'otilde', 'scaron', 'uacute', 'ucircumflex', 'udieresis', 'ugrave', 'yacute', 'ydieresis', 'zcaron', 'exclamsmall', 'Hungarumlautsmall', 'dollaroldstyle', 'dollarsuperior', 'ampersandsmall', 'Acutesmall', 'parenleftsuperior', 'parenrightsuperior', 'twodotenleader', 'onedotenleader', 'zerooldstyle', 'oneoldstyle', 'twooldstyle', 'threeoldstyle', 'fouroldstyle', 'fiveoldstyle', 'sixoldstyle', 'sevenoldstyle', 'eightoldstyle', 'nineoldstyle', 'commasuperior', 'threequartersemdash', 'periodsuperior', 'questionsmall', 'asuperior', 'bsuperior', 'centsuperior', 'dsuperior', 'esuperior', 'isuperior', 'lsuperior', 'msuperior', 'nsuperior', 'osuperior', 'rsuperior', 'ssuperior', 'tsuperior', 'ff', 'ffi', 'ffl', 'parenleftinferior', 'parenrightinferior', 'Circumflexsmall', 'hyphensuperior', 'Gravesmall', 'Asmall', 'Bsmall', 'Csmall', 'Dsmall', 'Esmall', 'Fsmall', 'Gsmall', 'Hsmall', 'Ismall', 'Jsmall', 'Ksmall', 'Lsmall', 'Msmall', 'Nsmall', 'Osmall', 'Psmall', 'Qsmall', 'Rsmall', 'Ssmall', 'Tsmall', 'Usmall', 'Vsmall', 'Wsmall', 'Xsmall', 'Ysmall', 'Zsmall', 'colonmonetary', 'onefitted', 'rupiah', 'Tildesmall', 'exclamdownsmall', 'centoldstyle', 'Lslashsmall', 'Scaronsmall', 'Zcaronsmall', 'Dieresissmall', 'Brevesmall', 'Caronsmall', 'Dotaccentsmall', 'Macronsmall', 'figuredash', 'hypheninferior', 'Ogoneksmall', 'Ringsmall', 'Cedillasmall', 'questiondownsmall', 'oneeighth', 'threeeighths', 'fiveeighths', 'seveneighths', 'onethird', 'twothirds', 'zerosuperior', 'foursuperior', 'fivesuperior', 'sixsuperior', 'sevensuperior', 'eightsuperior', 'ninesuperior', 'zeroinferior', 'oneinferior', 'twoinferior', 'threeinferior', 'fourinferior', 'fiveinferior', 'sixinferior', 'seveninferior', 'eightinferior', 'nineinferior', 'centinferior', 'dollarinferior', 'periodinferior', 'commainferior', 'Agravesmall', 'Aacutesmall', 'Acircumflexsmall', 'Atildesmall', 'Adieresissmall', 'Aringsmall', 'AEsmall', 'Ccedillasmall', 'Egravesmall', 'Eacutesmall', 'Ecircumflexsmall', 'Edieresissmall', 'Igravesmall', 'Iacutesmall', 'Icircumflexsmall', 'Idieresissmall', 'Ethsmall', 'Ntildesmall', 'Ogravesmall', 'Oacutesmall', 'Ocircumflexsmall', 'Otildesmall', 'Odieresissmall', 'OEsmall', 'Oslashsmall', 'Ugravesmall', 'Uacutesmall', 'Ucircumflexsmall', 'Udieresissmall', 'Yacutesmall', 'Thornsmall', 'Ydieresissmall', '001.000', '001.001', '001.002', '001.003', 'Black', 'Bold', 'Book', 'Light', 'Medium', 'Regular', 'Roman', 'Semibold'];
 var CFFParser = function CFFParserClosure() {
   var CharstringValidationData = [null, {
     id: 'hstem',
@@ -19157,31 +18582,17 @@ var CFFParser = function CFFParserClosur
         obj: cffIndex,
         endPos: end
       };
     },
     parseNameIndex: function CFFParser_parseNameIndex(index) {
       var names = [];
       for (var i = 0, ii = index.count; i < ii; ++i) {
         var name = index.get(i);
-        var length = Math.min(name.length, 127);
-        var data = [];
-        for (var j = 0; j < length; ++j) {
-          var c = name[j];
-          if (j === 0 && c === 0) {
-            data[j] = c;
-            continue;
-          }
-          if (c < 33 || c > 126 || c === 91 || c === 93 || c === 40 || c === 41 || c === 123 || c === 125 || c === 60 || c === 62 || c === 47 || c === 37 || c === 35) {
-            data[j] = 95;
-            continue;
-          }
-          data[j] = c;
-        }
-        names.push((0, _util.bytesToString)(data));
+        names.push((0, _util.bytesToString)(name));
       }
       return names;
     },
     parseStringIndex: function CFFParser_parseStringIndex(index) {
       var strings = new CFFStrings();
       for (var i = 0, ii = index.count; i < ii; ++i) {
         var data = index.get(i);
         strings.add((0, _util.bytesToString)(data));
@@ -19963,17 +19374,31 @@ var CFFCompiler = function CFFCompilerCl
       return code;
     },
     compileHeader: function CFFCompiler_compileHeader(header) {
       return [header.major, header.minor, header.hdrSize, header.offSize];
     },
     compileNameIndex: function CFFCompiler_compileNameIndex(names) {
       var nameIndex = new CFFIndex();
       for (var i = 0, ii = names.length; i < ii; ++i) {
-        nameIndex.add((0, _util.stringToBytes)(names[i]));
+        var name = names[i];
+        var length = Math.min(name.length, 127);
+        var sanitizedName = new Array(length);
+        for (var j = 0; j < length; j++) {
+          var char = name[j];
+          if (char < '!' || char > '~' || char === '[' || char === ']' || char === '(' || char === ')' || char === '{' || char === '}' || char === '<' || char === '>' || char === '/' || char === '%') {
+            char = '_';
+          }
+          sanitizedName[j] = char;
+        }
+        sanitizedName = sanitizedName.join('');
+        if (sanitizedName === '') {
+          sanitizedName = 'Bad_Font_Name';
+        }
+        nameIndex.add((0, _util.stringToBytes)(sanitizedName));
       }
       return this.compileIndex(nameIndex);
     },
     compileTopDicts: function CFFCompiler_compileTopDicts(dicts, length, removeCidKeys) {
       var fontDictTrackers = [];
       var fdArrayIndex = new CFFIndex();
       for (var i = 0, ii = dicts.length; i < ii; ++i) {
         var fontDict = dicts[i];
@@ -22798,17 +22223,17 @@ Object.defineProperty(exports, "__esModu
   value: true
 });
 exports.PostScriptCompiler = exports.PostScriptEvaluator = exports.PDFFunctionFactory = exports.isPDFFunction = undefined;
 
 var _util = __w_pdfjs_require__(0);
 
 var _primitives = __w_pdfjs_require__(1);
 
-var _ps_parser = __w_pdfjs_require__(34);
+var _ps_parser = __w_pdfjs_require__(37);
 
 let IsEvalSupportedCached = {
   get value() {
     return (0, _util.shadow)(this, 'value', (0, _util.isEvalSupported)());
   }
 };
 class PDFFunctionFactory {
   constructor({ xref, isEvalSupported = true }) {
@@ -23845,18 +23270,18 @@ exports.PostScriptCompiler = PostScriptC
 
 /***/ }),
 /* 17 */
 /***/ (function(module, exports, __w_pdfjs_require__) {
 
 "use strict";
 
 
-var pdfjsVersion = '1.9.659';
-var pdfjsBuild = '3ac4baff';
+var pdfjsVersion = '1.10.88';
+var pdfjsBuild = 'c62a1938';
 var pdfjsCoreWorker = __w_pdfjs_require__(18);
 exports.WorkerMessageHandler = pdfjsCoreWorker.WorkerMessageHandler;
 
 /***/ }),
 /* 18 */
 /***/ (function(module, exports, __w_pdfjs_require__) {
 
 "use strict";
@@ -24041,17 +23466,17 @@ var WorkerMessageHandler = {
     });
   },
   createDocumentHandler(docParams, port) {
     var pdfManager;
     var terminated = false;
     var cancelXHRs = null;
     var WorkerTasks = [];
     let apiVersion = docParams.apiVersion;
-    let workerVersion = '1.9.659';
+    let workerVersion = '1.10.88';
     if (apiVersion !== null && apiVersion !== workerVersion) {
       throw new Error(`The API version "${apiVersion}" does not match ` + `the Worker version "${workerVersion}".`);
     }
     var docId = docParams.docId;
     var docBaseUrl = docParams.docBaseUrl;
     var workerHandlerName = docParams.docId + '_worker';
     var handler = new _util.MessageHandler(workerHandlerName, docId, port);
     handler.postMessageTransfers = docParams.postMessageTransfers;
@@ -27684,17 +27109,17 @@ var _obj = __w_pdfjs_require__(8);
 var _primitives = __w_pdfjs_require__(1);
 
 var _util = __w_pdfjs_require__(0);
 
 var _stream = __w_pdfjs_require__(2);
 
 var _evaluator = __w_pdfjs_require__(12);
 
-var _annotation = __w_pdfjs_require__(37);
+var _annotation = __w_pdfjs_require__(40);
 
 var _crypto = __w_pdfjs_require__(11);
 
 var _parser = __w_pdfjs_require__(5);
 
 var _function = __w_pdfjs_require__(16);
 
 var Page = function PageClosure() {
@@ -28193,21 +27618,1599 @@ exports.PDFDocument = PDFDocument;
 /***/ (function(module, exports, __w_pdfjs_require__) {
 
 "use strict";
 
 
 Object.defineProperty(exports, "__esModule", {
   value: true
 });
+exports.JpegImage = undefined;
+
+var _util = __w_pdfjs_require__(0);
+
+let JpegError = function JpegErrorClosure() {
+  function JpegError(msg) {
+    this.message = 'JPEG error: ' + msg;
+  }
+  JpegError.prototype = new Error();
+  JpegError.prototype.name = 'JpegError';
+  JpegError.constructor = JpegError;
+  return JpegError;
+}();
+var JpegImage = function JpegImageClosure() {
+  var dctZigZag = new Uint8Array([0, 1, 8, 16, 9, 2, 3, 10, 17, 24, 32, 25, 18, 11, 4, 5, 12, 19, 26, 33, 40, 48, 41, 34, 27, 20, 13, 6, 7, 14, 21, 28, 35, 42, 49, 56, 57, 50, 43, 36, 29, 22, 15, 23, 30, 37, 44, 51, 58, 59, 52, 45, 38, 31, 39, 46, 53, 60, 61, 54, 47, 55, 62, 63]);
+  var dctCos1 = 4017;
+  var dctSin1 = 799;
+  var dctCos3 = 3406;
+  var dctSin3 = 2276;
+  var dctCos6 = 1567;
+  var dctSin6 = 3784;
+  var dctSqrt2 = 5793;
+  var dctSqrt1d2 = 2896;
+  function JpegImage() {
+    this.decodeTransform = null;
+    this.colorTransform = -1;
+  }
+  function buildHuffmanTable(codeLengths, values) {
+    var k = 0,
+        code = [],
+        i,
+        j,
+        length = 16;
+    while (length > 0 && !codeLengths[length - 1]) {
+      length--;
+    }
+    code.push({
+      children: [],
+      index: 0
+    });
+    var p = code[0],
+        q;
+    for (i = 0; i < length; i++) {
+      for (j = 0; j < codeLengths[i]; j++) {
+        p = code.pop();
+        p.children[p.index] = values[k];
+        while (p.index > 0) {
+          p = code.pop();
+        }
+        p.index++;
+        code.push(p);
+        while (code.length <= i) {
+          code.push(q = {
+            children: [],
+            index: 0
+          });
+          p.children[p.index] = q.children;
+          p = q;
+        }
+        k++;
+      }
+      if (i + 1 < length) {
+        code.push(q = {
+          children: [],
+          index: 0
+        });
+        p.children[p.index] = q.children;
+        p = q;
+      }
+    }
+    return code[0].children;
+  }
+  function getBlockBufferOffset(component, row, col) {
+    return 64 * ((component.blocksPerLine + 1) * row + col);
+  }
+  function decodeScan(data, offset, frame, components, resetInterval, spectralStart, spectralEnd, successivePrev, successive) {
+    var mcusPerLine = frame.mcusPerLine;
+    var progressive = frame.progressive;
+    var startOffset = offset,
+        bitsData = 0,
+        bitsCount = 0;
+    function readBit() {
+      if (bitsCount > 0) {
+        bitsCount--;
+        return bitsData >> bitsCount & 1;
+      }
+      bitsData = data[offset++];
+      if (bitsData === 0xFF) {
+        var nextByte = data[offset++];
+        if (nextByte) {
+          throw new JpegError(`unexpected marker ${(bitsData << 8 | nextByte).toString(16)}`);
+        }
+      }
+      bitsCount = 7;
+      return bitsData >>> 7;
+    }
+    function decodeHuffman(tree) {
+      var node = tree;
+      while (true) {
+        node = node[readBit()];
+        if (typeof node === 'number') {
+          return node;
+        }
+        if (typeof node !== 'object') {
+          throw new JpegError('invalid huffman sequence');
+        }
+      }
+    }
+    function receive(length) {
+      var n = 0;
+      while (length > 0) {
+        n = n << 1 | readBit();
+        length--;
+      }
+      return n;
+    }
+    function receiveAndExtend(length) {
+      if (length === 1) {
+        return readBit() === 1 ? 1 : -1;
+      }
+      var n = receive(length);
+      if (n >= 1 << length - 1) {
+        return n;
+      }
+      return n + (-1 << length) + 1;
+    }
+    function decodeBaseline(component, offset) {
+      var t = decodeHuffman(component.huffmanTableDC);
+      var diff = t === 0 ? 0 : receiveAndExtend(t);
+      component.blockData[offset] = component.pred += diff;
+      var k = 1;
+      while (k < 64) {
+        var rs = decodeHuffman(component.huffmanTableAC);
+        var s = rs & 15,
+            r = rs >> 4;
+        if (s === 0) {
+          if (r < 15) {
+            break;
+          }
+          k += 16;
+          continue;
+        }
+        k += r;
+        var z = dctZigZag[k];
+        component.blockData[offset + z] = receiveAndExtend(s);
+        k++;
+      }
+    }
+    function decodeDCFirst(component, offset) {
+      var t = decodeHuffman(component.huffmanTableDC);
+      var diff = t === 0 ? 0 : receiveAndExtend(t) << successive;
+      component.blockData[offset] = component.pred += diff;
+    }
+    function decodeDCSuccessive(component, offset) {
+      component.blockData[offset] |= readBit() << successive;
+    }
+    var eobrun = 0;
+    function decodeACFirst(component, offset) {
+      if (eobrun > 0) {
+        eobrun--;
+        return;
+      }
+      var k = spectralStart,
+          e = spectralEnd;
+      while (k <= e) {
+        var rs = decodeHuffman(component.huffmanTableAC);
+        var s = rs & 15,
+            r = rs >> 4;
+        if (s === 0) {
+          if (r < 15) {
+            eobrun = receive(r) + (1 << r) - 1;
+            break;
+          }
+          k += 16;
+          continue;
+        }
+        k += r;
+        var z = dctZigZag[k];
+        component.blockData[offset + z] = receiveAndExtend(s) * (1 << successive);
+        k++;
+      }
+    }
+    var successiveACState = 0,
+        successiveACNextValue;
+    function decodeACSuccessive(component, offset) {
+      var k = spectralStart;
+      var e = spectralEnd;
+      var r = 0;
+      var s;
+      var rs;
+      while (k <= e) {
+        var z = dctZigZag[k];
+        switch (successiveACState) {
+          case 0:
+            rs = decodeHuffman(component.huffmanTableAC);
+            s = rs & 15;
+            r = rs >> 4;
+            if (s === 0) {
+              if (r < 15) {
+                eobrun = receive(r) + (1 << r);
+                successiveACState = 4;
+              } else {
+                r = 16;
+                successiveACState = 1;
+              }
+            } else {
+              if (s !== 1) {
+                throw new JpegError('invalid ACn encoding');
+              }
+              successiveACNextValue = receiveAndExtend(s);
+              successiveACState = r ? 2 : 3;
+            }
+            continue;
+          case 1:
+          case 2:
+            if (component.blockData[offset + z]) {
+              component.blockData[offset + z] += readBit() << successive;
+            } else {
+              r--;
+              if (r === 0) {
+                successiveACState = successiveACState === 2 ? 3 : 0;
+              }
+            }
+            break;
+          case 3:
+            if (component.blockData[offset + z]) {
+              component.blockData[offset + z] += readBit() << successive;
+            } else {
+              component.blockData[offset + z] = successiveACNextValue << successive;
+              successiveACState = 0;
+            }
+            break;
+          case 4:
+            if (component.blockData[offset + z]) {
+              component.blockData[offset + z] += readBit() << successive;
+            }
+            break;
+        }
+        k++;
+      }
+      if (successiveACState === 4) {
+        eobrun--;
+        if (eobrun === 0) {
+          successiveACState = 0;
+        }
+      }
+    }
+    function decodeMcu(component, decode, mcu, row, col) {
+      var mcuRow = mcu / mcusPerLine | 0;
+      var mcuCol = mcu % mcusPerLine;
+      var blockRow = mcuRow * component.v + row;
+      var blockCol = mcuCol * component.h + col;
+      var offset = getBlockBufferOffset(component, blockRow, blockCol);
+      decode(component, offset);
+    }
+    function decodeBlock(component, decode, mcu) {
+      var blockRow = mcu / component.blocksPerLine | 0;
+      var blockCol = mcu % component.blocksPerLine;
+      var offset = getBlockBufferOffset(component, blockRow, blockCol);
+      decode(component, offset);
+    }
+    var componentsLength = components.length;
+    var component, i, j, k, n;
+    var decodeFn;
+    if (progressive) {
+      if (spectralStart === 0) {
+        decodeFn = successivePrev === 0 ? decodeDCFirst : decodeDCSuccessive;
+      } else {
+        decodeFn = successivePrev === 0 ? decodeACFirst : decodeACSuccessive;
+      }
+    } else {
+      decodeFn = decodeBaseline;
+    }
+    var mcu = 0,
+        fileMarker;
+    var mcuExpected;
+    if (componentsLength === 1) {
+      mcuExpected = components[0].blocksPerLine * components[0].blocksPerColumn;
+    } else {
+      mcuExpected = mcusPerLine * frame.mcusPerColumn;
+    }
+    var h, v;
+    while (mcu < mcuExpected) {
+      var mcuToRead = resetInterval ? Math.min(mcuExpected - mcu, resetInterval) : mcuExpected;
+      for (i = 0; i < componentsLength; i++) {
+        components[i].pred = 0;
+      }
+      eobrun = 0;
+      if (componentsLength === 1) {
+        component = components[0];
+        for (n = 0; n < mcuToRead; n++) {
+          decodeBlock(component, decodeFn, mcu);
+          mcu++;
+        }
+      } else {
+        for (n = 0; n < mcuToRead; n++) {
+          for (i = 0; i < componentsLength; i++) {
+            component = components[i];
+            h = component.h;
+            v = component.v;
+            for (j = 0; j < v; j++) {
+              for (k = 0; k < h; k++) {
+                decodeMcu(component, decodeFn, mcu, j, k);
+              }
+            }
+          }
+          mcu++;
+        }
+      }
+      bitsCount = 0;
+      fileMarker = findNextFileMarker(data, offset);
+      if (fileMarker && fileMarker.invalid) {
+        (0, _util.warn)('decodeScan - unexpected MCU data, next marker is: ' + fileMarker.invalid);
+        offset = fileMarker.offset;
+      }
+      var marker = fileMarker && fileMarker.marker;
+      if (!marker || marker <= 0xFF00) {
+        throw new JpegError('marker was not found');
+      }
+      if (marker >= 0xFFD0 && marker <= 0xFFD7) {
+        offset += 2;
+      } else {
+        break;
+      }
+    }
+    fileMarker = findNextFileMarker(data, offset);
+    if (fileMarker && fileMarker.invalid) {
+      (0, _util.warn)('decodeScan - unexpected Scan data, next marker is: ' + fileMarker.invalid);
+      offset = fileMarker.offset;
+    }
+    return offset - startOffset;
+  }
+  function quantizeAndInverse(component, blockBufferOffset, p) {
+    var qt = component.quantizationTable,
+        blockData = component.blockData;
+    var v0, v1, v2, v3, v4, v5, v6, v7;
+    var p0, p1, p2, p3, p4, p5, p6, p7;
+    var t;
+    if (!qt) {
+      throw new JpegError('missing required Quantization Table.');
+    }
+    for (var row = 0; row < 64; row += 8) {
+      p0 = blockData[blockBufferOffset + row];
+      p1 = blockData[blockBufferOffset + row + 1];
+      p2 = blockData[blockBufferOffset + row + 2];
+      p3 = blockData[blockBufferOffset + row + 3];
+      p4 = blockData[blockBufferOffset + row + 4];
+      p5 = blockData[blockBufferOffset + row + 5];
+      p6 = blockData[blockBufferOffset + row + 6];
+      p7 = blockData[blockBufferOffset + row + 7];
+      p0 *= qt[row];
+      if ((p1 | p2 | p3 | p4 | p5 | p6 | p7) === 0) {
+        t = dctSqrt2 * p0 + 512 >> 10;
+        p[row] = t;
+        p[row + 1] = t;
+        p[row + 2] = t;
+        p[row + 3] = t;
+        p[row + 4] = t;
+        p[row + 5] = t;
+        p[row + 6] = t;
+        p[row + 7] = t;
+        continue;
+      }
+      p1 *= qt[row + 1];
+      p2 *= qt[row + 2];
+      p3 *= qt[row + 3];
+      p4 *= qt[row + 4];
+      p5 *= qt[row + 5];
+      p6 *= qt[row + 6];
+      p7 *= qt[row + 7];
+      v0 = dctSqrt2 * p0 + 128 >> 8;
+      v1 = dctSqrt2 * p4 + 128 >> 8;
+      v2 = p2;
+      v3 = p6;
+      v4 = dctSqrt1d2 * (p1 - p7) + 128 >> 8;
+      v7 = dctSqrt1d2 * (p1 + p7) + 128 >> 8;
+      v5 = p3 << 4;
+      v6 = p5 << 4;
+      v0 = v0 + v1 + 1 >> 1;
+      v1 = v0 - v1;
+      t = v2 * dctSin6 + v3 * dctCos6 + 128 >> 8;
+      v2 = v2 * dctCos6 - v3 * dctSin6 + 128 >> 8;
+      v3 = t;
+      v4 = v4 + v6 + 1 >> 1;
+      v6 = v4 - v6;
+      v7 = v7 + v5 + 1 >> 1;
+      v5 = v7 - v5;
+      v0 = v0 + v3 + 1 >> 1;
+      v3 = v0 - v3;
+      v1 = v1 + v2 + 1 >> 1;
+      v2 = v1 - v2;
+      t = v4 * dctSin3 + v7 * dctCos3 + 2048 >> 12;
+      v4 = v4 * dctCos3 - v7 * dctSin3 + 2048 >> 12;
+      v7 = t;
+      t = v5 * dctSin1 + v6 * dctCos1 + 2048 >> 12;
+      v5 = v5 * dctCos1 - v6 * dctSin1 + 2048 >> 12;
+      v6 = t;
+      p[row] = v0 + v7;
+      p[row + 7] = v0 - v7;
+      p[row + 1] = v1 + v6;
+      p[row + 6] = v1 - v6;
+      p[row + 2] = v2 + v5;
+      p[row + 5] = v2 - v5;
+      p[row + 3] = v3 + v4;
+      p[row + 4] = v3 - v4;
+    }
+    for (var col = 0; col < 8; ++col) {
+      p0 = p[col];
+      p1 = p[col + 8];
+      p2 = p[col + 16];
+      p3 = p[col + 24];
+      p4 = p[col + 32];
+      p5 = p[col + 40];
+      p6 = p[col + 48];
+      p7 = p[col + 56];
+      if ((p1 | p2 | p3 | p4 | p5 | p6 | p7) === 0) {
+        t = dctSqrt2 * p0 + 8192 >> 14;
+        t = t < -2040 ? 0 : t >= 2024 ? 255 : t + 2056 >> 4;
+        blockData[blockBufferOffset + col] = t;
+        blockData[blockBufferOffset + col + 8] = t;
+        blockData[blockBufferOffset + col + 16] = t;
+        blockData[blockBufferOffset + col + 24] = t;
+        blockData[blockBufferOffset + col + 32] = t;
+        blockData[blockBufferOffset + col + 40] = t;
+        blockData[blockBufferOffset + col + 48] = t;
+        blockData[blockBufferOffset + col + 56] = t;
+        continue;
+      }
+      v0 = dctSqrt2 * p0 + 2048 >> 12;
+      v1 = dctSqrt2 * p4 + 2048 >> 12;
+      v2 = p2;
+      v3 = p6;
+      v4 = dctSqrt1d2 * (p1 - p7) + 2048 >> 12;
+      v7 = dctSqrt1d2 * (p1 + p7) + 2048 >> 12;
+      v5 = p3;
+      v6 = p5;
+      v0 = (v0 + v1 + 1 >> 1) + 4112;
+      v1 = v0 - v1;
+      t = v2 * dctSin6 + v3 * dctCos6 + 2048 >> 12;
+      v2 = v2 * dctCos6 - v3 * dctSin6 + 2048 >> 12;
+      v3 = t;
+      v4 = v4 + v6 + 1 >> 1;
+      v6 = v4 - v6;
+      v7 = v7 + v5 + 1 >> 1;
+      v5 = v7 - v5;
+      v0 = v0 + v3 + 1 >> 1;
+      v3 = v0 - v3;
+      v1 = v1 + v2 + 1 >> 1;
+      v2 = v1 - v2;
+      t = v4 * dctSin3 + v7 * dctCos3 + 2048 >> 12;
+      v4 = v4 * dctCos3 - v7 * dctSin3 + 2048 >> 12;
+      v7 = t;
+      t = v5 * dctSin1 + v6 * dctCos1 + 2048 >> 12;
+      v5 = v5 * dctCos1 - v6 * dctSin1 + 2048 >> 12;
+      v6 = t;
+      p0 = v0 + v7;
+      p7 = v0 - v7;
+      p1 = v1 + v6;
+      p6 = v1 - v6;
+      p2 = v2 + v5;
+      p5 = v2 - v5;
+      p3 = v3 + v4;
+      p4 = v3 - v4;
+      p0 = p0 < 16 ? 0 : p0 >= 4080 ? 255 : p0 >> 4;
+      p1 = p1 < 16 ? 0 : p1 >= 4080 ? 255 : p1 >> 4;
+      p2 = p2 < 16 ? 0 : p2 >= 4080 ? 255 : p2 >> 4;
+      p3 = p3 < 16 ? 0 : p3 >= 4080 ? 255 : p3 >> 4;
+      p4 = p4 < 16 ? 0 : p4 >= 4080 ? 255 : p4 >> 4;
+      p5 = p5 < 16 ? 0 : p5 >= 4080 ? 255 : p5 >> 4;
+      p6 = p6 < 16 ? 0 : p6 >= 4080 ? 255 : p6 >> 4;
+      p7 = p7 < 16 ? 0 : p7 >= 4080 ? 255 : p7 >> 4;
+      blockData[blockBufferOffset + col] = p0;
+      blockData[blockBufferOffset + col + 8] = p1;
+      blockData[blockBufferOffset + col + 16] = p2;
+      blockData[blockBufferOffset + col + 24] = p3;
+      blockData[blockBufferOffset + col + 32] = p4;
+      blockData[blockBufferOffset + col + 40] = p5;
+      blockData[blockBufferOffset + col + 48] = p6;
+      blockData[blockBufferOffset + col + 56] = p7;
+    }
+  }
+  function buildComponentData(frame, component) {
+    var blocksPerLine = component.blocksPerLine;
+    var blocksPerColumn = component.blocksPerColumn;
+    var computationBuffer = new Int16Array(64);
+    for (var blockRow = 0; blockRow < blocksPerColumn; blockRow++) {
+      for (var blockCol = 0; blockCol < blocksPerLine; blockCol++) {
+        var offset = getBlockBufferOffset(component, blockRow, blockCol);
+        quantizeAndInverse(component, offset, computationBuffer);
+      }
+    }
+    return component.blockData;
+  }
+  function findNextFileMarker(data, currentPos, startPos) {
+    function peekUint16(pos) {
+      return data[pos] << 8 | data[pos + 1];
+    }
+    var maxPos = data.length - 1;
+    var newPos = startPos < currentPos ? startPos : currentPos;
+    if (currentPos >= maxPos) {
+      return null;
+    }
+    var currentMarker = peekUint16(currentPos);
+    if (currentMarker >= 0xFFC0 && currentMarker <= 0xFFFE) {
+      return {
+        invalid: null,
+        marker: currentMarker,
+        offset: currentPos
+      };
+    }
+    var newMarker = peekUint16(newPos);
+    while (!(newMarker >= 0xFFC0 && newMarker <= 0xFFFE)) {
+      if (++newPos >= maxPos) {
+        return null;
+      }
+      newMarker = peekUint16(newPos);
+    }
+    return {
+      invalid: currentMarker.toString(16),
+      marker: newMarker,
+      offset: newPos
+    };
+  }
+  JpegImage.prototype = {
+    parse: function parse(data) {
+      function readUint16() {
+        var value = data[offset] << 8 | data[offset + 1];
+        offset += 2;
+        return value;
+      }
+      function readDataBlock() {
+        var length = readUint16();
+        var endOffset = offset + length - 2;
+        var fileMarker = findNextFileMarker(data, endOffset, offset);
+        if (fileMarker && fileMarker.invalid) {
+          (0, _util.warn)('readDataBlock - incorrect length, next marker is: ' + fileMarker.invalid);
+          endOffset = fileMarker.offset;
+        }
+        var array = data.subarray(offset, endOffset);
+        offset += array.length;
+        return array;
+      }
+      function prepareComponents(frame) {
+        var mcusPerLine = Math.ceil(frame.samplesPerLine / 8 / frame.maxH);
+        var mcusPerColumn = Math.ceil(frame.scanLines / 8 / frame.maxV);
+        for (var i = 0; i < frame.components.length; i++) {
+          component = frame.components[i];
+          var blocksPerLine = Math.ceil(Math.ceil(frame.samplesPerLine / 8) * component.h / frame.maxH);
+          var blocksPerColumn = Math.ceil(Math.ceil(frame.scanLines / 8) * component.v / frame.maxV);
+          var blocksPerLineForMcu = mcusPerLine * component.h;
+          var blocksPerColumnForMcu = mcusPerColumn * component.v;
+          var blocksBufferSize = 64 * blocksPerColumnForMcu * (blocksPerLineForMcu + 1);
+          component.blockData = new Int16Array(blocksBufferSize);
+          component.blocksPerLine = blocksPerLine;
+          component.blocksPerColumn = blocksPerColumn;
+        }
+        frame.mcusPerLine = mcusPerLine;
+        frame.mcusPerColumn = mcusPerColumn;
+      }
+      var offset = 0;
+      var jfif = null;
+      var adobe = null;
+      var frame, resetInterval;
+      var quantizationTables = [];
+      var huffmanTablesAC = [],
+          huffmanTablesDC = [];
+      var fileMarker = readUint16();
+      if (fileMarker !== 0xFFD8) {
+        throw new JpegError('SOI not found');
+      }
+      fileMarker = readUint16();
+      while (fileMarker !== 0xFFD9) {
+        var i, j, l;
+        switch (fileMarker) {
+          case 0xFFE0:
+          case 0xFFE1:
+          case 0xFFE2:
+          case 0xFFE3:
+          case 0xFFE4:
+          case 0xFFE5:
+          case 0xFFE6:
+          case 0xFFE7:
+          case 0xFFE8:
+          case 0xFFE9:
+          case 0xFFEA:
+          case 0xFFEB:
+          case 0xFFEC:
+          case 0xFFED:
+          case 0xFFEE:
+          case 0xFFEF:
+          case 0xFFFE:
+            var appData = readDataBlock();
+            if (fileMarker === 0xFFE0) {
+              if (appData[0] === 0x4A && appData[1] === 0x46 && appData[2] === 0x49 && appData[3] === 0x46 && appData[4] === 0) {
+                jfif = {
+                  version: {
+                    major: appData[5],
+                    minor: appData[6]
+                  },
+                  densityUnits: appData[7],
+                  xDensity: appData[8] << 8 | appData[9],
+                  yDensity: appData[10] << 8 | appData[11],
+                  thumbWidth: appData[12],
+                  thumbHeight: appData[13],
+                  thumbData: appData.subarray(14, 14 + 3 * appData[12] * appData[13])
+                };
+              }
+            }
+            if (fileMarker === 0xFFEE) {
+              if (appData[0] === 0x41 && appData[1] === 0x64 && appData[2] === 0x6F && appData[3] === 0x62 && appData[4] === 0x65) {
+                adobe = {
+                  version: appData[5] << 8 | appData[6],
+                  flags0: appData[7] << 8 | appData[8],
+                  flags1: appData[9] << 8 | appData[10],
+                  transformCode: appData[11]
+                };
+              }
+            }
+            break;
+          case 0xFFDB:
+            var quantizationTablesLength = readUint16();
+            var quantizationTablesEnd = quantizationTablesLength + offset - 2;
+            var z;
+            while (offset < quantizationTablesEnd) {
+              var quantizationTableSpec = data[offset++];
+              var tableData = new Uint16Array(64);
+              if (quantizationTableSpec >> 4 === 0) {
+                for (j = 0; j < 64; j++) {
+                  z = dctZigZag[j];
+                  tableData[z] = data[offset++];
+                }
+              } else if (quantizationTableSpec >> 4 === 1) {
+                for (j = 0; j < 64; j++) {
+                  z = dctZigZag[j];
+                  tableData[z] = readUint16();
+                }
+              } else {
+                throw new JpegError('DQT - invalid table spec');
+              }
+              quantizationTables[quantizationTableSpec & 15] = tableData;
+            }
+            break;
+          case 0xFFC0:
+          case 0xFFC1:
+          case 0xFFC2:
+            if (frame) {
+              throw new JpegError('Only single frame JPEGs supported');
+            }
+            readUint16();
+            frame = {};
+            frame.extended = fileMarker === 0xFFC1;
+            frame.progressive = fileMarker === 0xFFC2;
+            frame.precision = data[offset++];
+            frame.scanLines = readUint16();
+            frame.samplesPerLine = readUint16();
+            frame.components = [];
+            frame.componentIds = {};
+            var componentsCount = data[offset++],
+                componentId;
+            var maxH = 0,
+                maxV = 0;
+            for (i = 0; i < componentsCount; i++) {
+              componentId = data[offset];
+              var h = data[offset + 1] >> 4;
+              var v = data[offset + 1] & 15;
+              if (maxH < h) {
+                maxH = h;
+              }
+              if (maxV < v) {
+                maxV = v;
+              }
+              var qId = data[offset + 2];
+              l = frame.components.push({
+                h,
+                v,
+                quantizationId: qId,
+                quantizationTable: null
+              });
+              frame.componentIds[componentId] = l - 1;
+              offset += 3;
+            }
+            frame.maxH = maxH;
+            frame.maxV = maxV;
+            prepareComponents(frame);
+            break;
+          case 0xFFC4:
+            var huffmanLength = readUint16();
+            for (i = 2; i < huffmanLength;) {
+              var huffmanTableSpec = data[offset++];
+              var codeLengths = new Uint8Array(16);
+              var codeLengthSum = 0;
+              for (j = 0; j < 16; j++, offset++) {
+                codeLengthSum += codeLengths[j] = data[offset];
+              }
+              var huffmanValues = new Uint8Array(codeLengthSum);
+              for (j = 0; j < codeLengthSum; j++, offset++) {
+                huffmanValues[j] = data[offset];
+              }
+              i += 17 + codeLengthSum;
+              (huffmanTableSpec >> 4 === 0 ? huffmanTablesDC : huffmanTablesAC)[huffmanTableSpec & 15] = buildHuffmanTable(codeLengths, huffmanValues);
+            }
+            break;
+          case 0xFFDD:
+            readUint16();
+            resetInterval = readUint16();
+            break;
+          case 0xFFDA:
+            readUint16();
+            var selectorsCount = data[offset++];
+            var components = [],
+                component;
+            for (i = 0; i < selectorsCount; i++) {
+              var componentIndex = frame.componentIds[data[offset++]];
+              component = frame.components[componentIndex];
+              var tableSpec = data[offset++];
+              component.huffmanTableDC = huffmanTablesDC[tableSpec >> 4];
+              component.huffmanTableAC = huffmanTablesAC[tableSpec & 15];
+              components.push(component);
+            }
+            var spectralStart = data[offset++];
+            var spectralEnd = data[offset++];
+            var successiveApproximation = data[offset++];
+            var processed = decodeScan(data, offset, frame, components, resetInterval, spectralStart, spectralEnd, successiveApproximation >> 4, successiveApproximation & 15);
+            offset += processed;
+            break;
+          case 0xFFFF:
+            if (data[offset] !== 0xFF) {
+              offset--;
+            }
+            break;
+          default:
+            if (data[offset - 3] === 0xFF && data[offset - 2] >= 0xC0 && data[offset - 2] <= 0xFE) {
+              offset -= 3;
+              break;
+            }
+            throw new JpegError('unknown marker ' + fileMarker.toString(16));
+        }
+        fileMarker = readUint16();
+      }
+      this.width = frame.samplesPerLine;
+      this.height = frame.scanLines;
+      this.jfif = jfif;
+      this.adobe = adobe;
+      this.components = [];
+      for (i = 0; i < frame.components.length; i++) {
+        component = frame.components[i];
+        var quantizationTable = quantizationTables[component.quantizationId];
+        if (quantizationTable) {
+          component.quantizationTable = quantizationTable;
+        }
+        this.components.push({
+          output: buildComponentData(frame, component),
+          scaleX: component.h / frame.maxH,
+          scaleY: component.v / frame.maxV,
+          blocksPerLine: component.blocksPerLine,
+          blocksPerColumn: component.blocksPerColumn
+        });
+      }
+      this.numComponents = this.components.length;
+    },
+    _getLinearizedBlockData: function getLinearizedBlockData(width, height) {
+      var scaleX = this.width / width,
+          scaleY = this.height / height;
+      var component, componentScaleX, componentScaleY, blocksPerScanline;
+      var x, y, i, j, k;
+      var index;
+      var offset = 0;
+      var output;
+      var numComponents = this.components.length;
+      var dataLength = width * height * numComponents;
+      var data = new Uint8ClampedArray(dataLength);
+      var xScaleBlockOffset = new Uint32Array(width);
+      var mask3LSB = 0xfffffff8;
+      for (i = 0; i < numComponents; i++) {
+        component = this.components[i];
+        componentScaleX = component.scaleX * scaleX;
+        componentScaleY = component.scaleY * scaleY;
+        offset = i;
+        output = component.output;
+        blocksPerScanline = component.blocksPerLine + 1 << 3;
+        for (x = 0; x < width; x++) {
+          j = 0 | x * componentScaleX;
+          xScaleBlockOffset[x] = (j & mask3LSB) << 3 | j & 7;
+        }
+        for (y = 0; y < height; y++) {
+          j = 0 | y * componentScaleY;
+          index = blocksPerScanline * (j & mask3LSB) | (j & 7) << 3;
+          for (x = 0; x < width; x++) {
+            data[offset] = output[index + xScaleBlockOffset[x]];
+            offset += numComponents;
+          }
+        }
+      }
+      var transform = this.decodeTransform;
+      if (transform) {
+        for (i = 0; i < dataLength;) {
+          for (j = 0, k = 0; j < numComponents; j++, i++, k += 2) {
+            data[i] = (data[i] * transform[k] >> 8) + transform[k + 1];
+          }
+        }
+      }
+      return data;
+    },
+    _isColorConversionNeeded() {
+      if (this.adobe) {
+        return !!this.adobe.transformCode;
+      }
+      if (this.numComponents === 3) {
+        if (this.colorTransform === 0) {
+          return false;
+        }
+        return true;
+      }
+      if (this.colorTransform === 1) {
+        return true;
+      }
+      return false;
+    },
+    _convertYccToRgb: function convertYccToRgb(data) {
+      var Y, Cb, Cr;
+      for (var i = 0, length = data.length; i < length; i += 3) {
+        Y = data[i];
+        Cb = data[i + 1];
+        Cr = data[i + 2];
+        data[i] = Y - 179.456 + 1.402 * Cr;
+        data[i + 1] = Y + 135.459 - 0.344 * Cb - 0.714 * Cr;
+        data[i + 2] = Y - 226.816 + 1.772 * Cb;
+      }
+      return data;
+    },
+    _convertYcckToRgb: function convertYcckToRgb(data) {
+      var Y, Cb, Cr, k;
+      var offset = 0;
+      for (var i = 0, length = data.length; i < length; i += 4) {
+        Y = data[i];
+        Cb = data[i + 1];
+        Cr = data[i + 2];
+        k = data[i + 3];
+        data[offset++] = -122.67195406894 + Cb * (-6.60635669420364e-5 * Cb + 0.000437130475926232 * Cr - 5.4080610064599e-5 * Y + 0.00048449797120281 * k - 0.154362151871126) + Cr * (-0.000957964378445773 * Cr + 0.000817076911346625 * Y - 0.00477271405408747 * k + 1.53380253221734) + Y * (0.000961250184130688 * Y - 0.00266257332283933 * k + 0.48357088451265) + k * (-0.000336197177618394 * k + 0.484791561490776);
+        data[offset++] = 107.268039397724 + Cb * (2.19927104525741e-5 * Cb - 0.000640992018297945 * Cr + 0.000659397001245577 * Y + 0.000426105652938837 * k - 0.176491792462875) + Cr * (-0.000778269941513683 * Cr + 0.00130872261408275 * Y + 0.000770482631801132 * k - 0.151051492775562) + Y * (0.00126935368114843 * Y - 0.00265090189010898 * k + 0.25802910206845) + k * (-0.000318913117588328 * k - 0.213742400323665);
+        data[offset++] = -20.810012546947 + Cb * (-0.000570115196973677 * Cb - 2.63409051004589e-5 * Cr + 0.0020741088115012 * Y - 0.00288260236853442 * k + 0.814272968359295) + Cr * (-1.53496057440975e-5 * Cr - 0.000132689043961446 * Y + 0.000560833691242812 * k - 0.195152027534049) + Y * (0.00174418132927582 * Y - 0.00255243321439347 * k + 0.116935020465145) + k * (-0.000343531996510555 * k + 0.24165260232407);
+      }
+      return data;
+    },
+    _convertYcckToCmyk: function convertYcckToCmyk(data) {
+      var Y, Cb, Cr;
+      for (var i = 0, length = data.length; i < length; i += 4) {
+        Y = data[i];
+        Cb = data[i + 1];
+        Cr = data[i + 2];
+        data[i] = 434.456 - Y - 1.402 * Cr;
+        data[i + 1] = 119.541 - Y + 0.344 * Cb + 0.714 * Cr;
+        data[i + 2] = 481.816 - Y - 1.772 * Cb;
+      }
+      return data;
+    },
+    _convertCmykToRgb: function convertCmykToRgb(data) {
+      var c, m, y, k;
+      var offset = 0;
+      var scale = 1 / 255;
+      for (var i = 0, length = data.length; i < length; i += 4) {
+        c = data[i] * scale;
+        m = data[i + 1] * scale;
+        y = data[i + 2] * scale;
+        k = data[i + 3] * scale;
+        data[offset++] = 255 + c * (-4.387332384609988 * c + 54.48615194189176 * m + 18.82290502165302 * y + 212.25662451639585 * k - 285.2331026137004) + m * (1.7149763477362134 * m - 5.6096736904047315 * y - 17.873870861415444 * k - 5.497006427196366) + y * (-2.5217340131683033 * y - 21.248923337353073 * k + 17.5119270841813) - k * (21.86122147463605 * k + 189.48180835922747);
+        data[offset++] = 255 + c * (8.841041422036149 * c + 60.118027045597366 * m + 6.871425592049007 * y + 31.159100130055922 * k - 79.2970844816548) + m * (-15.310361306967817 * m + 17.575251261109482 * y + 131.35250912493976 * k - 190.9453302588951) + y * (4.444339102852739 * y + 9.8632861493405 * k - 24.86741582555878) - k * (20.737325471181034 * k + 187.80453709719578);
+        data[offset++] = 255 + c * (0.8842522430003296 * c + 8.078677503112928 * m + 30.89978309703729 * y - 0.23883238689178934 * k - 14.183576799673286) + m * (10.49593273432072 * m + 63.02378494754052 * y + 50.606957656360734 * k - 112.23884253719248) + y * (0.03296041114873217 * y + 115.60384449646641 * k - 193.58209356861505) - k * (22.33816807309886 * k + 180.12613974708367);
+      }
+      return data;
+    },
+    getData: function getData(width, height, forceRGBoutput) {
+      if (this.numComponents > 4) {
+        throw new JpegError('Unsupported color mode');
+      }
+      var data = this._getLinearizedBlockData(width, height);
+      if (this.numComponents === 1 && forceRGBoutput) {
+        var dataLength = data.length;
+        var rgbData = new Uint8ClampedArray(dataLength * 3);
+        var offset = 0;
+        for (var i = 0; i < dataLength; i++) {
+          var grayColor = data[i];
+          rgbData[offset++] = grayColor;
+          rgbData[offset++] = grayColor;
+          rgbData[offset++] = grayColor;
+        }
+        return rgbData;
+      } else if (this.numComponents === 3 && this._isColorConversionNeeded()) {
+        return this._convertYccToRgb(data);
+      } else if (this.numComponents === 4) {
+        if (this._isColorConversionNeeded()) {
+          if (forceRGBoutput) {
+            return this._convertYcckToRgb(data);
+          }
+          return this._convertYcckToCmyk(data);
+        } else if (forceRGBoutput) {
+          return this._convertCmykToRgb(data);
+        }
+      }
+      return data;
+    }
+  };
+  return JpegImage;
+}();
+exports.JpegImage = JpegImage;
+
+/***/ }),
+/* 25 */
+/***/ (function(module, exports, __w_pdfjs_require__) {
+
+"use strict";
+
+
+Object.defineProperty(exports, "__esModule", {
+  value: true
+});
+exports.CCITTFaxStream = undefined;
+
+var _primitives = __w_pdfjs_require__(1);
+
+var _ccitt = __w_pdfjs_require__(26);
+
+var _stream = __w_pdfjs_require__(2);
+
+var CCITTFaxStream = function CCITTFaxStreamClosure() {
+  function CCITTFaxStream(str, maybeLength, params) {
+    this.str = str;
+    this.dict = str.dict;
+    if (!(0, _primitives.isDict)(params)) {
+      params = _primitives.Dict.empty;
+    }
+    const source = {
+      next() {
+        return str.getByte();
+      }
+    };
+    this.ccittFaxDecoder = new _ccitt.CCITTFaxDecoder(source, {
+      K: params.get('K'),
+      EndOfLine: params.get('EndOfLine'),
+      EncodedByteAlign: params.get('EncodedByteAlign'),
+      Columns: params.get('Columns'),
+      Rows: params.get('Rows'),
+      EndOfBlock: params.get('EndOfBlock'),
+      BlackIs1: params.get('BlackIs1')
+    });
+    _stream.DecodeStream.call(this, maybeLength);
+  }
+  CCITTFaxStream.prototype = Object.create(_stream.DecodeStream.prototype);
+  CCITTFaxStream.prototype.readBlock = function () {
+    while (!this.eof) {
+      let c = this.ccittFaxDecoder.readNextChar();
+      if (c === -1) {
+        this.eof = true;
+        return;
+      }
+      this.ensureBuffer(this.bufferLength + 1);
+      this.buffer[this.bufferLength++] = c;
+    }
+  };
+  return CCITTFaxStream;
+}();
+exports.CCITTFaxStream = CCITTFaxStream;
+
+/***/ }),
+/* 26 */
+/***/ (function(module, exports, __w_pdfjs_require__) {
+
+"use strict";
+
+
+Object.defineProperty(exports, "__esModule", {
+  value: true
+});
+exports.CCITTFaxDecoder = undefined;
+
+var _util = __w_pdfjs_require__(0);
+
+let CCITTFaxDecoder = function CCITTFaxDecoder() {
+  const ccittEOL = -2;
+  const ccittEOF = -1;
+  const twoDimPass = 0;
+  const twoDimHoriz = 1;
+  const twoDimVert0 = 2;
+  const twoDimVertR1 = 3;
+  const twoDimVertL1 = 4;
+  const twoDimVertR2 = 5;
+  const twoDimVertL2 = 6;
+  const twoDimVertR3 = 7;
+  const twoDimVertL3 = 8;
+  const twoDimTable = [[-1, -1], [-1, -1], [7, twoDimVertL3], [7, twoDimVertR3], [6, twoDimVertL2], [6, twoDimVertL2], [6, twoDimVertR2], [6, twoDimVertR2], [4, twoDimPass], [4, twoDimPass], [4, twoDimPass], [4, twoDimPass], [4, twoDimPass], [4, twoDimPass], [4, twoDimPass], [4, twoDimPass], [3, twoDimHoriz], [3, twoDimHoriz], [3, twoDimHoriz], [3, twoDimHoriz], [3, twoDimHoriz], [3, twoDimHoriz], [3, twoDimHoriz], [3, twoDimHoriz], [3, twoDimHoriz], [3, twoDimHoriz], [3, twoDimHoriz], [3, twoDimHoriz], [3, twoDimHoriz], [3, twoDimHoriz], [3, twoDimHoriz], [3, twoDimHoriz], [3, twoDimVertL1], [3, twoDimVertL1], [3, twoDimVertL1], [3, twoDimVertL1], [3, twoDimVertL1], [3, twoDimVertL1], [3, twoDimVertL1], [3, twoDimVertL1], [3, twoDimVertL1], [3, twoDimVertL1], [3, twoDimVertL1], [3, twoDimVertL1], [3, twoDimVertL1], [3, twoDimVertL1], [3, twoDimVertL1], [3, twoDimVertL1], [3, twoDimVertR1], [3, twoDimVertR1], [3, twoDimVertR1], [3, twoDimVertR1], [3, twoDimVertR1], [3, twoDimVertR1], [3, twoDimVertR1], [3, twoDimVertR1], [3, twoDimVertR1], [3, twoDimVertR1], [3, twoDimVertR1], [3, twoDimVertR1], [3, twoDimVertR1], [3, twoDimVertR1], [3, twoDimVertR1], [3, twoDimVertR1], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0], [1, twoDimVert0]];
+  const whiteTable1 = [[-1, -1], [12, ccittEOL], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [11, 1792], [11, 1792], [12, 1984], [12, 2048], [12, 2112], [12, 2176], [12, 2240], [12, 2304], [11, 1856], [11, 1856], [11, 1920], [11, 1920], [12, 2368], [12, 2432], [12, 2496], [12, 2560]];
+  const whiteTable2 = [[-1, -1], [-1, -1], [-1, -1], [-1, -1], [8, 29], [8, 29], [8, 30], [8, 30], [8, 45], [8, 45], [8, 46], [8, 46], [7, 22], [7, 22], [7, 22], [7, 22], [7, 23], [7, 23], [7, 23], [7, 23], [8, 47], [8, 47], [8, 48], [8, 48], [6, 13], [6, 13], [6, 13], [6, 13], [6, 13], [6, 13], [6, 13], [6, 13], [7, 20], [7, 20], [7, 20], [7, 20], [8, 33], [8, 33], [8, 34], [8, 34], [8, 35], [8, 35], [8, 36], [8, 36], [8, 37], [8, 37], [8, 38], [8, 38], [7, 19], [7, 19], [7, 19], [7, 19], [8, 31], [8, 31], [8, 32], [8, 32], [6, 1], [6, 1], [6, 1], [6, 1], [6, 1], [6, 1], [6, 1], [6, 1], [6, 12], [6, 12], [6, 12], [6, 12], [6, 12], [6, 12], [6, 12], [6, 12], [8, 53], [8, 53], [8, 54], [8, 54], [7, 26], [7, 26], [7, 26], [7, 26], [8, 39], [8, 39], [8, 40], [8, 40], [8, 41], [8, 41], [8, 42], [8, 42], [8, 43], [8, 43], [8, 44], [8, 44], [7, 21], [7, 21], [7, 21], [7, 21], [7, 28], [7, 28], [7, 28], [7, 28], [8, 61], [8, 61], [8, 62], [8, 62], [8, 63], [8, 63], [8, 0], [8, 0], [8, 320], [8, 320], [8, 384], [8, 384], [5, 10], [5, 10], [5, 10], [5, 10], [5, 10], [5, 10], [5, 10], [5, 10], [5, 10], [5, 10], [5, 10], [5, 10], [5, 10], [5, 10], [5, 10], [5, 10], [5, 11], [5, 11], [5, 11], [5, 11], [5, 11], [5, 11], [5, 11], [5, 11], [5, 11], [5, 11], [5, 11], [5, 11], [5, 11], [5, 11], [5, 11], [5, 11], [7, 27], [7, 27], [7, 27], [7, 27], [8, 59], [8, 59], [8, 60], [8, 60], [9, 1472], [9, 1536], [9, 1600], [9, 1728], [7, 18], [7, 18], [7, 18], [7, 18], [7, 24], [7, 24], [7, 24], [7, 24], [8, 49], [8, 49], [8, 50], [8, 50], [8, 51], [8, 51], [8, 52], [8, 52], [7, 25], [7, 25], [7, 25], [7, 25], [8, 55], [8, 55], [8, 56], [8, 56], [8, 57], [8, 57], [8, 58], [8, 58], [6, 192], [6, 192], [6, 192], [6, 192], [6, 192], [6, 192], [6, 192], [6, 192], [6, 1664], [6, 1664], [6, 1664], [6, 1664], [6, 1664], [6, 1664], [6, 1664], [6, 1664], [8, 448], [8, 448], [8, 512], [8, 512], [9, 704], [9, 768], [8, 640], [8, 640], [8, 576], [8, 576], [9, 832], [9, 896], [9, 960], [9, 1024], [9, 1088], [9, 1152], [9, 1216], [9, 1280], [9, 1344], [9, 1408], [7, 256], [7, 256], [7, 256], [7, 256], [4, 2], [4, 2], [4, 2], [4, 2], [4, 2], [4, 2], [4, 2], [4, 2], [4, 2], [4, 2], [4, 2], [4, 2], [4, 2], [4, 2], [4, 2], [4, 2], [4, 2], [4, 2], [4, 2], [4, 2], [4, 2], [4, 2], [4, 2], [4, 2], [4, 2], [4, 2], [4, 2], [4, 2], [4, 2], [4, 2], [4, 2], [4, 2], [4, 3], [4, 3], [4, 3], [4, 3], [4, 3], [4, 3], [4, 3], [4, 3], [4, 3], [4, 3], [4, 3], [4, 3], [4, 3], [4, 3], [4, 3], [4, 3], [4, 3], [4, 3], [4, 3], [4, 3], [4, 3], [4, 3], [4, 3], [4, 3], [4, 3], [4, 3], [4, 3], [4, 3], [4, 3], [4, 3], [4, 3], [4, 3], [5, 128], [5, 128], [5, 128], [5, 128], [5, 128], [5, 128], [5, 128], [5, 128], [5, 128], [5, 128], [5, 128], [5, 128], [5, 128], [5, 128], [5, 128], [5, 128], [5, 8], [5, 8], [5, 8], [5, 8], [5, 8], [5, 8], [5, 8], [5, 8], [5, 8], [5, 8], [5, 8], [5, 8], [5, 8], [5, 8], [5, 8], [5, 8], [5, 9], [5, 9], [5, 9], [5, 9], [5, 9], [5, 9], [5, 9], [5, 9], [5, 9], [5, 9], [5, 9], [5, 9], [5, 9], [5, 9], [5, 9], [5, 9], [6, 16], [6, 16], [6, 16], [6, 16], [6, 16], [6, 16], [6, 16], [6, 16], [6, 17], [6, 17], [6, 17], [6, 17], [6, 17], [6, 17], [6, 17], [6, 17], [4, 4], [4, 4], [4, 4], [4, 4], [4, 4], [4, 4], [4, 4], [4, 4], [4, 4], [4, 4], [4, 4], [4, 4], [4, 4], [4, 4], [4, 4], [4, 4], [4, 4], [4, 4], [4, 4], [4, 4], [4, 4], [4, 4], [4, 4], [4, 4], [4, 4], [4, 4], [4, 4], [4, 4], [4, 4], [4, 4], [4, 4], [4, 4], [4, 5], [4, 5], [4, 5], [4, 5], [4, 5], [4, 5], [4, 5], [4, 5], [4, 5], [4, 5], [4, 5], [4, 5], [4, 5], [4, 5], [4, 5], [4, 5], [4, 5], [4, 5], [4, 5], [4, 5], [4, 5], [4, 5], [4, 5], [4, 5], [4, 5], [4, 5], [4, 5], [4, 5], [4, 5], [4, 5], [4, 5], [4, 5], [6, 14], [6, 14], [6, 14], [6, 14], [6, 14], [6, 14], [6, 14], [6, 14], [6, 15], [6, 15], [6, 15], [6, 15], [6, 15], [6, 15], [6, 15], [6, 15], [5, 64], [5, 64], [5, 64], [5, 64], [5, 64], [5, 64], [5, 64], [5, 64], [5, 64], [5, 64], [5, 64], [5, 64], [5, 64], [5, 64], [5, 64], [5, 64], [4, 6], [4, 6], [4, 6], [4, 6], [4, 6], [4, 6], [4, 6], [4, 6], [4, 6], [4, 6], [4, 6], [4, 6], [4, 6], [4, 6], [4, 6], [4, 6], [4, 6], [4, 6], [4, 6], [4, 6], [4, 6], [4, 6], [4, 6], [4, 6], [4, 6], [4, 6], [4, 6], [4, 6], [4, 6], [4, 6], [4, 6], [4, 6], [4, 7], [4, 7], [4, 7], [4, 7], [4, 7], [4, 7], [4, 7], [4, 7], [4, 7], [4, 7], [4, 7], [4, 7], [4, 7], [4, 7], [4, 7], [4, 7], [4, 7], [4, 7], [4, 7], [4, 7], [4, 7], [4, 7], [4, 7], [4, 7], [4, 7], [4, 7], [4, 7], [4, 7], [4, 7], [4, 7], [4, 7], [4, 7]];
+  const blackTable1 = [[-1, -1], [-1, -1], [12, ccittEOL], [12, ccittEOL], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [-1, -1], [11, 1792], [11, 1792], [11, 1792], [11, 1792], [12, 1984], [12, 1984], [12, 2048], [12, 2048], [12, 2112], [12, 2112], [12, 2176], [12, 2176], [12, 2240], [12, 2240], [12, 2304], [12, 2304], [11, 1856], [11, 1856], [11, 1856], [11, 1856], [11, 1920], [11, 1920], [11, 1920], [11, 1920], [12, 2368], [12, 2368], [12, 2432], [12, 2432], [12, 2496], [12, 2496], [12, 2560], [12, 2560], [10, 18], [10, 18], [10, 18], [10, 18], [10, 18], [10, 18], [10, 18], [10, 18], [12, 52], [12, 52], [13, 640], [13, 704], [13, 768], [13, 832], [12, 55], [12, 55], [12, 56], [12, 56], [13, 1280], [13, 1344], [13, 1408], [13, 1472], [12, 59], [12, 59], [12, 60], [12, 60], [13, 1536], [13, 1600], [11, 24], [11, 24], [11, 24], [11, 24], [11, 25], [11, 25], [11, 25], [11, 25], [13, 1664], [13, 1728], [12, 320], [12, 320], [12, 384], [12, 384], [12, 448], [12, 448], [13, 512], [13, 576], [12, 53], [12, 53], [12, 54], [12, 54], [13, 896], [13, 960], [13, 1024], [13, 1088], [13, 1152], [13, 1216], [10, 64], [10, 64], [10, 64], [10, 64], [10, 64], [10, 64], [10, 64], [10, 64]];
+  const blackTable2 = [[8, 13], [8, 13], [8, 13], [8, 13], [8, 13], [8, 13], [8, 13], [8, 13], [8, 13], [8, 13], [8, 13], [8, 13], [8, 13], [8, 13], [8, 13], [8, 13], [11, 23], [11, 23], [12, 50], [12, 51], [12, 44], [12, 45], [12, 46], [12, 47], [12, 57], [12, 58], [12, 61], [12, 256], [10, 16], [10, 16], [10, 16], [10, 16], [10, 17], [10, 17], [10, 17], [10, 17], [12, 48], [12, 49], [12, 62], [12, 63], [12, 30], [12, 31], [12, 32], [12, 33], [12, 40], [12, 41], [11, 22], [11, 22], [8, 14], [8, 14], [8, 14], [8, 14], [8, 14], [8, 14], [8, 14], [8, 14], [8, 14], [8, 14], [8, 14], [8, 14], [8, 14], [8, 14], [8, 14], [8, 14], [7, 10], [7, 10], [7, 10], [7, 10], [7, 10], [7, 10], [7, 10], [7, 10], [7, 10], [7, 10], [7, 10], [7, 10], [7, 10], [7, 10], [7, 10], [7, 10], [7, 10], [7, 10], [7, 10], [7, 10], [7, 10], [7, 10], [7, 10], [7, 10], [7, 10], [7, 10], [7, 10], [7, 10], [7, 10], [7, 10], [7, 10], [7, 10], [7, 11], [7, 11], [7, 11], [7, 11], [7, 11], [7, 11], [7, 11], [7, 11], [7, 11], [7, 11], [7, 11], [7, 11], [7, 11], [7, 11], [7, 11], [7, 11], [7, 11], [7, 11], [7, 11], [7, 11], [7, 11], [7, 11], [7, 11], [7, 11], [7, 11], [7, 11], [7, 11], [7, 11], [7, 11], [7, 11], [7, 11], [7, 11], [9, 15], [9, 15], [9, 15], [9, 15], [9, 15], [9, 15], [9, 15], [9, 15], [12, 128], [12, 192], [12, 26], [12, 27], [12, 28], [12, 29], [11, 19], [11, 19], [11, 20], [11, 20], [12, 34], [12, 35], [12, 36], [12, 37], [12, 38], [12, 39], [11, 21], [11, 21], [12, 42], [12, 43], [10, 0], [10, 0], [10, 0], [10, 0], [7, 12], [7, 12], [7, 12], [7, 12], [7, 12], [7, 12], [7, 12], [7, 12], [7, 12], [7, 12], [7, 12], [7, 12], [7, 12], [7, 12], [7, 12], [7, 12], [7, 12], [7, 12], [7, 12], [7, 12], [7, 12], [7, 12], [7, 12], [7, 12], [7, 12], [7, 12], [7, 12], [7, 12], [7, 12], [7, 12], [7, 12], [7, 12]];
+  const blackTable3 = [[-1, -1], [-1, -1], [-1, -1], [-1, -1], [6, 9], [6, 8], [5, 7], [5, 7], [4, 6], [4, 6], [4, 6], [4, 6], [4, 5], [4, 5], [4, 5], [4, 5], [3, 1], [3, 1], [3, 1], [3, 1], [3, 1], [3, 1], [3, 1], [3, 1], [3, 4], [3, 4], [3, 4], [3, 4], [3, 4], [3, 4], [3, 4], [3, 4], [2, 3], [2, 3], [2, 3], [2, 3], [2, 3], [2, 3], [2, 3], [2, 3], [2, 3], [2, 3], [2, 3], [2, 3], [2, 3], [2, 3], [2, 3], [2, 3], [2, 2], [2, 2], [2, 2], [2, 2], [2, 2], [2, 2], [2, 2], [2, 2], [2, 2], [2, 2], [2, 2], [2, 2], [2, 2], [2, 2], [2, 2], [2, 2]];
+  function CCITTFaxDecoder(source, options = {}) {
+    if (!source || typeof source.next !== 'function') {
+      throw new Error('CCITTFaxDecoder - invalid "source" parameter.');
+    }
+    this.source = source;
+    this.eof = false;
+    this.encoding = options['K'] || 0;
+    this.eoline = options['EndOfLine'] || false;
+    this.byteAlign = options['EncodedByteAlign'] || false;
+    this.columns = options['Columns'] || 1728;
+    this.rows = options['Rows'] || 0;
+    let eoblock = options['EndOfBlock'];
+    if (eoblock === null || eoblock === undefined) {
+      eoblock = true;
+    }
+    this.eoblock = eoblock;
+    this.black = options['BlackIs1'] || false;
+    this.codingLine = new Uint32Array(this.columns + 1);
+    this.refLine = new Uint32Array(this.columns + 2);
+    this.codingLine[0] = this.columns;
+    this.codingPos = 0;
+    this.row = 0;
+    this.nextLine2D = this.encoding < 0;
+    this.inputBits = 0;
+    this.inputBuf = 0;
+    this.outputBits = 0;
+    this.rowsDone = false;
+    let code1;
+    while ((code1 = this._lookBits(12)) === 0) {
+      this._eatBits(1);
+    }
+    if (code1 === 1) {
+      this._eatBits(12);
+    }
+    if (this.encoding > 0) {
+      this.nextLine2D = !this._lookBits(1);
+      this._eatBits(1);
+    }
+  }
+  CCITTFaxDecoder.prototype = {
+    readNextChar() {
+      if (this.eof) {
+        return -1;
+      }
+      let refLine = this.refLine;
+      let codingLine = this.codingLine;
+      let columns = this.columns;
+      let refPos, blackPixels, bits, i;
+      if (this.outputBits === 0) {
+        if (this.rowsDone) {
+          this.eof = true;
+        }
+        if (this.eof) {
+          return -1;
+        }
+        this.err = false;
+        let code1, code2, code3;
+        if (this.nextLine2D) {
+          for (i = 0; codingLine[i] < columns; ++i) {
+            refLine[i] = codingLine[i];
+          }
+          refLine[i++] = columns;
+          refLine[i] = columns;
+          codingLine[0] = 0;
+          this.codingPos = 0;
+          refPos = 0;
+          blackPixels = 0;
+          while (codingLine[this.codingPos] < columns) {
+            code1 = this._getTwoDimCode();
+            switch (code1) {
+              case twoDimPass:
+                this._addPixels(refLine[refPos + 1], blackPixels);
+                if (refLine[refPos + 1] < columns) {
+                  refPos += 2;
+                }
+                break;
+              case twoDimHoriz:
+                code1 = code2 = 0;
+                if (blackPixels) {
+                  do {
+                    code1 += code3 = this._getBlackCode();
+                  } while (code3 >= 64);
+                  do {
+                    code2 += code3 = this._getWhiteCode();
+                  } while (code3 >= 64);
+                } else {
+                  do {
+                    code1 += code3 = this._getWhiteCode();
+                  } while (code3 >= 64);
+                  do {
+                    code2 += code3 = this._getBlackCode();
+                  } while (code3 >= 64);
+                }
+                this._addPixels(codingLine[this.codingPos] + code1, blackPixels);
+                if (codingLine[this.codingPos] < columns) {
+                  this._addPixels(codingLine[this.codingPos] + code2, blackPixels ^ 1);
+                }
+                while (refLine[refPos] <= codingLine[this.codingPos] && refLine[refPos] < columns) {
+                  refPos += 2;
+                }
+                break;
+              case twoDimVertR3:
+                this._addPixels(refLine[refPos] + 3, blackPixels);
+                blackPixels ^= 1;
+                if (codingLine[this.codingPos] < columns) {
+                  ++refPos;
+                  while (refLine[refPos] <= codingLine[this.codingPos] && refLine[refPos] < columns) {
+                    refPos += 2;
+                  }
+                }
+                break;
+              case twoDimVertR2:
+                this._addPixels(refLine[refPos] + 2, blackPixels);
+                blackPixels ^= 1;
+                if (codingLine[this.codingPos] < columns) {
+                  ++refPos;
+                  while (refLine[refPos] <= codingLine[this.codingPos] && refLine[refPos] < columns) {
+                    refPos += 2;
+                  }
+                }
+                break;
+              case twoDimVertR1:
+                this._addPixels(refLine[refPos] + 1, blackPixels);
+                blackPixels ^= 1;
+                if (codingLine[this.codingPos] < columns) {
+                  ++refPos;
+                  while (refLine[refPos] <= codingLine[this.codingPos] && refLine[refPos] < columns) {
+                    refPos += 2;
+                  }
+                }
+                break;
+              case twoDimVert0:
+                this._addPixels(refLine[refPos], blackPixels);
+                blackPixels ^= 1;
+                if (codingLine[this.codingPos] < columns) {
+                  ++refPos;
+                  while (refLine[refPos] <= codingLine[this.codingPos] && refLine[refPos] < columns) {
+                    refPos += 2;
+                  }
+                }
+                break;
+              case twoDimVertL3:
+                this._addPixelsNeg(refLine[refPos] - 3, blackPixels);
+                blackPixels ^= 1;
+                if (codingLine[this.codingPos] < columns) {
+                  if (refPos > 0) {
+                    --refPos;
+                  } else {
+                    ++refPos;
+                  }
+                  while (refLine[refPos] <= codingLine[this.codingPos] && refLine[refPos] < columns) {
+                    refPos += 2;
+                  }
+                }
+                break;
+              case twoDimVertL2:
+                this._addPixelsNeg(refLine[refPos] - 2, blackPixels);
+                blackPixels ^= 1;
+                if (codingLine[this.codingPos] < columns) {
+                  if (refPos > 0) {
+                    --refPos;
+                  } else {
+                    ++refPos;
+                  }
+                  while (refLine[refPos] <= codingLine[this.codingPos] && refLine[refPos] < columns) {
+                    refPos += 2;
+                  }
+                }
+                break;
+              case twoDimVertL1:
+                this._addPixelsNeg(refLine[refPos] - 1, blackPixels);
+                blackPixels ^= 1;
+                if (codingLine[this.codingPos] < columns) {
+                  if (refPos > 0) {
+                    --refPos;
+                  } else {
+                    ++refPos;
+                  }
+                  while (refLine[refPos] <= codingLine[this.codingPos] && refLine[refPos] < columns) {
+                    refPos += 2;
+                  }
+                }
+                break;
+              case ccittEOF:
+                this._addPixels(columns, 0);
+                this.eof = true;
+                break;
+              default:
+                (0, _util.info)('bad 2d code');
+                this._addPixels(columns, 0);
+                this.err = true;
+            }
+          }
+        } else {
+          codingLine[0] = 0;
+          this.codingPos = 0;
+          blackPixels = 0;
+          while (codingLine[this.codingPos] < columns) {
+            code1 = 0;
+            if (blackPixels) {
+              do {
+                code1 += code3 = this._getBlackCode();
+              } while (code3 >= 64);
+            } else {
+              do {
+                code1 += code3 = this._getWhiteCode();
+              } while (code3 >= 64);
+            }
+            this._addPixels(codingLine[this.codingPos] + code1, blackPixels);
+            blackPixels ^= 1;
+          }
+        }
+        let gotEOL = false;
+        if (this.byteAlign) {
+          this.inputBits &= ~7;
+        }
+        if (!this.eoblock && this.row === this.rows - 1) {
+          this.rowsDone = true;
+        } else {
+          code1 = this._lookBits(12);
+          if (this.eoline) {
+            while (code1 !== ccittEOF && code1 !== 1) {
+              this._eatBits(1);
+              code1 = this._lookBits(12);
+            }
+          } else {
+            while (code1 === 0) {
+              this._eatBits(1);
+              code1 = this._lookBits(12);
+            }
+          }
+          if (code1 === 1) {
+            this._eatBits(12);
+            gotEOL = true;
+          } else if (code1 === ccittEOF) {
+            this.eof = true;
+          }
+        }
+        if (!this.eof && this.encoding > 0 && !this.rowsDone) {
+          this.nextLine2D = !this._lookBits(1);
+          this._eatBits(1);
+        }
+        if (this.eoblock && gotEOL && this.byteAlign) {
+          code1 = this._lookBits(12);
+          if (code1 === 1) {
+            this._eatBits(12);
+            if (this.encoding > 0) {
+              this._lookBits(1);
+              this._eatBits(1);
+            }
+            if (this.encoding >= 0) {
+              for (i = 0; i < 4; ++i) {
+                code1 = this._lookBits(12);
+                if (code1 !== 1) {
+                  (0, _util.info)('bad rtc code: ' + code1);
+                }
+                this._eatBits(12);
+                if (this.encoding > 0) {
+                  this._lookBits(1);
+                  this._eatBits(1);
+                }
+              }
+            }
+            this.eof = true;
+          }
+        } else if (this.err && this.eoline) {
+          while (true) {
+            code1 = this._lookBits(13);
+            if (code1 === ccittEOF) {
+              this.eof = true;
+              return -1;
+            }
+            if (code1 >> 1 === 1) {
+              break;
+            }
+            this._eatBits(1);
+          }
+          this._eatBits(12);
+          if (this.encoding > 0) {
+            this._eatBits(1);
+            this.nextLine2D = !(code1 & 1);
+          }
+        }
+        if (codingLine[0] > 0) {
+          this.outputBits = codingLine[this.codingPos = 0];
+        } else {
+          this.outputBits = codingLine[this.codingPos = 1];
+        }
+        this.row++;
+      }
+      let c;
+      if (this.outputBits >= 8) {
+        c = this.codingPos & 1 ? 0 : 0xFF;
+        this.outputBits -= 8;
+        if (this.outputBits === 0 && codingLine[this.codingPos] < columns) {
+          this.codingPos++;
+          this.outputBits = codingLine[this.codingPos] - codingLine[this.codingPos - 1];
+        }
+      } else {
+        bits = 8;
+        c = 0;
+        do {
+          if (this.outputBits > bits) {
+            c <<= bits;
+            if (!(this.codingPos & 1)) {
+              c |= 0xFF >> 8 - bits;
+            }
+            this.outputBits -= bits;
+            bits = 0;
+          } else {
+            c <<= this.outputBits;
+            if (!(this.codingPos & 1)) {
+              c |= 0xFF >> 8 - this.outputBits;
+            }
+            bits -= this.outputBits;
+            this.outputBits = 0;
+            if (codingLine[this.codingPos] < columns) {
+              this.codingPos++;
+              this.outputBits = codingLine[this.codingPos] - codingLine[this.codingPos - 1];
+            } else if (bits > 0) {
+              c <<= bits;
+              bits = 0;
+            }
+          }
+        } while (bits);
+      }
+      if (this.black) {
+        c ^= 0xFF;
+      }
+      return c;
+    },
+    _addPixels(a1, blackPixels) {
+      let codingLine = this.codingLine;
+      let codingPos = this.codingPos;
+      if (a1 > codingLine[codingPos]) {
+        if (a1 > this.columns) {
+          (0, _util.info)('row is wrong length');
+          this.err = true;
+          a1 = this.columns;
+        }
+        if (codingPos & 1 ^ blackPixels) {
+          ++codingPos;
+        }
+        codingLine[codingPos] = a1;
+      }
+      this.codingPos = codingPos;
+    },
+    _addPixelsNeg(a1, blackPixels) {
+      let codingLine = this.codingLine;
+      let codingPos = this.codingPos;
+      if (a1 > codingLine[codingPos]) {
+        if (a1 > this.columns) {
+          (0, _util.info)('row is wrong length');
+          this.err = true;
+          a1 = this.columns;
+        }
+        if (codingPos & 1 ^ blackPixels) {
+          ++codingPos;
+        }
+        codingLine[codingPos] = a1;
+      } else if (a1 < codingLine[codingPos]) {
+        if (a1 < 0) {
+          (0, _util.info)('invalid code');
+          this.err = true;
+          a1 = 0;
+        }
+        while (codingPos > 0 && a1 < codingLine[codingPos - 1]) {
+          --codingPos;
+        }
+        codingLine[codingPos] = a1;
+      }
+      this.codingPos = codingPos;
+    },
+    _findTableCode(start, end, table, limit) {
+      let limitValue = limit || 0;
+      for (let i = start; i <= end; ++i) {
+        let code = this._lookBits(i);
+        if (code === ccittEOF) {
+          return [true, 1, false];
+        }
+        if (i < end) {
+          code <<= end - i;
+        }
+        if (!limitValue || code >= limitValue) {
+          let p = table[code - limitValue];
+          if (p[0] === i) {
+            this._eatBits(i);
+            return [true, p[1], true];
+          }
+        }
+      }
+      return [false, 0, false];
+    },
+    _getTwoDimCode() {
+      let code = 0;
+      let p;
+      if (this.eoblock) {
+        code = this._lookBits(7);
+        p = twoDimTable[code];
+        if (p && p[0] > 0) {
+          this._eatBits(p[0]);
+          return p[1];
+        }
+      } else {
+        let result = this._findTableCode(1, 7, twoDimTable);
+        if (result[0] && result[2]) {
+          return result[1];
+        }
+      }
+      (0, _util.info)('Bad two dim code');
+      return ccittEOF;
+    },
+    _getWhiteCode() {
+      let code = 0;
+      let p;
+      if (this.eoblock) {
+        code = this._lookBits(12);
+        if (code === ccittEOF) {
+          return 1;
+        }
+        if (code >> 5 === 0) {
+          p = whiteTable1[code];
+        } else {
+          p = whiteTable2[code >> 3];
+        }
+        if (p[0] > 0) {
+          this._eatBits(p[0]);
+          return p[1];
+        }
+      } else {
+        let result = this._findTableCode(1, 9, whiteTable2);
+        if (result[0]) {
+          return result[1];
+        }
+        result = this._findTableCode(11, 12, whiteTable1);
+        if (result[0]) {
+          return result[1];
+        }
+      }
+      (0, _util.info)('bad white code');
+      this._eatBits(1);
+      return 1;
+    },
+    _getBlackCode() {
+      let code, p;
+      if (this.eoblock) {
+        code = this._lookBits(13);
+        if (code === ccittEOF) {
+          return 1;
+        }
+        if (code >> 7 === 0) {
+          p = blackTable1[code];
+        } else if (code >> 9 === 0 && code >> 7 !== 0) {
+          p = blackTable2[(code >> 1) - 64];
+        } else {
+          p = blackTable3[code >> 7];
+        }
+        if (p[0] > 0) {
+          this._eatBits(p[0]);
+          return p[1];
+        }
+      } else {
+        let result = this._findTableCode(2, 6, blackTable3);
+        if (result[0]) {
+          return result[1];
+        }
+        result = this._findTableCode(7, 12, blackTable2, 64);
+        if (result[0]) {
+          return result[1];
+        }
+        result = this._findTableCode(10, 13, blackTable1);
+        if (result[0]) {
+          return result[1];
+        }
+      }
+      (0, _util.info)('bad black code');
+      this._eatBits(1);
+      return 1;
+    },
+    _lookBits(n) {
+      let c;
+      while (this.inputBits < n) {
+        if ((c = this.source.next()) === -1) {
+          if (this.inputBits === 0) {
+            return ccittEOF;
+          }
+          return this.inputBuf << n - this.inputBits & 0xFFFF >> 16 - n;
+        }
+        this.inputBuf = this.inputBuf << 8 | c;
+        this.inputBits += 8;
+      }
+      return this.inputBuf >> this.inputBits - n & 0xFFFF >> 16 - n;
+    },
+    _eatBits(n) {
+      if ((this.inputBits -= n) < 0) {
+        this.inputBits = 0;
+      }
+    }
+  };
+  return CCITTFaxDecoder;
+}();
+exports.CCITTFaxDecoder = CCITTFaxDecoder;
+
+/***/ }),
+/* 27 */
+/***/ (function(module, exports, __w_pdfjs_require__) {
+
+"use strict";
+
+
+Object.defineProperty(exports, "__esModule", {
+  value: true
+});
+exports.Jbig2Stream = undefined;
+
+var _primitives = __w_pdfjs_require__(1);
+
+var _stream = __w_pdfjs_require__(2);
+
+var _jbig = __w_pdfjs_require__(28);
+
+var _util = __w_pdfjs_require__(0);
+
+var Jbig2Stream = function Jbig2StreamClosure() {
+  function Jbig2Stream(stream, maybeLength, dict, params) {
+    this.stream = stream;
+    this.maybeLength = maybeLength;
+    this.dict = dict;
+    this.params = params;
+    _stream.DecodeStream.call(this, maybeLength);
+  }
+  Jbig2Stream.prototype = Object.create(_stream.DecodeStream.prototype);
+  Object.defineProperty(Jbig2Stream.prototype, 'bytes', {
+    get() {
+      return (0, _util.shadow)(this, 'bytes', this.stream.getBytes(this.maybeLength));
+    },
+    configurable: true
+  });
+  Jbig2Stream.prototype.ensureBuffer = function (req) {
+    if (this.bufferLength) {
+      return;
+    }
+    var jbig2Image = new _jbig.Jbig2Image();
+    var chunks = [];
+    if ((0, _primitives.isDict)(this.params)) {
+      var globalsStream = this.params.get('JBIG2Globals');
+      if ((0, _primitives.isStream)(globalsStream)) {
+        var globals = globalsStream.getBytes();
+        chunks.push({
+          data: globals,
+          start: 0,
+          end: globals.length
+        });
+      }
+    }
+    chunks.push({
+      data: this.bytes,
+      start: 0,
+      end: this.bytes.length
+    });
+    var data = jbig2Image.parseChunks(chunks);
+    var dataLength = data.length;
+    for (var i = 0; i < dataLength; i++) {
+      data[i] ^= 0xFF;
+    }
+    this.buffer = data;
+    this.bufferLength = dataLength;
+    this.eof = true;
+  };
+  return Jbig2Stream;
+}();
+exports.Jbig2Stream = Jbig2Stream;
+
+/***/ }),
+/* 28 */
+/***/ (function(module, exports, __w_pdfjs_require__) {
+
+"use strict";
+
+
+Object.defineProperty(exports, "__esModule", {
+  value: true
+});
 exports.Jbig2Image = undefined;
 
 var _util = __w_pdfjs_require__(0);
 
-var _arithmetic_decoder = __w_pdfjs_require__(9);
+var _arithmetic_decoder = __w_pdfjs_require__(10);
 
 let Jbig2Error = function Jbig2ErrorClosure() {
   function Jbig2Error(msg) {
     this.message = 'JBIG2 error: ' + msg;
   }
   Jbig2Error.prototype = new Error();
   Jbig2Error.prototype.name = 'Jbig2Error';
   Jbig2Error.constructor = Jbig2Error;
@@ -29425,933 +30428,17 @@ var Jbig2Image = function Jbig2ImageClos
       return parseJbig2Chunks(chunks);
     }
   };
   return Jbig2Image;
 }();
 exports.Jbig2Image = Jbig2Image;
 
 /***/ }),
-/* 25 */
-/***/ (function(module, exports, __w_pdfjs_require__) {
-
-"use strict";
-
-
-Object.defineProperty(exports, "__esModule", {
-  value: true
-});
-exports.JpegImage = undefined;
-
-var _util = __w_pdfjs_require__(0);
-
-let JpegError = function JpegErrorClosure() {
-  function JpegError(msg) {
-    this.message = 'JPEG error: ' + msg;
-  }
-  JpegError.prototype = new Error();
-  JpegError.prototype.name = 'JpegError';
-  JpegError.constructor = JpegError;
-  return JpegError;
-}();
-var JpegImage = function JpegImageClosure() {
-  var dctZigZag = new Uint8Array([0, 1, 8, 16, 9, 2, 3, 10, 17, 24, 32, 25, 18, 11, 4, 5, 12, 19, 26, 33, 40, 48, 41, 34, 27, 20, 13, 6, 7, 14, 21, 28, 35, 42, 49, 56, 57, 50, 43, 36, 29, 22, 15, 23, 30, 37, 44, 51, 58, 59, 52, 45, 38, 31, 39, 46, 53, 60, 61, 54, 47, 55, 62, 63]);
-  var dctCos1 = 4017;
-  var dctSin1 = 799;
-  var dctCos3 = 3406;
-  var dctSin3 = 2276;
-  var dctCos6 = 1567;
-  var dctSin6 = 3784;
-  var dctSqrt2 = 5793;
-  var dctSqrt1d2 = 2896;
-  function JpegImage() {
-    this.decodeTransform = null;
-    this.colorTransform = -1;
-  }
-  function buildHuffmanTable(codeLengths, values) {
-    var k = 0,
-        code = [],
-        i,
-        j,
-        length = 16;
-    while (length > 0 && !codeLengths[length - 1]) {
-      length--;
-    }
-    code.push({
-      children: [],
-      index: 0
-    });
-    var p = code[0],
-        q;
-    for (i = 0; i < length; i++) {
-      for (j = 0; j < codeLengths[i]; j++) {
-        p = code.pop();
-        p.children[p.index] = values[k];
-        while (p.index > 0) {
-          p = code.pop();
-        }
-        p.index++;
-        code.push(p);
-        while (code.length <= i) {
-          code.push(q = {
-            children: [],
-            index: 0
-          });
-          p.children[p.index] = q.children;
-          p = q;
-        }
-        k++;
-      }
-      if (i + 1 < length) {
-        code.push(q = {
-          children: [],
-          index: 0
-        });
-        p.children[p.index] = q.children;
-        p = q;
-      }
-    }
-    return code[0].children;
-  }
-  function getBlockBufferOffset(component, row, col) {
-    return 64 * ((component.blocksPerLine + 1) * row + col);
-  }
-  function decodeScan(data, offset, frame, components, resetInterval, spectralStart, spectralEnd, successivePrev, successive) {
-    var mcusPerLine = frame.mcusPerLine;
-    var progressive = frame.progressive;
-    var startOffset = offset,
-        bitsData = 0,
-        bitsCount = 0;
-    function readBit() {
-      if (bitsCount > 0) {
-        bitsCount--;
-        return bitsData >> bitsCount & 1;
-      }
-      bitsData = data[offset++];
-      if (bitsData === 0xFF) {
-        var nextByte = data[offset++];
-        if (nextByte) {
-          throw new JpegError(`unexpected marker ${(bitsData << 8 | nextByte).toString(16)}`);
-        }
-      }
-      bitsCount = 7;
-      return bitsData >>> 7;
-    }
-    function decodeHuffman(tree) {
-      var node = tree;
-      while (true) {
-        node = node[readBit()];
-        if (typeof node === 'number') {
-          return node;
-        }
-        if (typeof node !== 'object') {
-          throw new JpegError('invalid huffman sequence');
-        }
-      }
-    }
-    function receive(length) {
-      var n = 0;
-      while (length > 0) {
-        n = n << 1 | readBit();
-        length--;
-      }
-      return n;
-    }
-    function receiveAndExtend(length) {
-      if (length === 1) {
-        return readBit() === 1 ? 1 : -1;
-      }
-      var n = receive(length);
-      if (n >= 1 << length - 1) {
-        return n;
-      }
-      return n + (-1 << length) + 1;
-    }
-    function decodeBaseline(component, offset) {
-      var t = decodeHuffman(component.huffmanTableDC);
-      var diff = t === 0 ? 0 : receiveAndExtend(t);
-      component.blockData[offset] = component.pred += diff;
-      var k = 1;
-      while (k < 64) {
-        var rs = decodeHuffman(component.huffmanTableAC);
-        var s = rs & 15,
-            r = rs >> 4;
-        if (s === 0) {
-          if (r < 15) {
-            break;
-          }
-          k += 16;
-          continue;
-        }
-        k += r;
-        var z = dctZigZag[k];
-        component.blockData[offset + z] = receiveAndExtend(s);
-        k++;
-      }
-    }
-    function decodeDCFirst(component, offset) {
-      var t = decodeHuffman(component.huffmanTableDC);
-      var diff = t === 0 ? 0 : receiveAndExtend(t) << successive;
-      component.blockData[offset] = component.pred += diff;
-    }
-    function decodeDCSuccessive(component, offset) {
-      component.blockData[offset] |= readBit() << successive;
-    }
-    var eobrun = 0;
-    function decodeACFirst(component, offset) {
-      if (eobrun > 0) {
-        eobrun--;
-        return;
-      }
-      var k = spectralStart,
-          e = spectralEnd;
-      while (k <= e) {
-        var rs = decodeHuffman(component.huffmanTableAC);
-        var s = rs & 15,
-            r = rs >> 4;
-        if (s === 0) {
-          if (r < 15) {
-            eobrun = receive(r) + (1 << r) - 1;
-            break;
-          }
-          k += 16;
-          continue;
-        }
-        k += r;
-        var z = dctZigZag[k];
-        component.blockData[offset + z] = receiveAndExtend(s) * (1 << successive);
-        k++;
-      }
-    }
-    var successiveACState = 0,
-        successiveACNextValue;
-    function decodeACSuccessive(component, offset) {
-      var k = spectralStart;
-      var e = spectralEnd;
-      var r = 0;
-      var s;
-      var rs;
-      while (k <= e) {
-        var z = dctZigZag[k];
-        switch (successiveACState) {
-          case 0:
-            rs = decodeHuffman(component.huffmanTableAC);
-            s = rs & 15;
-            r = rs >> 4;
-            if (s === 0) {
-              if (r < 15) {
-                eobrun = receive(r) + (1 << r);
-                successiveACState = 4;
-              } else {
-                r = 16;
-                successiveACState = 1;
-              }
-            } else {
-              if (s !== 1) {
-                throw new JpegError('invalid ACn encoding');
-              }
-              successiveACNextValue = receiveAndExtend(s);
-              successiveACState = r ? 2 : 3;
-            }
-            continue;
-          case 1:
-          case 2:
-            if (component.blockData[offset + z]) {
-              component.blockData[offset + z] += readBit() << successive;
-            } else {
-              r--;
-              if (r === 0) {
-                successiveACState = successiveACState === 2 ? 3 : 0;
-              }
-            }
-            break;
-          case 3:
-            if (component.blockData[offset + z]) {
-              component.blockData[offset + z] += readBit() << successive;
-            } else {
-              component.blockData[offset + z] = successiveACNextValue << successive;
-              successiveACState = 0;
-            }
-            break;
-          case 4:
-            if (component.blockData[offset + z]) {
-              component.blockData[offset + z] += readBit() << successive;
-            }
-            break;
-        }
-        k++;
-      }
-      if (successiveACState === 4) {
-        eobrun--;
-        if (eobrun === 0) {
-          successiveACState = 0;
-        }
-      }
-    }
-    function decodeMcu(component, decode, mcu, row, col) {
-      var mcuRow = mcu / mcusPerLine | 0;
-      var mcuCol = mcu % mcusPerLine;
-      var blockRow = mcuRow * component.v + row;
-      var blockCol = mcuCol * component.h + col;
-      var offset = getBlockBufferOffset(component, blockRow, blockCol);
-      decode(component, offset);
-    }
-    function decodeBlock(component, decode, mcu) {
-      var blockRow = mcu / component.blocksPerLine | 0;
-      var blockCol = mcu % component.blocksPerLine;
-      var offset = getBlockBufferOffset(component, blockRow, blockCol);
-      decode(component, offset);
-    }
-    var componentsLength = components.length;
-    var component, i, j, k, n;
-    var decodeFn;
-    if (progressive) {
-      if (spectralStart === 0) {
-        decodeFn = successivePrev === 0 ? decodeDCFirst : decodeDCSuccessive;
-      } else {
-        decodeFn = successivePrev === 0 ? decodeACFirst : decodeACSuccessive;
-      }
-    } else {
-      decodeFn = decodeBaseline;
-    }
-    var mcu = 0,
-        fileMarker;
-    var mcuExpected;
-    if (componentsLength === 1) {
-      mcuExpected = components[0].blocksPerLine * components[0].blocksPerColumn;
-    } else {
-      mcuExpected = mcusPerLine * frame.mcusPerColumn;
-    }
-    var h, v;
-    while (mcu < mcuExpected) {
-      var mcuToRead = resetInterval ? Math.min(mcuExpected - mcu, resetInterval) : mcuExpected;
-      for (i = 0; i < componentsLength; i++) {
-        components[i].pred = 0;
-      }
-      eobrun = 0;
-      if (componentsLength === 1) {
-        component = components[0];
-        for (n = 0; n < mcuToRead; n++) {
-          decodeBlock(component, decodeFn, mcu);
-          mcu++;
-        }
-      } else {
-        for (n = 0; n < mcuToRead; n++) {
-          for (i = 0; i < componentsLength; i++) {
-            component = components[i];
-            h = component.h;
-            v = component.v;
-            for (j = 0; j < v; j++) {
-              for (k = 0; k < h; k++) {
-                decodeMcu(component, decodeFn, mcu, j, k);
-              }
-            }
-          }
-          mcu++;
-        }
-      }
-      bitsCount = 0;
-      fileMarker = findNextFileMarker(data, offset);
-      if (fileMarker && fileMarker.invalid) {
-        (0, _util.warn)('decodeScan - unexpected MCU data, next marker is: ' + fileMarker.invalid);
-        offset = fileMarker.offset;
-      }
-      var marker = fileMarker && fileMarker.marker;
-      if (!marker || marker <= 0xFF00) {
-        throw new JpegError('marker was not found');
-      }
-      if (marker >= 0xFFD0 && marker <= 0xFFD7) {
-        offset += 2;
-      } else {
-        break;
-      }
-    }
-    fileMarker = findNextFileMarker(data, offset);
-    if (fileMarker && fileMarker.invalid) {
-      (0, _util.warn)('decodeScan - unexpected Scan data, next marker is: ' + fileMarker.invalid);
-      offset = fileMarker.offset;
-    }
-    return offset - startOffset;
-  }
-  function quantizeAndInverse(component, blockBufferOffset, p) {
-    var qt = component.quantizationTable,
-        blockData = component.blockData;
-    var v0, v1, v2, v3, v4, v5, v6, v7;
-    var p0, p1, p2, p3, p4, p5, p6, p7;
-    var t;
-    if (!qt) {
-      throw new JpegError('missing required Quantization Table.');
-    }
-    for (var row = 0; row < 64; row += 8) {
-      p0 = blockData[blockBufferOffset + row];
-      p1 = blockData[blockBufferOffset + row + 1];
-      p2 = blockData[blockBufferOffset + row + 2];
-      p3 = blockData[blockBufferOffset + row + 3];
-      p4 = blockData[blockBufferOffset + row + 4];
-      p5 = blockData[blockBufferOffset + row + 5];
-      p6 = blockData[blockBufferOffset + row + 6];
-      p7 = blockData[blockBufferOffset + row + 7];
-      p0 *= qt[row];
-      if ((p1 | p2 | p3 | p4 | p5 | p6 | p7) === 0) {
-        t = dctSqrt2 * p0 + 512 >> 10;
-        p[row] = t;
-        p[row + 1] = t;
-        p[row + 2] = t;
-        p[row + 3] = t;
-        p[row + 4] = t;
-        p[row + 5] = t;
-        p[row + 6] = t;
-        p[row + 7] = t;
-        continue;
-      }
-      p1 *= qt[row + 1];
-      p2 *= qt[row + 2];
-      p3 *= qt[row + 3];
-      p4 *= qt[row + 4];
-      p5 *= qt[row + 5];
-      p6 *= qt[row + 6];
-      p7 *= qt[row + 7];
-      v0 = dctSqrt2 * p0 + 128 >> 8;
-      v1 = dctSqrt2 * p4 + 128 >> 8;
-      v2 = p2;
-      v3 = p6;
-      v4 = dctSqrt1d2 * (p1 - p7) + 128 >> 8;
-      v7 = dctSqrt1d2 * (p1 + p7) + 128 >> 8;
-      v5 = p3 << 4;
-      v6 = p5 << 4;
-      v0 = v0 + v1 + 1 >> 1;
-      v1 = v0 - v1;
-      t = v2 * dctSin6 + v3 * dctCos6 + 128 >> 8;
-      v2 = v2 * dctCos6 - v3 * dctSin6 + 128 >> 8;
-      v3 = t;
-      v4 = v4 + v6 + 1 >> 1;
-      v6 = v4 - v6;
-      v7 = v7 + v5 + 1 >> 1;
-      v5 = v7 - v5;
-      v0 = v0 + v3 + 1 >> 1;
-      v3 = v0 - v3;
-      v1 = v1 + v2 + 1 >> 1;
-      v2 = v1 - v2;
-      t = v4 * dctSin3 + v7 * dctCos3 + 2048 >> 12;
-      v4 = v4 * dctCos3 - v7 * dctSin3 + 2048 >> 12;
-      v7 = t;
-      t = v5 * dctSin1 + v6 * dctCos1 + 2048 >> 12;
-      v5 = v5 * dctCos1 - v6 * dctSin1 + 2048 >> 12;
-      v6 = t;
-      p[row] = v0 + v7;
-      p[row + 7] = v0 - v7;
-      p[row + 1] = v1 + v6;
-      p[row + 6] = v1 - v6;
-      p[row + 2] = v2 + v5;
-      p[row + 5] = v2 - v5;
-      p[row + 3] = v3 + v4;
-      p[row + 4] = v3 - v4;
-    }
-    for (var col = 0; col < 8; ++col) {
-      p0 = p[col];
-      p1 = p[col + 8];
-      p2 = p[col + 16];
-      p3 = p[col + 24];
-      p4 = p[col + 32];
-      p5 = p[col + 40];
-      p6 = p[col + 48];
-      p7 = p[col + 56];
-      if ((p1 | p2 | p3 | p4 | p5 | p6 | p7) === 0) {
-        t = dctSqrt2 * p0 + 8192 >> 14;
-        t = t < -2040 ? 0 : t >= 2024 ? 255 : t + 2056 >> 4;
-        blockData[blockBufferOffset + col] = t;
-        blockData[blockBufferOffset + col + 8] = t;
-        blockData[blockBufferOffset + col + 16] = t;
-        blockData[blockBufferOffset + col + 24] = t;
-        blockData[blockBufferOffset + col + 32] = t;
-        blockData[blockBufferOffset + col + 40] = t;
-        blockData[blockBufferOffset + col + 48] = t;
-        blockData[blockBufferOffset + col + 56] = t;
-        continue;
-      }
-      v0 = dctSqrt2 * p0 + 2048 >> 12;
-      v1 = dctSqrt2 * p4 + 2048 >> 12;
-      v2 = p2;
-      v3 = p6;
-      v4 = dctSqrt1d2 * (p1 - p7) + 2048 >> 12;
-      v7 = dctSqrt1d2 * (p1 + p7) + 2048 >> 12;
-      v5 = p3;
-      v6 = p5;
-      v0 = (v0 + v1 + 1 >> 1) + 4112;
-      v1 = v0 - v1;
-      t = v2 * dctSin6 + v3 * dctCos6 + 2048 >> 12;
-      v2 = v2 * dctCos6 - v3 * dctSin6 + 2048 >> 12;
-      v3 = t;
-      v4 = v4 + v6 + 1 >> 1;
-      v6 = v4 - v6;
-      v7 = v7 + v5 + 1 >> 1;
-      v5 = v7 - v5;
-      v0 = v0 + v3 + 1 >> 1;
-      v3 = v0 - v3;
-      v1 = v1 + v2 + 1 >> 1;
-      v2 = v1 - v2;
-      t = v4 * dctSin3 + v7 * dctCos3 + 2048 >> 12;
-      v4 = v4 * dctCos3 - v7 * dctSin3 + 2048 >> 12;
-      v7 = t;
-      t = v5 * dctSin1 + v6 * dctCos1 + 2048 >> 12;
-      v5 = v5 * dctCos1 - v6 * dctSin1 + 2048 >> 12;
-      v6 = t;
-      p0 = v0 + v7;
-      p7 = v0 - v7;
-      p1 = v1 + v6;
-      p6 = v1 - v6;
-      p2 = v2 + v5;
-      p5 = v2 - v5;
-      p3 = v3 + v4;
-      p4 = v3 - v4;
-      p0 = p0 < 16 ? 0 : p0 >= 4080 ? 255 : p0 >> 4;
-      p1 = p1 < 16 ? 0 : p1 >= 4080 ? 255 : p1 >> 4;
-      p2 = p2 < 16 ? 0 : p2 >= 4080 ? 255 : p2 >> 4;
-      p3 = p3 < 16 ? 0 : p3 >= 4080 ? 255 : p3 >> 4;
-      p4 = p4 < 16 ? 0 : p4 >= 4080 ? 255 : p4 >> 4;
-      p5 = p5 < 16 ? 0 : p5 >= 4080 ? 255 : p5 >> 4;
-      p6 = p6 < 16 ? 0 : p6 >= 4080 ? 255 : p6 >> 4;
-      p7 = p7 < 16 ? 0 : p7 >= 4080 ? 255 : p7 >> 4;
-      blockData[blockBufferOffset + col] = p0;
-      blockData[blockBufferOffset + col + 8] = p1;
-      blockData[blockBufferOffset + col + 16] = p2;
-      blockData[blockBufferOffset + col + 24] = p3;
-      blockData[blockBufferOffset + col + 32] = p4;
-      blockData[blockBufferOffset + col + 40] = p5;
-      blockData[blockBufferOffset + col + 48] = p6;
-      blockData[blockBufferOffset + col + 56] = p7;
-    }
-  }
-  function buildComponentData(frame, component) {
-    var blocksPerLine = component.blocksPerLine;
-    var blocksPerColumn = component.blocksPerColumn;
-    var computationBuffer = new Int16Array(64);
-    for (var blockRow = 0; blockRow < blocksPerColumn; blockRow++) {
-      for (var blockCol = 0; blockCol < blocksPerLine; blockCol++) {
-        var offset = getBlockBufferOffset(component, blockRow, blockCol);
-        quantizeAndInverse(component, offset, computationBuffer);
-      }
-    }
-    return component.blockData;
-  }
-  function findNextFileMarker(data, currentPos, startPos) {
-    function peekUint16(pos) {
-      return data[pos] << 8 | data[pos + 1];
-    }
-    var maxPos = data.length - 1;
-    var newPos = startPos < currentPos ? startPos : currentPos;
-    if (currentPos >= maxPos) {
-      return null;
-    }
-    var currentMarker = peekUint16(currentPos);
-    if (currentMarker >= 0xFFC0 && currentMarker <= 0xFFFE) {
-      return {
-        invalid: null,
-        marker: currentMarker,
-        offset: currentPos
-      };
-    }
-    var newMarker = peekUint16(newPos);
-    while (!(newMarker >= 0xFFC0 && newMarker <= 0xFFFE)) {
-      if (++newPos >= maxPos) {
-        return null;
-      }
-      newMarker = peekUint16(newPos);
-    }
-    return {
-      invalid: currentMarker.toString(16),
-      marker: newMarker,
-      offset: newPos
-    };
-  }
-  JpegImage.prototype = {
-    parse: function parse(data) {
-      function readUint16() {
-        var value = data[offset] << 8 | data[offset + 1];
-        offset += 2;
-        return value;
-      }
-      function readDataBlock() {
-        var length = readUint16();
-        var endOffset = offset + length - 2;
-        var fileMarker = findNextFileMarker(data, endOffset, offset);
-        if (fileMarker && fileMarker.invalid) {
-          (0, _util.warn)('readDataBlock - incorrect length, next marker is: ' + fileMarker.invalid);
-          endOffset = fileMarker.offset;
-        }
-        var array = data.subarray(offset, endOffset);
-        offset += array.length;
-        return array;
-      }
-      function prepareComponents(frame) {
-        var mcusPerLine = Math.ceil(frame.samplesPerLine / 8 / frame.maxH);
-        var mcusPerColumn = Math.ceil(frame.scanLines / 8 / frame.maxV);
-        for (var i = 0; i < frame.components.length; i++) {
-          component = frame.components[i];
-          var blocksPerLine = Math.ceil(Math.ceil(frame.samplesPerLine / 8) * component.h / frame.maxH);
-          var blocksPerColumn = Math.ceil(Math.ceil(frame.scanLines / 8) * component.v / frame.maxV);
-          var blocksPerLineForMcu = mcusPerLine * component.h;
-          var blocksPerColumnForMcu = mcusPerColumn * component.v;
-          var blocksBufferSize = 64 * blocksPerColumnForMcu * (blocksPerLineForMcu + 1);
-          component.blockData = new Int16Array(blocksBufferSize);
-          component.blocksPerLine = blocksPerLine;
-          component.blocksPerColumn = blocksPerColumn;
-        }
-        frame.mcusPerLine = mcusPerLine;
-        frame.mcusPerColumn = mcusPerColumn;
-      }
-      var offset = 0;
-      var jfif = null;
-      var adobe = null;
-      var frame, resetInterval;
-      var quantizationTables = [];
-      var huffmanTablesAC = [],
-          huffmanTablesDC = [];
-      var fileMarker = readUint16();
-      if (fileMarker !== 0xFFD8) {
-        throw new JpegError('SOI not found');
-      }
-      fileMarker = readUint16();
-      while (fileMarker !== 0xFFD9) {
-        var i, j, l;
-        switch (fileMarker) {
-          case 0xFFE0:
-          case 0xFFE1:
-          case 0xFFE2:
-          case 0xFFE3:
-          case 0xFFE4:
-          case 0xFFE5:
-          case 0xFFE6:
-          case 0xFFE7:
-          case 0xFFE8:
-          case 0xFFE9:
-          case 0xFFEA:
-          case 0xFFEB:
-          case 0xFFEC:
-          case 0xFFED:
-          case 0xFFEE:
-          case 0xFFEF:
-          case 0xFFFE:
-            var appData = readDataBlock();
-            if (fileMarker === 0xFFE0) {
-              if (appData[0] === 0x4A && appData[1] === 0x46 && appData[2] === 0x49 && appData[3] === 0x46 && appData[4] === 0) {
-                jfif = {
-                  version: {
-                    major: appData[5],
-                    minor: appData[6]
-                  },
-                  densityUnits: appData[7],
-                  xDensity: appData[8] << 8 | appData[9],
-                  yDensity: appData[10] << 8 | appData[11],
-                  thumbWidth: appData[12],
-                  thumbHeight: appData[13],
-                  thumbData: appData.subarray(14, 14 + 3 * appData[12] * appData[13])
-                };
-              }
-            }
-            if (fileMarker === 0xFFEE) {
-              if (appData[0] === 0x41 && appData[1] === 0x64 && appData[2] === 0x6F && appData[3] === 0x62 && appData[4] === 0x65) {
-                adobe = {
-                  version: appData[5] << 8 | appData[6],
-                  flags0: appData[7] << 8 | appData[8],
-                  flags1: appData[9] << 8 | appData[10],
-                  transformCode: appData[11]
-                };
-              }
-            }
-            break;
-          case 0xFFDB:
-            var quantizationTablesLength = readUint16();
-            var quantizationTablesEnd = quantizationTablesLength + offset - 2;
-            var z;
-            while (offset < quantizationTablesEnd) {
-              var quantizationTableSpec = data[offset++];
-              var tableData = new Uint16Array(64);
-              if (quantizationTableSpec >> 4 === 0) {
-                for (j = 0; j < 64; j++) {
-                  z = dctZigZag[j];
-                  tableData[z] = data[offset++];
-                }
-              } else if (quantizationTableSpec >> 4 === 1) {
-                for (j = 0; j < 64; j++) {
-                  z = dctZigZag[j];
-                  tableData[z] = readUint16();
-                }
-              } else {
-                throw new JpegError('DQT - invalid table spec');
-              }
-              quantizationTables[quantizationTableSpec & 15] = tableData;
-            }
-            break;
-          case 0xFFC0:
-          case 0xFFC1:
-          case 0xFFC2:
-            if (frame) {
-              throw new JpegError('Only single frame JPEGs supported');
-            }
-            readUint16();
-            frame = {};
-            frame.extended = fileMarker === 0xFFC1;
-            frame.progressive = fileMarker === 0xFFC2;
-            frame.precision = data[offset++];
-            frame.scanLines = readUint16();
-            frame.samplesPerLine = readUint16();
-            frame.components = [];
-            frame.componentIds = {};
-            var componentsCount = data[offset++],
-                componentId;
-            var maxH = 0,
-                maxV = 0;
-            for (i = 0; i < componentsCount; i++) {
-              componentId = data[offset];
-              var h = data[offset + 1] >> 4;
-              var v = data[offset + 1] & 15;
-              if (maxH < h) {
-                maxH = h;
-              }
-              if (maxV < v) {
-                maxV = v;
-              }
-              var qId = data[offset + 2];
-              l = frame.components.push({
-                h,
-                v,
-                quantizationId: qId,
-                quantizationTable: null
-              });
-              frame.componentIds[componentId] = l - 1;
-              offset += 3;
-            }
-            frame.maxH = maxH;
-            frame.maxV = maxV;
-            prepareComponents(frame);
-            break;
-          case 0xFFC4:
-            var huffmanLength = readUint16();
-            for (i = 2; i < huffmanLength;) {
-              var huffmanTableSpec = data[offset++];
-              var codeLengths = new Uint8Array(16);
-              var codeLengthSum = 0;
-              for (j = 0; j < 16; j++, offset++) {
-                codeLengthSum += codeLengths[j] = data[offset];
-              }
-              var huffmanValues = new Uint8Array(codeLengthSum);
-              for (j = 0; j < codeLengthSum; j++, offset++) {
-                huffmanValues[j] = data[offset];
-              }
-              i += 17 + codeLengthSum;
-              (huffmanTableSpec >> 4 === 0 ? huffmanTablesDC : huffmanTablesAC)[huffmanTableSpec & 15] = buildHuffmanTable(codeLengths, huffmanValues);
-            }
-            break;
-          case 0xFFDD:
-            readUint16();
-            resetInterval = readUint16();
-            break;
-          case 0xFFDA:
-            readUint16();
-            var selectorsCount = data[offset++];
-            var components = [],
-                component;
-            for (i = 0; i < selectorsCount; i++) {
-              var componentIndex = frame.componentIds[data[offset++]];
-              component = frame.components[componentIndex];
-              var tableSpec = data[offset++];
-              component.huffmanTableDC = huffmanTablesDC[tableSpec >> 4];
-              component.huffmanTableAC = huffmanTablesAC[tableSpec & 15];
-              components.push(component);
-            }
-            var spectralStart = data[offset++];
-            var spectralEnd = data[offset++];
-            var successiveApproximation = data[offset++];
-            var processed = decodeScan(data, offset, frame, components, resetInterval, spectralStart, spectralEnd, successiveApproximation >> 4, successiveApproximation & 15);
-            offset += processed;
-            break;
-          case 0xFFFF:
-            if (data[offset] !== 0xFF) {
-              offset--;
-            }
-            break;
-          default:
-            if (data[offset - 3] === 0xFF && data[offset - 2] >= 0xC0 && data[offset - 2] <= 0xFE) {
-              offset -= 3;
-              break;
-            }
-            throw new JpegError('unknown marker ' + fileMarker.toString(16));
-        }
-        fileMarker = readUint16();
-      }
-      this.width = frame.samplesPerLine;
-      this.height = frame.scanLines;
-      this.jfif = jfif;
-      this.adobe = adobe;
-      this.components = [];
-      for (i = 0; i < frame.components.length; i++) {
-        component = frame.components[i];
-        var quantizationTable = quantizationTables[component.quantizationId];
-        if (quantizationTable) {
-          component.quantizationTable = quantizationTable;
-        }
-        this.components.push({
-          output: buildComponentData(frame, component),
-          scaleX: component.h / frame.maxH,
-          scaleY: component.v / frame.maxV,
-          blocksPerLine: component.blocksPerLine,
-          blocksPerColumn: component.blocksPerColumn
-        });
-      }
-      this.numComponents = this.components.length;
-    },
-    _getLinearizedBlockData: function getLinearizedBlockData(width, height) {
-      var scaleX = this.width / width,
-          scaleY = this.height / height;
-      var component, componentScaleX, componentScaleY, blocksPerScanline;
-      var x, y, i, j, k;
-      var index;
-      var offset = 0;
-      var output;
-      var numComponents = this.components.length;
-      var dataLength = width * height * numComponents;
-      var data = new Uint8ClampedArray(dataLength);
-      var xScaleBlockOffset = new Uint32Array(width);
-      var mask3LSB = 0xfffffff8;
-      for (i = 0; i < numComponents; i++) {
-        component = this.components[i];
-        componentScaleX = component.scaleX * scaleX;
-        componentScaleY = component.scaleY * scaleY;
-        offset = i;
-        output = component.output;
-        blocksPerScanline = component.blocksPerLine + 1 << 3;
-        for (x = 0; x < width; x++) {
-          j = 0 | x * componentScaleX;
-          xScaleBlockOffset[x] = (j & mask3LSB) << 3 | j & 7;
-        }
-        for (y = 0; y < height; y++) {
-          j = 0 | y * componentScaleY;
-          index = blocksPerScanline * (j & mask3LSB) | (j & 7) << 3;
-          for (x = 0; x < width; x++) {
-            data[offset] = output[index + xScaleBlockOffset[x]];
-            offset += numComponents;
-          }
-        }
-      }
-      var transform = this.decodeTransform;
-      if (transform) {
-        for (i = 0; i < dataLength;) {
-          for (j = 0, k = 0; j < numComponents; j++, i++, k += 2) {
-            data[i] = (data[i] * transform[k] >> 8) + transform[k + 1];
-          }
-        }
-      }
-      return data;
-    },
-    _isColorConversionNeeded() {
-      if (this.adobe) {
-        return !!this.adobe.transformCode;
-      }
-      if (this.numComponents === 3) {
-        if (this.colorTransform === 0) {
-          return false;
-        }
-        return true;
-      }
-      if (this.colorTransform === 1) {
-        return true;
-      }
-      return false;
-    },
-    _convertYccToRgb: function convertYccToRgb(data) {
-      var Y, Cb, Cr;
-      for (var i = 0, length = data.length; i < length; i += 3) {
-        Y = data[i];
-        Cb = data[i + 1];
-        Cr = data[i + 2];
-        data[i] = Y - 179.456 + 1.402 * Cr;
-        data[i + 1] = Y + 135.459 - 0.344 * Cb - 0.714 * Cr;
-        data[i + 2] = Y - 226.816 + 1.772 * Cb;
-      }
-      return data;
-    },
-    _convertYcckToRgb: function convertYcckToRgb(data) {
-      var Y, Cb, Cr, k;
-      var offset = 0;
-      for (var i = 0, length = data.length; i < length; i += 4) {
-        Y = data[i];
-        Cb = data[i + 1];
-        Cr = data[i + 2];
-        k = data[i + 3];
-        data[offset++] = -122.67195406894 + Cb * (-6.60635669420364e-5 * Cb + 0.000437130475926232 * Cr - 5.4080610064599e-5 * Y + 0.00048449797120281 * k - 0.154362151871126) + Cr * (-0.000957964378445773 * Cr + 0.000817076911346625 * Y - 0.00477271405408747 * k + 1.53380253221734) + Y * (0.000961250184130688 * Y - 0.00266257332283933 * k + 0.48357088451265) + k * (-0.000336197177618394 * k + 0.484791561490776);
-        data[offset++] = 107.268039397724 + Cb * (2.19927104525741e-5 * Cb - 0.000640992018297945 * Cr + 0.000659397001245577 * Y + 0.000426105652938837 * k - 0.176491792462875) + Cr * (-0.000778269941513683 * Cr + 0.00130872261408275 * Y + 0.000770482631801132 * k - 0.151051492775562) + Y * (0.00126935368114843 * Y - 0.00265090189010898 * k + 0.25802910206845) + k * (-0.000318913117588328 * k - 0.213742400323665);
-        data[offset++] = -20.810012546947 + Cb * (-0.000570115196973677 * Cb - 2.63409051004589e-5 * Cr + 0.0020741088115012 * Y - 0.00288260236853442 * k + 0.814272968359295) + Cr * (-1.53496057440975e-5 * Cr - 0.000132689043961446 * Y + 0.000560833691242812 * k - 0.195152027534049) + Y * (0.00174418132927582 * Y - 0.00255243321439347 * k + 0.116935020465145) + k * (-0.000343531996510555 * k + 0.24165260232407);
-      }
-      return data;
-    },
-    _convertYcckToCmyk: function convertYcckToCmyk(data) {
-      var Y, Cb, Cr;
-      for (var i = 0, length = data.length; i < length; i += 4) {
-        Y = data[i];
-        Cb = data[i + 1];
-        Cr = data[i + 2];
-        data[i] = 434.456 - Y - 1.402 * Cr;
-        data[i + 1] = 119.541 - Y + 0.344 * Cb + 0.714 * Cr;
-        data[i + 2] = 481.816 - Y - 1.772 * Cb;
-      }
-      return data;
-    },
-    _convertCmykToRgb: function convertCmykToRgb(data) {
-      var c, m, y, k;
-      var offset = 0;
-      var scale = 1 / 255;
-      for (var i = 0, length = data.length; i < length; i += 4) {
-        c = data[i] * scale;
-        m = data[i + 1] * scale;
-        y = data[i + 2] * scale;
-        k = data[i + 3] * scale;
-        data[offset++] = 255 + c * (-4.387332384609988 * c + 54.48615194189176 * m + 18.82290502165302 * y + 212.25662451639585 * k - 285.2331026137004) + m * (1.7149763477362134 * m - 5.6096736904047315 * y - 17.873870861415444 * k - 5.497006427196366) + y * (-2.5217340131683033 * y - 21.248923337353073 * k + 17.5119270841813) - k * (21.86122147463605 * k + 189.48180835922747);
-        data[offset++] = 255 + c * (8.841041422036149 * c + 60.118027045597366 * m + 6.871425592049007 * y + 31.159100130055922 * k - 79.2970844816548) + m * (-15.310361306967817 * m + 17.575251261109482 * y + 131.35250912493976 * k - 190.9453302588951) + y * (4.444339102852739 * y + 9.8632861493405 * k - 24.86741582555878) - k * (20.737325471181034 * k + 187.80453709719578);
-        data[offset++] = 255 + c * (0.8842522430003296 * c + 8.078677503112928 * m + 30.89978309703729 * y - 0.23883238689178934 * k - 14.183576799673286) + m * (10.49593273432072 * m + 63.02378494754052 * y + 50.606957656360734 * k - 112.23884253719248) + y * (0.03296041114873217 * y + 115.60384449646641 * k - 193.58209356861505) - k * (22.33816807309886 * k + 180.12613974708367);
-      }
-      return data;
-    },
-    getData: function getData(width, height, forceRGBoutput) {
-      if (this.numComponents > 4) {
-        throw new JpegError('Unsupported color mode');
-      }
-      var data = this._getLinearizedBlockData(width, height);
-      if (this.numComponents === 1 && forceRGBoutput) {
-        var dataLength = data.length;
-        var rgbData = new Uint8ClampedArray(dataLength * 3);
-        var offset = 0;
-        for (var i = 0; i < dataLength; i++) {
-          var grayColor = data[i];
-          rgbData[offset++] = grayColor;
-          rgbData[offset++] = grayColor;
-          rgbData[offset++] = grayColor;
-        }
-        return rgbData;
-      } else if (this.numComponents === 3 && this._isColorConversionNeeded()) {
-        return this._convertYccToRgb(data);
-      } else if (this.numComponents === 4) {
-        if (this._isColorConversionNeeded()) {
-          if (forceRGBoutput) {
-            return this._convertYcckToRgb(data);
-          }
-          return this._convertYcckToCmyk(data);
-        } else if (forceRGBoutput) {
-          return this._convertCmykToRgb(data);
-        }
-      }
-      return data;
-    }
-  };
-  return JpegImage;
-}();
-exports.JpegImage = JpegImage;
-
-/***/ }),
-/* 26 */
+/* 29 */
 /***/ (function(module, exports, __w_pdfjs_require__) {
 
 "use strict";
 
 
 Object.defineProperty(exports, "__esModule", {
   value: true
 });
@@ -31056,17 +31143,17 @@ var CMapFactory = function CMapFactoryCl
     }
   };
 }();
 exports.CMap = CMap;
 exports.IdentityCMap = IdentityCMap;
 exports.CMapFactory = CMapFactory;
 
 /***/ }),
-/* 27 */
+/* 30 */
 /***/ (function(module, exports, __w_pdfjs_require__) {
 
 "use strict";
 
 
 Object.defineProperty(exports, "__esModule", {
   value: true
 });
@@ -31079,21 +31166,21 @@ var _cff_parser = __w_pdfjs_require__(13
 var _glyphlist = __w_pdfjs_require__(6);
 
 var _encodings = __w_pdfjs_require__(4);
 
 var _standard_fonts = __w_pdfjs_require__(14);
 
 var _unicode = __w_pdfjs_require__(15);
 
-var _font_renderer = __w_pdfjs_require__(29);
+var _font_renderer = __w_pdfjs_require__(32);
 
 var _stream = __w_pdfjs_require__(2);
 
-var _type1_parser = __w_pdfjs_require__(30);
+var _type1_parser = __w_pdfjs_require__(33);
 
 var PRIVATE_USE_OFFSET_START = 0xE000;
 var PRIVATE_USE_OFFSET_END = 0xF8FF;
 var SKIP_PRIVATE_USE_RANGE_F000_TO_F01F = false;
 var PDF_GLYPH_SPACE_UNITS = 1000;
 var SEAC_ANALYSIS_ENABLED = false;
 var FontFlags = {
   FixedPitch: 1,
@@ -33526,34 +33613,34 @@ exports.ErrorFont = ErrorFont;
 exports.Font = Font;
 exports.FontFlags = FontFlags;
 exports.ToUnicodeMap = ToUnicodeMap;
 exports.IdentityToUnicodeMap = IdentityToUnicodeMap;
 exports.ProblematicCharRanges = ProblematicCharRanges;
 exports.getFontType = getFontType;
 
 /***/ }),
-/* 28 */
+/* 31 */
 /***/ (function(module, exports, __w_pdfjs_require__) {
 
 "use strict";
 
 
 Object.defineProperty(exports, "__esModule", {
   value: true
 });
 var ISOAdobeCharset = ['.notdef', 'space', 'exclam', 'quotedbl', 'numbersign', 'dollar', 'percent', 'ampersand', 'quoteright', 'parenleft', 'parenright', 'asterisk', 'plus', 'comma', 'hyphen', 'period', 'slash', 'zero', 'one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine', 'colon', 'semicolon', 'less', 'equal', 'greater', 'question', 'at', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'bracketleft', 'backslash', 'bracketright', 'asciicircum', 'underscore', 'quoteleft', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'braceleft', 'bar', 'braceright', 'asciitilde', 'exclamdown', 'cent', 'sterling', 'fraction', 'yen', 'florin', 'section', 'currency', 'quotesingle', 'quotedblleft', 'guillemotleft', 'guilsinglleft', 'guilsinglright', 'fi', 'fl', 'endash', 'dagger', 'daggerdbl', 'periodcentered', 'paragraph', 'bullet', 'quotesinglbase', 'quotedblbase', 'quotedblright', 'guillemotright', 'ellipsis', 'perthousand', 'questiondown', 'grave', 'acute', 'circumflex', 'tilde', 'macron', 'breve', 'dotaccent', 'dieresis', 'ring', 'cedilla', 'hungarumlaut', 'ogonek', 'caron', 'emdash', 'AE', 'ordfeminine', 'Lslash', 'Oslash', 'OE', 'ordmasculine', 'ae', 'dotlessi', 'lslash', 'oslash', 'oe', 'germandbls', 'onesuperior', 'logicalnot', 'mu', 'trademark', 'Eth', 'onehalf', 'plusminus', 'Thorn', 'onequarter', 'divide', 'brokenbar', 'degree', 'thorn', 'threequarters', 'twosuperior', 'registered', 'minus', 'eth', 'multiply', 'threesuperior', 'copyright', 'Aacute', 'Acircumflex', 'Adieresis', 'Agrave', 'Aring', 'Atilde', 'Ccedilla', 'Eacute', 'Ecircumflex', 'Edieresis', 'Egrave', 'Iacute', 'Icircumflex', 'Idieresis', 'Igrave', 'Ntilde', 'Oacute', 'Ocircumflex', 'Odieresis', 'Ograve', 'Otilde', 'Scaron', 'Uacute', 'Ucircumflex', 'Udieresis', 'Ugrave', 'Yacute', 'Ydieresis', 'Zcaron', 'aacute', 'acircumflex', 'adieresis', 'agrave', 'aring', 'atilde', 'ccedilla', 'eacute', 'ecircumflex', 'edieresis', 'egrave', 'iacute', 'icircumflex', 'idieresis', 'igrave', 'ntilde', 'oacute', 'ocircumflex', 'odieresis', 'ograve', 'otilde', 'scaron', 'uacute', 'ucircumflex', 'udieresis', 'ugrave', 'yacute', 'ydieresis', 'zcaron'];
 var ExpertCharset = ['.notdef', 'space', 'exclamsmall', 'Hungarumlautsmall', 'dollaroldstyle', 'dollarsuperior', 'ampersandsmall', 'Acutesmall', 'parenleftsuperior', 'parenrightsuperior', 'twodotenleader', 'onedotenleader', 'comma', 'hyphen', 'period', 'fraction', 'zerooldstyle', 'oneoldstyle', 'twooldstyle', 'threeoldstyle', 'fouroldstyle', 'fiveoldstyle', 'sixoldstyle', 'sevenoldstyle', 'eightoldstyle', 'nineoldstyle', 'colon', 'semicolon', 'commasuperior', 'threequartersemdash', 'periodsuperior', 'questionsmall', 'asuperior', 'bsuperior', 'centsuperior', 'dsuperior', 'esuperior', 'isuperior', 'lsuperior', 'msuperior', 'nsuperior', 'osuperior', 'rsuperior', 'ssuperior', 'tsuperior', 'ff', 'fi', 'fl', 'ffi', 'ffl', 'parenleftinferior', 'parenrightinferior', 'Circumflexsmall', 'hyphensuperior', 'Gravesmall', 'Asmall', 'Bsmall', 'Csmall', 'Dsmall', 'Esmall', 'Fsmall', 'Gsmall', 'Hsmall', 'Ismall', 'Jsmall', 'Ksmall', 'Lsmall', 'Msmall', 'Nsmall', 'Osmall', 'Psmall', 'Qsmall', 'Rsmall', 'Ssmall', 'Tsmall', 'Usmall', 'Vsmall', 'Wsmall', 'Xsmall', 'Ysmall', 'Zsmall', 'colonmonetary', 'onefitted', 'rupiah', 'Tildesmall', 'exclamdownsmall', 'centoldstyle', 'Lslashsmall', 'Scaronsmall', 'Zcaronsmall', 'Dieresissmall', 'Brevesmall', 'Caronsmall', 'Dotaccentsmall', 'Macronsmall', 'figuredash', 'hypheninferior', 'Ogoneksmall', 'Ringsmall', 'Cedillasmall', 'onequarter', 'onehalf', 'threequarters', 'questiondownsmall', 'oneeighth', 'threeeighths', 'fiveeighths', 'seveneighths', 'onethird', 'twothirds', 'zerosuperior', 'onesuperior', 'twosuperior', 'threesuperior', 'foursuperior', 'fivesuperior', 'sixsuperior', 'sevensuperior', 'eightsuperior', 'ninesuperior', 'zeroinferior', 'oneinferior', 'twoinferior', 'threeinferior', 'fourinferior', 'fiveinferior', 'sixinferior', 'seveninferior', 'eightinferior', 'nineinferior', 'centinferior', 'dollarinferior', 'periodinferior', 'commainferior', 'Agravesmall', 'Aacutesmall', 'Acircumflexsmall', 'Atildesmall', 'Adieresissmall', 'Aringsmall', 'AEsmall', 'Ccedillasmall', 'Egravesmall', 'Eacutesmall', 'Ecircumflexsmall', 'Edieresissmall', 'Igravesmall', 'Iacutesmall', 'Icircumflexsmall', 'Idieresissmall', 'Ethsmall', 'Ntildesmall', 'Ogravesmall', 'Oacutesmall', 'Ocircumflexsmall', 'Otildesmall', 'Odieresissmall', 'OEsmall', 'Oslashsmall', 'Ugravesmall', 'Uacutesmall', 'Ucircumflexsmall', 'Udieresissmall', 'Yacutesmall', 'Thornsmall', 'Ydieresissmall'];
 var ExpertSubsetCharset = ['.notdef', 'space', 'dollaroldstyle', 'dollarsuperior', 'parenleftsuperior', 'parenrightsuperior', 'twodotenleader', 'onedotenleader', 'comma', 'hyphen', 'period', 'fraction', 'zerooldstyle', 'oneoldstyle', 'twooldstyle', 'threeoldstyle', 'fouroldstyle', 'fiveoldstyle', 'sixoldstyle', 'sevenoldstyle', 'eightoldstyle', 'nineoldstyle', 'colon', 'semicolon', 'commasuperior', 'threequartersemdash', 'periodsuperior', 'asuperior', 'bsuperior', 'centsuperior', 'dsuperior', 'esuperior', 'isuperior', 'lsuperior', 'msuperior', 'nsuperior', 'osuperior', 'rsuperior', 'ssuperior', 'tsuperior', 'ff', 'fi', 'fl', 'ffi', 'ffl', 'parenleftinferior', 'parenrightinferior', 'hyphensuperior', 'colonmonetary', 'onefitted', 'rupiah', 'centoldstyle', 'figuredash', 'hypheninferior', 'onequarter', 'onehalf', 'threequarters', 'oneeighth', 'threeeighths', 'fiveeighths', 'seveneighths', 'onethird', 'twothirds', 'zerosuperior', 'onesuperior', 'twosuperior', 'threesuperior', 'foursuperior', 'fivesuperior', 'sixsuperior', 'sevensuperior', 'eightsuperior', 'ninesuperior', 'zeroinferior', 'oneinferior', 'twoinferior', 'threeinferior', 'fourinferior', 'fiveinferior', 'sixinferior', 'seveninferior', 'eightinferior', 'nineinferior', 'centinferior', 'dollarinferior', 'periodinferior', 'commainferior'];
 exports.ISOAdobeCharset = ISOAdobeCharset;
 exports.ExpertCharset = ExpertCharset;
 exports.ExpertSubsetCharset = ExpertSubsetCharset;
 
 /***/ }),
-/* 29 */
+/* 32 */
 /***/ (function(module, exports, __w_pdfjs_require__) {
 
 "use strict";
 
 
 Object.defineProperty(exports, "__esModule", {
   value: true
 });
@@ -34283,17 +34370,17 @@ var FontRendererFactory = function FontR
       }
       return new Type2Compiled(cff, cmap, font.fontMatrix, font.glyphNameMap);
     }
   };
 }();
 exports.FontRendererFactory = FontRendererFactory;
 
 /***/ }),
-/* 30 */
+/* 33 */
 /***/ (function(module, exports, __w_pdfjs_require__) {
 
 "use strict";
 
 
 Object.defineProperty(exports, "__esModule", {
   value: true
 });
@@ -34846,17 +34933,17 @@ var Type1Parser = function Type1ParserCl
       }
     }
   };
   return Type1Parser;
 }();
 exports.Type1Parser = Type1Parser;
 
 /***/ }),
-/* 31 */
+/* 34 */
 /***/ (function(module, exports, __w_pdfjs_require__) {
 
 "use strict";
 
 
 Object.defineProperty(exports, "__esModule", {
   value: true
 });
@@ -35636,17 +35723,17 @@ function getTilingPatternIR(operatorList
     throw new _util.FormatError(`Invalid getTilingPatternIR /BBox array: [${bbox}].`);
   }
   return ['TilingPattern', args, operatorList, matrix, bbox, xstep, ystep, paintType, tilingType];
 }
 exports.Pattern = Pattern;
 exports.getTilingPatternIR = getTilingPatternIR;
 
 /***/ }),
-/* 32 */
+/* 35 */
 /***/ (function(module, exports, __w_pdfjs_require__) {
 
 "use strict";
 
 
 Object.defineProperty(exports, "__esModule", {
   value: true
 });
@@ -35881,17 +35968,17 @@ function bidi(str, startLevel, vertical)
       chars[i] = '';
     }
   }
   return createBidiText(chars.join(''), isLTR);
 }
 exports.bidi = bidi;
 
 /***/ }),
-/* 33 */
+/* 36 */
 /***/ (function(module, exports, __w_pdfjs_require__) {
 
 "use strict";
 
 
 Object.defineProperty(exports, "__esModule", {
   value: true
 });
@@ -38835,17 +38922,17 @@ var getMetrics = (0, _util.getLookupTabl
     t['a189'] = 927;
     t['a190'] = 970;
     t['a191'] = 918;
   });
 });
 exports.getMetrics = getMetrics;
 
 /***/ }),
-/* 34 */
+/* 37 */
 /***/ (function(module, exports, __w_pdfjs_require__) {
 
 "use strict";
 
 
 Object.defineProperty(exports, "__esModule", {
   value: true
 });
@@ -39039,17 +39126,17 @@ var PostScriptLexer = function PostScrip
     }
   };
   return PostScriptLexer;
 }();
 exports.PostScriptLexer = PostScriptLexer;
 exports.PostScriptParser = PostScriptParser;
 
 /***/ }),
-/* 35 */
+/* 38 */
 /***/ (function(module, exports, __w_pdfjs_require__) {
 
 "use strict";
 
 
 Object.defineProperty(exports, "__esModule", {
   value: true
 });
@@ -39153,17 +39240,17 @@ var MurmurHash3_64 = function MurmurHash
       return str;
     }
   };
   return MurmurHash3_64;
 }();
 exports.MurmurHash3_64 = MurmurHash3_64;
 
 /***/ }),
-/* 36 */
+/* 39 */
 /***/ (function(module, exports, __w_pdfjs_require__) {
 
 "use strict";
 
 
 Object.defineProperty(exports, "__esModule", {
   value: true
 });
@@ -39172,17 +39259,17 @@ exports.PDFImage = undefined;
 var _util = __w_pdfjs_require__(0);
 
 var _stream = __w_pdfjs_require__(2);
 
 var _primitives = __w_pdfjs_require__(1);
 
 var _colorspace = __w_pdfjs_require__(3);
 
-var _jpx = __w_pdfjs_require__(10);
+var _jpx = __w_pdfjs_require__(9);
 
 var PDFImage = function PDFImageClosure() {
   function handleImageData(image, nativeDecoder) {
     if (nativeDecoder && nativeDecoder.canDecode(image)) {
       return nativeDecoder.decode(image);
     }
     return Promise.resolve(image);
   }
@@ -39661,17 +39748,17 @@ var PDFImage = function PDFImageClosure(
       return this.image.getBytes(length);
     }
   };
   return PDFImage;
 }();
 exports.PDFImage = PDFImage;
 
 /***/ }),
-/* 37 */
+/* 40 */
 /***/ (function(module, exports, __w_pdfjs_require__) {
 
 "use strict";
 
 
 Object.defineProperty(exports, "__esModule", {
   value: true
 });
--- a/browser/tools/mozscreenshots/mozscreenshots/extension/configurations/LightweightThemes.jsm
+++ b/browser/tools/mozscreenshots/mozscreenshots/extension/configurations/LightweightThemes.jsm
@@ -66,10 +66,24 @@ this.LightweightThemes = {
         // Wait for LWT listener
         return new Promise(resolve => {
           setTimeout(() => {
             resolve("lightLWT");
           }, 500);
         });
       },
     },
+
+    compactLight: {
+      selectors: ["#navigator-toolbox"],
+      applyConfig() {
+        LightweightThemeManager.currentTheme = LightweightThemeManager.getUsedTheme("firefox-compact-light@mozilla.org");
+      },
+    },
+
+    compactDark: {
+      selectors: ["#navigator-toolbox"],
+      applyConfig() {
+        LightweightThemeManager.currentTheme = LightweightThemeManager.getUsedTheme("firefox-compact-dark@mozilla.org");
+      },
+    },
   },
 };
--- a/build/moz.configure/rust.configure
+++ b/build/moz.configure/rust.configure
@@ -53,17 +53,17 @@ def rust_compiler(rustc_info, cargo_info
         die(dedent('''\
         Rust compiler not found.
         To compile rust language sources, you must have 'rustc' in your path.
         See https://www.rust-lang.org/ for more information.
 
         You can install rust by running './mach bootstrap'
         or by directly running the installer from https://rustup.rs/
         '''))
-    rustc_min_version = Version('1.20.0')
+    rustc_min_version = Version('1.21.0')
     cargo_min_version = Version('0.{}'.format(rustc_min_version.minor + 1))
 
     version = rustc_info.version
     if version < rustc_min_version:
         die(dedent('''\
         Rust compiler {} is too old.
 
         To compile Rust language sources please install at least
--- a/caps/nsScriptSecurityManager.cpp
+++ b/caps/nsScriptSecurityManager.cpp
@@ -1436,17 +1436,17 @@ nsScriptSecurityManager::InitStatics()
 
 // Currently this nsGenericFactory constructor is used only from FastLoad
 // (XPCOM object deserialization) code, when "creating" the system principal
 // singleton.
 already_AddRefed<SystemPrincipal>
 nsScriptSecurityManager::SystemPrincipalSingletonConstructor()
 {
     if (gScriptSecMan)
-        return do_AddRef(gScriptSecMan->mSystemPrincipal.get()).downcast<SystemPrincipal>();
+        return do_AddRef(gScriptSecMan->mSystemPrincipal).downcast<SystemPrincipal>();
     return nullptr;
 }
 
 struct IsWhitespace {
     static bool Test(char aChar) { return NS_IsAsciiWhitespace(aChar); };
 };
 struct IsWhitespaceOrComma {
     static bool Test(char aChar) { return aChar == ',' || NS_IsAsciiWhitespace(aChar); };
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/changes/actions/index.js
@@ -0,0 +1,14 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+const { createEnum } = require("devtools/client/shared/enum");
+
+createEnum([
+
+  // Update the entire changes state with the new list of changes.
+  "UPDATE_CHANGES",
+
+], module.exports);
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/changes/actions/moz.build
@@ -0,0 +1,9 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+DevToolsModules(
+    'index.js',
+)
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/changes/changes.js
@@ -0,0 +1,52 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+const { createFactory, createElement } = require("devtools/client/shared/vendor/react");
+const { Provider } = require("devtools/client/shared/vendor/react-redux");
+
+const ChangesApp = createFactory(require("./components/ChangesApp"));
+
+const { LocalizationHelper } = require("devtools/shared/l10n");
+const INSPECTOR_L10N =
+  new LocalizationHelper("devtools/client/locales/inspector.properties");
+
+class ChangesView {
+
+  constructor(inspector, window) {
+    this.document = window.document;
+    this.inspector = inspector;
+    this.store = inspector.store;
+
+    this.init();
+  }
+
+  init() {
+    if (!this.inspector) {
+      return;
+    }
+
+    let changesApp = ChangesApp({});
+
+    let provider = createElement(Provider, {
+      id: "changesview",
+      key: "changesview",
+      store: this.store,
+      title: INSPECTOR_L10N.getStr("inspector.sidebar.changesViewTitle")
+    }, changesApp);
+
+    // Expose the provider to let inspector.js use it in setupSidebar.
+    this.provider = provider;
+  }
+
+  destroy() {
+    this.document = null;
+    this.inspector = null;
+    this.store = null;
+  }
+
+}
+
+module.exports = ChangesView;
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/changes/components/ChangesApp.js
@@ -0,0 +1,27 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+const {
+  DOM: dom,
+  PureComponent,
+} = require("devtools/client/shared/vendor/react");
+const { connect } = require("devtools/client/shared/vendor/react-redux");
+
+class ChangesApp extends PureComponent {
+  static get propTypes() {
+    return {};
+  }
+
+  render() {
+    return dom.div(
+      {
+        id: "changes-container",
+      }
+    );
+  }
+}
+
+module.exports = connect(state => state)(ChangesApp);
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/changes/components/moz.build
@@ -0,0 +1,9 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+DevToolsModules(
+    'ChangesApp.js',
+)
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/changes/moz.build
@@ -0,0 +1,15 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+DIRS += [
+    'actions',
+    'components',
+    'reducers',
+]
+
+DevToolsModules(
+    'changes.js',
+)
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/changes/reducers/changes.js
@@ -0,0 +1,19 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+const INITIAL_CHANGES = [];
+
+let reducers = {
+
+};
+
+module.exports = function (changes = INITIAL_CHANGES, action) {
+  let reducer = reducers[action.type];
+  if (!reducer) {
+    return changes;
+  }
+  return reducer(changes, action);
+};
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/changes/reducers/index.js
@@ -0,0 +1,7 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+exports.changes = require("./changes");
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/changes/reducers/moz.build
@@ -0,0 +1,10 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+DevToolsModules(
+    'changes.js',
+    'index.js',
+)
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/events/actions/index.js
@@ -0,0 +1,14 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+const { createEnum } = require("devtools/client/shared/enum");
+
+createEnum([
+
+  // Update the entire events state with the new list of events.
+  "UPDATE_EVENTS",
+
+], module.exports);
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/events/actions/moz.build
@@ -0,0 +1,9 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+DevToolsModules(
+    'index.js',
+)
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/events/components/EventsApp.js
@@ -0,0 +1,27 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+const {
+  DOM: dom,
+  PureComponent,
+} = require("devtools/client/shared/vendor/react");
+const { connect } = require("devtools/client/shared/vendor/react-redux");
+
+class EventsApp extends PureComponent {
+  static get propTypes() {
+    return {};
+  }
+
+  render() {
+    return dom.div(
+      {
+        id: "events-container",
+      }
+    );
+  }
+}
+
+module.exports = connect(state => state)(EventsApp);
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/events/components/moz.build
@@ -0,0 +1,9 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+DevToolsModules(
+    'EventsApp.js',
+)
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/events/events.js
@@ -0,0 +1,52 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+const { createFactory, createElement } = require("devtools/client/shared/vendor/react");
+const { Provider } = require("devtools/client/shared/vendor/react-redux");
+
+const EventsApp = createFactory(require("./components/EventsApp"));
+
+const { LocalizationHelper } = require("devtools/shared/l10n");
+const INSPECTOR_L10N =
+  new LocalizationHelper("devtools/client/locales/inspector.properties");
+
+class EventsView {
+
+  constructor(inspector, window) {
+    this.document = window.document;
+    this.inspector = inspector;
+    this.store = inspector.store;
+
+    this.init();
+  }
+
+  init() {
+    if (!this.inspector) {
+      return;
+    }
+
+    let eventsApp = EventsApp({});
+
+    let provider = createElement(Provider, {
+      id: "eventsview",
+      key: "eventsview",
+      store: this.store,
+      title: INSPECTOR_L10N.getStr("inspector.sidebar.eventsViewTitle")
+    }, eventsApp);
+
+    // Expose the provider to let inspector.js use it in setupSidebar.
+    this.provider = provider;
+  }
+
+  destroy() {
+    this.document = null;
+    this.inspector = null;
+    this.store = null;
+  }
+
+}
+
+module.exports = EventsView;
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/events/moz.build
@@ -0,0 +1,15 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+DIRS += [
+    'actions',
+    'components',
+    'reducers',
+]
+
+DevToolsModules(
+    'events.js',
+)
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/events/reducers/events.js
@@ -0,0 +1,19 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+const INITIAL_EVENTS = [];
+
+let reducers = {
+
+};
+
+module.exports = function (events = INITIAL_EVENTS, action) {
+  let reducer = reducers[action.type];
+  if (!reducer) {
+    return events;
+  }
+  return reducer(events, action);
+};
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/events/reducers/index.js
@@ -0,0 +1,7 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+exports.events = require("./events");
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/events/reducers/moz.build
@@ -0,0 +1,10 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+DevToolsModules(
+    'events.js',
+    'index.js',
+)
--- a/devtools/client/inspector/extensions/extension-sidebar.js
+++ b/devtools/client/inspector/extensions/extension-sidebar.js
@@ -1,18 +1,15 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
-const {
-  createElement, createFactory,
-} = require("devtools/client/shared/vendor/react");
-
+const { createElement, createFactory } = require("devtools/client/shared/vendor/react");
 const { Provider } = require("devtools/client/shared/vendor/react-redux");
 
 const ExtensionSidebarComponent = createFactory(require("./components/ExtensionSidebar"));
 
 const {
   updateObjectTreeView,
   removeExtensionSidebar,
 } = require("./actions/sidebar");
--- a/devtools/client/inspector/flexbox/actions/index.js
+++ b/devtools/client/inspector/flexbox/actions/index.js
@@ -3,9 +3,12 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const { createEnum } = require("devtools/client/shared/enum");
 
 createEnum([
 
+  // Update the entire flexboxes state with the new list of flexboxes.
+  "UPDATE_FLEXBOXES",
+
 ], module.exports);
--- a/devtools/client/inspector/flexbox/types.js
+++ b/devtools/client/inspector/flexbox/types.js
@@ -1,9 +1,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
+const { PropTypes } = require("devtools/client/shared/vendor/react");
+
 exports.flexbox = {
 
+  // The id of the flexbox container.
+  id: PropTypes.number,
+
+  // The node front of the flexbox container.
+  nodeFront: PropTypes.object,
+
 };
rename from devtools/client/inspector/fonts/components/App.js
rename to devtools/client/inspector/fonts/components/FontsApp.js
--- a/devtools/client/inspector/fonts/components/App.js
+++ b/devtools/client/inspector/fonts/components/FontsApp.js
@@ -15,17 +15,17 @@ const { connect } = require("devtools/cl
 const SearchBox = createFactory(require("devtools/client/shared/components/SearchBox"));
 const FontList = createFactory(require("./FontList"));
 
 const { getStr } = require("../utils/l10n");
 const Types = require("../types");
 
 const PREVIEW_UPDATE_DELAY = 150;
 
-class App extends PureComponent {
+class FontsApp extends PureComponent {
   static get propTypes() {
     return {
       fonts: PropTypes.arrayOf(PropTypes.shape(Types.font)).isRequired,
       onPreviewFonts: PropTypes.func.isRequired,
       onShowAllFont: PropTypes.func.isRequired,
     };
   }
 
@@ -69,9 +69,9 @@ class App extends PureComponent {
             className: "devtools-sidepanel-no-result"
           },
           getStr("fontinspector.noFontsOnSelectedElement")
         )
     );
   }
 }
 
-module.exports = connect(state => state)(App);
+module.exports = connect(state => state)(FontsApp);
--- a/devtools/client/inspector/fonts/components/moz.build
+++ b/devtools/client/inspector/fonts/components/moz.build
@@ -1,11 +1,11 @@
 # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 DevToolsModules(
-    'App.js',
     'Font.js',
     'FontList.js',
+    'FontsApp.js',
 )
--- a/devtools/client/inspector/fonts/fonts.js
+++ b/devtools/client/inspector/fonts/fonts.js
@@ -1,140 +1,140 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
-const { Task } = require("devtools/shared/task");
 const { getColor } = require("devtools/client/shared/theme");
 
 const { createFactory, createElement } = require("devtools/client/shared/vendor/react");
 const { Provider } = require("devtools/client/shared/vendor/react-redux");
 
 const { gDevTools } = require("devtools/client/framework/devtools");
 
-const App = createFactory(require("./components/App"));
+const FontsApp = createFactory(require("./components/FontsApp"));
 
 const { LocalizationHelper } = require("devtools/shared/l10n");
 const INSPECTOR_L10N =
   new LocalizationHelper("devtools/client/locales/inspector.properties");
 
 const { updateFonts } = require("./actions/fonts");
 const { updatePreviewText, updateShowAllFonts } = require("./actions/font-options");
 
-function FontInspector(inspector, window) {
-  this.document = window.document;
-  this.inspector = inspector;
-  this.pageStyle = this.inspector.pageStyle;
-  this.store = this.inspector.store;
+class FontInspector {
 
-  this.update = this.update.bind(this);
+  constructor(inspector, window) {
+    this.document = window.document;
+    this.inspector = inspector;
+    this.pageStyle = this.inspector.pageStyle;
+    this.store = this.inspector.store;
 
-  this.onNewNode = this.onNewNode.bind(this);
-  this.onPreviewFonts = this.onPreviewFonts.bind(this);
-  this.onShowAllFont = this.onShowAllFont.bind(this);
-  this.onThemeChanged = this.onThemeChanged.bind(this);
+    this.update = this.update.bind(this);
 
-  this.init();
-}
+    this.onNewNode = this.onNewNode.bind(this);
+    this.onPreviewFonts = this.onPreviewFonts.bind(this);
+    this.onShowAllFont = this.onShowAllFont.bind(this);
+    this.onThemeChanged = this.onThemeChanged.bind(this);
 
-FontInspector.prototype = {
+    this.init();
+  }
+
   init() {
     if (!this.inspector) {
       return;
     }
 
-    let app = App({
+    let fontsApp = FontsApp({
       onPreviewFonts: this.onPreviewFonts,
       onShowAllFont: this.onShowAllFont,
     });
 
     let provider = createElement(Provider, {
       id: "fontinspector",
       key: "fontinspector",
       store: this.store,
       title: INSPECTOR_L10N.getStr("inspector.sidebar.fontInspectorTitle"),
-    }, app);
+    }, fontsApp);
 
     // Expose the provider to let inspector.js use it in setupSidebar.
     this.provider = provider;
 
     this.inspector.selection.on("new-node-front", this.onNewNode);
     this.inspector.sidebar.on("fontinspector-selected", this.onNewNode);
 
     // Listen for theme changes as the color of the previews depend on the theme
     gDevTools.on("theme-switched", this.onThemeChanged);
 
     this.store.dispatch(updatePreviewText(""));
     this.store.dispatch(updateShowAllFonts(false));
     this.update(false, "");
-  },
+  }
 
   /**
    * Destruction function called when the inspector is destroyed. Removes event listeners
    * and cleans up references.
    */
-  destroy: function () {
+  destroy() {
     this.inspector.selection.off("new-node-front", this.onNewNode);
     this.inspector.sidebar.off("fontinspector-selected", this.onNewNode);
     gDevTools.off("theme-switched", this.onThemeChanged);
 
     this.document = null;
     this.inspector = null;
     this.pageStyle = null;
     this.store = null;
-  },
+  }
 
   /**
    * Returns true if the font inspector panel is visible, and false otherwise.
    */
   isPanelVisible() {
     return this.inspector.sidebar &&
            this.inspector.sidebar.getCurrentTabID() === "fontinspector";
-  },
+  }
 
   /**
    * Selection 'new-node' event handler.
    */
   onNewNode() {
     if (this.isPanelVisible()) {
       this.store.dispatch(updateShowAllFonts(false));
       this.update();
     }
-  },
+  }
+
+  /**
+   * Handler for change in preview input.
+   */
+  onPreviewFonts(value) {
+    this.store.dispatch(updatePreviewText(value));
+    this.update();
+  }
+
+  /**
+   * Handler for click on show all fonts button.
+   */
+  onShowAllFont() {
+    this.store.dispatch(updateShowAllFonts(true));
+    this.update();
+  }
 
   /**
    * Handler for the "theme-switched" event.
    */
   onThemeChanged(event, frame) {
     if (frame === this.document.defaultView) {
       this.update();
     }
-  },
-
-  /**
-   * Handler for change in preview input.
-   */
-  onPreviewFonts(value) {
-    this.store.dispatch(updatePreviewText(value));
-    this.update();
-  },
+  }
 
-  /**
-   * Handler for click on show all fonts button.
-   */
-  onShowAllFont() {
-    this.store.dispatch(updateShowAllFonts(true));
-    this.update();
-  },
-
-  update: Task.async(function* () {
+  async update() {
     // Stop refreshing if the inspector or store is already destroyed.
     if (!this.inspector || !this.store) {
       return;
     }
 
     let node = this.inspector.selection.nodeFront;
     let fonts = [];
     let { fontOptions } = this.store.getState();
@@ -153,37 +153,38 @@ FontInspector.prototype = {
 
     let options = {
       includePreviews: true,
       previewText,
       previewFillStyle: getColor("body-color")
     };
 
     if (showAllFonts) {
-      fonts = yield this.pageStyle.getAllUsedFontFaces(options)
+      fonts = await this.pageStyle.getAllUsedFontFaces(options)
                       .catch(console.error);
     } else {
-      fonts = yield this.pageStyle.getUsedFontFaces(node, options)
+      fonts = await this.pageStyle.getUsedFontFaces(node, options)
                       .catch(console.error);
     }
 
     if (!fonts || !fonts.length) {
       // No fonts to display. Clear the previously shown fonts.
       this.store.dispatch(updateFonts(fonts));
       return;
     }
 
     for (let font of fonts) {
-      font.previewUrl = yield font.preview.data.string();
+      font.previewUrl = await font.preview.data.string();
     }
 
     // in case we've been destroyed in the meantime
     if (!this.document) {
       return;
     }
 
     this.store.dispatch(updateFonts(fonts));
 
     this.inspector.emit("fontinspector-updated");
-  })
-};
+  }
+
+}
 
 module.exports = FontInspector;
--- a/devtools/client/inspector/grids/grid-inspector.js
+++ b/devtools/client/inspector/grids/grid-inspector.js
@@ -1,16 +1,15 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const Services = require("Services");
-const { Task } = require("devtools/shared/task");
 
 const SwatchColorPickerTooltip = require("devtools/client/shared/widgets/tooltip/SwatchColorPickerTooltip");
 const { throttle } = require("devtools/client/inspector/shared/utils");
 const { compareFragmentsGeometry } = require("devtools/client/inspector/grids/utils/utils");
 
 const {
   updateGridColor,
   updateGridHighlighted,
@@ -39,55 +38,55 @@ const GRID_COLORS = [
   "#D70022",
   "#4B42FF",
   "#B5007F",
   "#058B00",
   "#A47F00",
   "#005A71"
 ];
 
-function GridInspector(inspector, window) {
-  this.document = window.document;
-  this.highlighters = inspector.highlighters;
-  this.inspector = inspector;
-  this.store = inspector.store;
-  this.telemetry = inspector.telemetry;
-  this.walker = this.inspector.walker;
+class GridInspector {
 
-  this.getSwatchColorPickerTooltip = this.getSwatchColorPickerTooltip.bind(this);
-  this.updateGridPanel = this.updateGridPanel.bind(this);
+  constructor(inspector, window) {
+    this.document = window.document;
+    this.highlighters = inspector.highlighters;
+    this.inspector = inspector;
+    this.store = inspector.store;
+    this.telemetry = inspector.telemetry;
+    this.walker = this.inspector.walker;
+
+    this.getSwatchColorPickerTooltip = this.getSwatchColorPickerTooltip.bind(this);
+    this.updateGridPanel = this.updateGridPanel.bind(this);
 
-  this.onHighlighterChange = this.onHighlighterChange.bind(this);
-  this.onNavigate = this.onNavigate.bind(this);
-  this.onReflow = throttle(this.onReflow, 500, this);
-  this.onSetGridOverlayColor = this.onSetGridOverlayColor.bind(this);
-  this.onShowGridAreaHighlight = this.onShowGridAreaHighlight.bind(this);
-  this.onShowGridCellHighlight = this.onShowGridCellHighlight.bind(this);
-  this.onShowGridLineNamesHighlight = this.onShowGridLineNamesHighlight.bind(this);
-  this.onSidebarSelect = this.onSidebarSelect.bind(this);
-  this.onToggleGridHighlighter = this.onToggleGridHighlighter.bind(this);
-  this.onToggleShowGridAreas = this.onToggleShowGridAreas.bind(this);
-  this.onToggleShowGridLineNumbers = this.onToggleShowGridLineNumbers.bind(this);
-  this.onToggleShowInfiniteLines = this.onToggleShowInfiniteLines.bind(this);
+    this.onHighlighterChange = this.onHighlighterChange.bind(this);
+    this.onNavigate = this.onNavigate.bind(this);
+    this.onReflow = throttle(this.onReflow, 500, this);
+    this.onSetGridOverlayColor = this.onSetGridOverlayColor.bind(this);
+    this.onShowGridAreaHighlight = this.onShowGridAreaHighlight.bind(this);
+    this.onShowGridCellHighlight = this.onShowGridCellHighlight.bind(this);
+    this.onShowGridLineNamesHighlight = this.onShowGridLineNamesHighlight.bind(this);
+    this.onSidebarSelect = this.onSidebarSelect.bind(this);
+    this.onToggleGridHighlighter = this.onToggleGridHighlighter.bind(this);
+    this.onToggleShowGridAreas = this.onToggleShowGridAreas.bind(this);
+    this.onToggleShowGridLineNumbers = this.onToggleShowGridLineNumbers.bind(this);
+    this.onToggleShowInfiniteLines = this.onToggleShowInfiniteLines.bind(this);
 
-  this.init();
-}
-
-GridInspector.prototype = {
+    this.init();
+  }
 
   /**
    * Initializes the grid inspector by fetching the LayoutFront from the walker, loading
    * the highlighter settings and initalizing the SwatchColorPicker instance.
    */
-  init: Task.async(function* () {
+  async init() {
     if (!this.inspector) {
       return;
     }
 
-    this.layoutInspector = yield this.inspector.walker.getLayoutInspector();
+    this.layoutInspector = await this.inspector.walker.getLayoutInspector();
 
     this.loadHighlighterSettings();
 
     // Create a shared SwatchColorPicker instance to be reused by all GridItem components.
     this.swatchColorPickerTooltip = new SwatchColorPickerTooltip(
       this.inspector.toolbox.doc,
       this.inspector,
       {
@@ -96,17 +95,17 @@ GridInspector.prototype = {
     );
 
     this.highlighters.on("grid-highlighter-hidden", this.onHighlighterChange);
     this.highlighters.on("grid-highlighter-shown", this.onHighlighterChange);
     this.inspector.sidebar.on("select", this.onSidebarSelect);
     this.inspector.on("new-root", this.onNavigate);
 
     this.onSidebarSelect();
-  }),
+  }
 
   /**
    * Destruction function called when the inspector is destroyed. Removes event listeners
    * and cleans up references.
    */
   destroy() {
     this.highlighters.off("grid-highlighter-hidden", this.onHighlighterChange);
     this.highlighters.off("grid-highlighter-shown", this.onHighlighterChange);
@@ -123,31 +122,31 @@ GridInspector.prototype = {
 
     this.document = null;
     this.highlighters = null;
     this.inspector = null;
     this.layoutInspector = null;
     this.store = null;
     this.swatchColorPickerTooltip = null;
     this.walker = null;
-  },
+  }
 
   getComponentProps() {
     return {
       getSwatchColorPickerTooltip: this.getSwatchColorPickerTooltip,
       onSetGridOverlayColor: this.onSetGridOverlayColor,
       onShowGridAreaHighlight: this.onShowGridAreaHighlight,
       onShowGridCellHighlight: this.onShowGridCellHighlight,
       onShowGridLineNamesHighlight: this.onShowGridLineNamesHighlight,
       onToggleGridHighlighter: this.onToggleGridHighlighter,
       onToggleShowGridAreas: this.onToggleShowGridAreas,
       onToggleShowGridLineNumbers: this.onToggleShowGridLineNumbers,
       onToggleShowInfiniteLines: this.onToggleShowInfiniteLines,
     };
-  },
+  }
 
   /**
    * Returns the initial color linked to a grid container. Will attempt to check the
    * current grid highlighter state and the store.
    *
    * @param  {NodeFront} nodeFront
    *         The NodeFront for which we need the color.
    * @param  {String} fallbackColor
@@ -164,17 +163,17 @@ GridInspector.prototype = {
       // options.
       color = this.highlighters.state.grid.options.color;
     } else {
       // Otherwise use the color defined in the store for this node front.
       color = this.getGridColorForNodeFront(nodeFront);
     }
 
     return color || fallbackColor;
-  },
+  }
 
   /**
    * Returns the color set for the grid highlighter associated with the provided
    * nodeFront.
    *
    * @param  {NodeFront} nodeFront
    *         The NodeFront for which we need the color.
    */
@@ -183,17 +182,17 @@ GridInspector.prototype = {
 
     for (let grid of grids) {
       if (grid.nodeFront === nodeFront) {
         return grid.color;
       }
     }
 
     return null;
-  },
+  }
 
   /**
    * Create a highlighter settings object for the provided nodeFront.
    *
    * @param  {NodeFront} nodeFront
    *         The NodeFront for which we need highlighter settings.
    */
   getGridHighlighterSettings(nodeFront) {
@@ -201,24 +200,24 @@ GridInspector.prototype = {
 
     // Get the grid color for the provided nodeFront.
     let color = this.getGridColorForNodeFront(nodeFront);
 
     // Merge the grid color to the generic highlighter settings.
     return Object.assign({}, highlighterSettings, {
       color
     });
-  },
+  }
 
   /**
    * Retrieve the shared SwatchColorPicker instance.
    */
   getSwatchColorPickerTooltip() {
     return this.swatchColorPickerTooltip;
-  },
+  }
 
   /**
    * Given a list of new grid fronts, and if we have a currently highlighted grid, check
    * if its fragments have changed.
    *
    * @param  {Array} newGridFronts
    *         A list of GridFront objects.
    * @return {Boolean}
@@ -234,72 +233,72 @@ GridInspector.prototype = {
       return false;
     }
 
     const { grids } = this.store.getState();
     const oldFragments = grids.find(g => g.nodeFront === currentNode).gridFragments;
     const newFragments = newGridFront.gridFragments;
 
     return !compareFragmentsGeometry(oldFragments, newFragments);
-  },
+  }
 
   /**
    * Returns true if the layout panel is visible, and false otherwise.
    */
   isPanelVisible() {
     return this.inspector && this.inspector.toolbox && this.inspector.sidebar &&
            this.inspector.toolbox.currentToolId === "inspector" &&
            this.inspector.sidebar.getCurrentTabID() === "layoutview";
-  },
+  }
 
   /**
    * Load the grid highligher display settings into the store from the stored preferences.
    */
   loadHighlighterSettings() {
     let { dispatch } = this.store;
 
     let showGridAreas = Services.prefs.getBoolPref(SHOW_GRID_AREAS);
     let showGridLineNumbers = Services.prefs.getBoolPref(SHOW_GRID_LINE_NUMBERS);
     let showInfinteLines = Services.prefs.getBoolPref(SHOW_INFINITE_LINES_PREF);
 
     dispatch(updateShowGridAreas(showGridAreas));
     dispatch(updateShowGridLineNumbers(showGridLineNumbers));
     dispatch(updateShowInfiniteLines(showInfinteLines));
-  },
+  }
 
   showGridHighlighter(node, settings) {
     this.lastHighlighterColor = settings.color;
     this.lastHighlighterNode = node;
     this.lastHighlighterState = true;
 
     this.highlighters.showGridHighlighter(node, settings);
-  },
+  }
 
   toggleGridHighlighter(node, settings) {
     this.lastHighlighterColor = settings.color;
     this.lastHighlighterNode = node;
     this.lastHighlighterState = node !== this.highlighters.gridHighlighterShown;
 
     this.highlighters.toggleGridHighlighter(node, settings, "grid");
-  },
+  }
 
   /**
    * Updates the grid panel by dispatching the new grid data. This is called when the
    * layout view becomes visible or the view needs to be updated with new grid data.
    */
-  updateGridPanel: Task.async(function* () {
+  async updateGridPanel() {
     // Stop refreshing if the inspector or store is already destroyed.
     if (!this.inspector || !this.store) {
       return;
     }
 
     // Get all the GridFront from the server if no gridFronts were provided.
     let gridFronts;
     try {
-      gridFronts = yield this.layoutInspector.getAllGrids(this.walker.rootNode);
+      gridFronts = await this.layoutInspector.getAllGrids(this.walker.rootNode);
     } catch (e) {
       // This call might fail if called asynchrously after the toolbox is finished
       // closing.
       return;
     }
 
     // Log how many CSS Grid elements DevTools sees.
     if (gridFronts.length > 0 &&
@@ -314,17 +313,17 @@ GridInspector.prototype = {
 
       let nodeFront = grid.containerNodeFront;
 
       // If the GridFront didn't yet have access to the NodeFront for its container, then
       // get it from the walker. This happens when the walker hasn't yet seen this
       // particular DOM Node in the tree yet, or when we are connected to an older server.
       if (!nodeFront) {
         try {
-          nodeFront = yield this.walker.getNodeFromActor(grid.actorID, ["containerEl"]);
+          nodeFront = await this.walker.getNodeFromActor(grid.actorID, ["containerEl"]);
         } catch (e) {
           // This call might fail if called asynchrously after the toolbox is finished
           // closing.
           return;
         }
       }
 
       let fallbackColor = GRID_COLORS[i % GRID_COLORS.length];
@@ -336,17 +335,17 @@ GridInspector.prototype = {
         color,
         gridFragments: grid.gridFragments,
         highlighted: nodeFront == this.highlighters.gridHighlighterShown,
         nodeFront,
       });
     }
 
     this.store.dispatch(updateGrids(grids));
-  }),
+  }
 
   /**
    * Handler for "grid-highlighter-shown" and "grid-highlighter-hidden" events emitted
    * from the HighlightersOverlay. Updates the NodeFront's grid highlighted state.
    *
    * @param  {Event} event
    *         Event that was triggered.
    * @param  {NodeFront} nodeFront
@@ -369,53 +368,53 @@ GridInspector.prototype = {
 
     if (this.lastHighlighterColor !== color || this.lastHighlighterNode !== nodeFront) {
       this.store.dispatch(updateGridColor(nodeFront, color));
     }
 
     this.lastHighlighterColor = null;
     this.lastHighlighterNode = null;
     this.lastHighlighterState = null;
-  },
+  }
 
   /**
    * Handler for "new-root" event fired by the inspector, which indicates a page
    * navigation. Updates grid panel contents.
    */
   onNavigate() {
     if (this.isPanelVisible()) {
       this.updateGridPanel();
     }
-  },
+  }
 
   /**
    * Handler for the "reflow" event fired by the inspector's reflow tracker. On reflows,
    * update the grid panel content, because the shape or number of grids on the page may
    * have changed.
    *
    * Note that there may be frequent reflows on the page and that not all of them actually
    * cause the grids to change. So, we want to limit how many times we update the grid
    * panel to only reflows that actually either change the list of grids, or those that
    * change the current outlined grid.
    * To achieve this, this function compares the list of grid containers from before and
    * after the reflow, as well as the grid fragment data on the currently highlighted
    * grid.
    */
-  onReflow: Task.async(function* () {
+  async onReflow() {
     if (!this.isPanelVisible()) {
       return;
     }
 
     // The list of grids currently displayed.
     const { grids } = this.store.getState();
 
     // The new list of grids from the server.
     let newGridFronts;
     try {
-      newGridFronts = yield this.layoutInspector.getAllGrids(this.walker.rootNode);
+      newGridFronts = await this.layoutInspector.getAllGrids(this.walker.rootNode);
     } catch (e) {
       // This call might fail if called asynchrously after the toolbox is finished
       // closing.
       return;
     }
 
     // Get the node front(s) from the current grid(s) so we can compare them to them to
     // node(s) of the new grids.
@@ -439,17 +438,17 @@ GridInspector.prototype = {
           (this.highlighters.gridHighlighterShown &&
            !this.haveCurrentFragmentsChanged(newGridFronts))) {
         return;
       }
     }
 
     // Either the list of containers or the current fragments have changed, do update.
     this.updateGridPanel(newGridFronts);
-  }),
+  }
 
   /**
    * Handler for a change in the grid overlay color picker for a grid container.
    *
    * @param  {NodeFront} node
    *         The NodeFront of the grid container element for which the grid color is
    *         being updated.
    * @param  {String} color
@@ -462,17 +461,17 @@ GridInspector.prototype = {
     // If the grid for which the color was updated currently has a highlighter, update
     // the color.
     for (let grid of grids) {
       if (grid.nodeFront === node && grid.highlighted) {
         let highlighterSettings = this.getGridHighlighterSettings(node);
         this.showGridHighlighter(node, highlighterSettings);
       }
     }
-  },
+  }
 
   /**
    * Highlights the grid area in the CSS Grid Highlighter for the given grid.
    *
    * @param  {NodeFront} node
    *         The NodeFront of the grid container element for which the grid
    *         highlighter is highlighted for.
    * @param  {String} gridAreaName
@@ -486,17 +485,17 @@ GridInspector.prototype = {
     let { highlighterSettings } = this.store.getState();
 
     highlighterSettings.showGridArea = gridAreaName;
     highlighterSettings.color = color;
 
     this.showGridHighlighter(node, highlighterSettings);
 
     this.store.dispatch(updateGridHighlighted(node, true));
-  },
+  }
 
   /**
    * Highlights the grid cell in the CSS Grid Highlighter for the given grid.
    *
    * @param  {NodeFront} node
    *         The NodeFront of the grid container element for which the grid
    *         highlighter is highlighted for.
    * @param  {String} color
@@ -516,17 +515,17 @@ GridInspector.prototype = {
     let { highlighterSettings } = this.store.getState();
 
     highlighterSettings.showGridCell = { gridFragmentIndex, rowNumber, columnNumber };
     highlighterSettings.color = color;
 
     this.showGridHighlighter(node, highlighterSettings);
 
     this.store.dispatch(updateGridHighlighted(node, true));
-  },
+  }
 
   /**
    * Highlights the grid line in the CSS Grid Highlighter for the given grid.
    *
    * @param  {NodeFront} node
    *         The NodeFront of the grid container element for which the grid
    *         highlighter is highlighted for.
    * @param  {Number|null} gridFragmentIndex
@@ -549,48 +548,48 @@ GridInspector.prototype = {
       lineNumber,
       type
     };
     highlighterSettings.color = color;
 
     this.showGridHighlighter(node, highlighterSettings);
 
     this.store.dispatch(updateGridHighlighted(node, true));
-  },
+  }
 
   /**
    * Handler for the inspector sidebar "select" event. Starts tracking reflows
    * if the layout panel is visible. Otherwise, stop tracking reflows.
    * Finally, refresh the layout view if it is visible.
    */
   onSidebarSelect() {
     if (!this.isPanelVisible()) {
       this.inspector.reflowTracker.untrackReflows(this, this.onReflow);
       return;
     }
 
     this.inspector.reflowTracker.trackReflows(this, this.onReflow);
     this.updateGridPanel();
-  },
+  }
 
   /**
    * Handler for a change in the input checkboxes in the GridList component.
    * Toggles on/off the grid highlighter for the provided grid container element.
    *
    * @param  {NodeFront} node
    *         The NodeFront of the grid container element for which the grid
    *         highlighter is toggled on/off for.
    */
   onToggleGridHighlighter(node) {
     let highlighterSettings = this.getGridHighlighterSettings(node);
     this.toggleGridHighlighter(node, highlighterSettings);
 
     this.store.dispatch(updateGridHighlighted(node,
       node !== this.highlighters.gridHighlighterShown));
-  },
+  }
 
   /**
     * Handler for a change in the show grid areas checkbox in the GridDisplaySettings
     * component. Toggles on/off the option to show the grid areas in the grid highlighter.
     * Refreshes the shown grid highlighter for the grids currently highlighted.
     *
     * @param  {Boolean} enabled
     *         Whether or not the grid highlighter should show the grid areas.
@@ -606,17 +605,17 @@ GridInspector.prototype = {
     let { grids } = this.store.getState();
 
     for (let grid of grids) {
       if (grid.highlighted) {
         let highlighterSettings = this.getGridHighlighterSettings(grid.nodeFront);
         this.highlighters.showGridHighlighter(grid.nodeFront, highlighterSettings);
       }
     }
-  },
+  }
 
   /**
    * Handler for a change in the show grid line numbers checkbox in the
    * GridDisplaySettings component. Toggles on/off the option to show the grid line
    * numbers in the grid highlighter. Refreshes the shown grid highlighter for the
    * grids currently highlighted.
    *
    * @param  {Boolean} enabled
@@ -633,17 +632,17 @@ GridInspector.prototype = {
     let { grids } = this.store.getState();
 
     for (let grid of grids) {
       if (grid.highlighted) {
         let highlighterSettings = this.getGridHighlighterSettings(grid.nodeFront);
         this.showGridHighlighter(grid.nodeFront, highlighterSettings);
       }
     }
-  },
+  }
 
   /**
    * Handler for a change in the extend grid lines infinitely checkbox in the
    * GridDisplaySettings component. Toggles on/off the option to extend the grid
    * lines infinitely in the grid highlighter. Refreshes the shown grid highlighter
    * for grids currently highlighted.
    *
    * @param  {Boolean} enabled
@@ -660,13 +659,13 @@ GridInspector.prototype = {
     let { grids } = this.store.getState();
 
     for (let grid of grids) {
       if (grid.highlighted) {
         let highlighterSettings = this.getGridHighlighterSettings(grid.nodeFront);
         this.showGridHighlighter(grid.nodeFront, highlighterSettings);
       }
     }
-  },
+  }
 
-};
+}
 
 module.exports = GridInspector;
--- a/devtools/client/inspector/inspector.js
+++ b/devtools/client/inspector/inspector.js
@@ -641,21 +641,74 @@ Inspector.prototype = {
           title: layoutTitle
         },
         panel: () => {
           if (!this.layoutview) {
             const LayoutView =
               this.browserRequire("devtools/client/inspector/layout/layout");
             this.layoutview = new LayoutView(this, this.panelWin);
           }
+
           return this.layoutview.provider;
         }
       },
       defaultTab == layoutId);
 
+    if (Services.prefs.getBoolPref("devtools.changesview.enabled")) {
+      // Inject a lazy loaded react tab by exposing a fake React object
+      // with a lazy defined Tab thanks to `panel` being a function
+      let changesId = "changesview";
+      let changesTitle = INSPECTOR_L10N.getStr("inspector.sidebar.changesViewTitle");
+      this.sidebar.addTab(
+        changesId,
+        changesTitle,
+        {
+          props: {
+            id: changesId,
+            title: changesTitle
+          },
+          panel: () => {
+            if (!this.changesview) {
+              const ChangesView =
+                this.browserRequire("devtools/client/inspector/changes/changes");
+              this.changesview = new ChangesView(this, this.panelWin);
+            }
+
+            return this.changesview.provider;
+          }
+        },
+        defaultTab == changesId);
+    }
+
+    if (Services.prefs.getBoolPref("devtools.eventsview.enabled")) {
+      // Inject a lazy loaded react tab by exposing a fake React object
+      // with a lazy defined Tab thanks to `panel` being a function
+      let eventsId = "eventsview";
+      let eventsTitle = INSPECTOR_L10N.getStr("inspector.sidebar.eventsViewTitle");
+      this.sidebar.addTab(
+        eventsId,
+        eventsTitle,
+        {
+          props: {
+            id: eventsId,
+            title: eventsTitle
+          },
+          panel: () => {
+            if (!this.eventview) {
+              const EventsView =
+                this.browserRequire("devtools/client/inspector/events/events");
+              this.eventsview = new EventsView(this, this.panelWin);
+            }
+
+            return this.eventsview.provider;
+          }
+        },
+        defaultTab == eventsId);
+    }
+
     if (this.target.form.animationsActor) {
       this.sidebar.addFrameTab(
         "animationinspector",
         INSPECTOR_L10N.getStr("inspector.sidebar.animationInspectorTitle"),
         "chrome://devtools/content/animationinspector/animation-inspector.xhtml",
         defaultTab == "animationinspector");
     }
 
@@ -673,16 +726,17 @@ Inspector.prototype = {
             title: fontTitle
           },
           panel: () => {
             if (!this.fontinspector) {
               const FontInspector =
                 this.browserRequire("devtools/client/inspector/fonts/fonts");
               this.fontinspector = new FontInspector(this, this.panelWin);
             }
+
             return this.fontinspector.provider;
           }
         },
         defaultTab == fontId);
     }
 
     // Persist splitter state in preferences.
     this.sidebar.on("show", this.onSidebarShown);
rename from devtools/client/inspector/layout/components/App.js
rename to devtools/client/inspector/layout/components/LayoutApp.js
--- a/devtools/client/inspector/layout/components/App.js
+++ b/devtools/client/inspector/layout/components/LayoutApp.js
@@ -30,17 +30,17 @@ const LAYOUT_STRINGS_URI = "devtools/cli
 const LAYOUT_L10N = new LocalizationHelper(LAYOUT_STRINGS_URI);
 
 const FLEXBOX_ENABLED_PREF = "devtools.flexboxinspector.enabled";
 
 const BOXMODEL_OPENED_PREF = "devtools.layout.boxmodel.opened";
 const FLEXBOX_OPENED_PREF = "devtools.layout.flexbox.opened";
 const GRID_OPENED_PREF = "devtools.layout.grid.opened";
 
-class App extends PureComponent {
+class LayoutApp extends PureComponent {
   static get propTypes() {
     return {
       boxModel: PropTypes.shape(BoxModelTypes.boxModel).isRequired,
       getSwatchColorPickerTooltip: PropTypes.func.isRequired,
       grids: PropTypes.arrayOf(PropTypes.shape(GridTypes.grid)).isRequired,
       highlighterSettings: PropTypes.shape(GridTypes.highlighterSettings).isRequired,
       setSelectedNode: PropTypes.func.isRequired,
       showBoxModelProperties: PropTypes.bool.isRequired,
@@ -97,9 +97,9 @@ class App extends PureComponent {
 
     return dom.div(
       { id: "layout-container" },
       Accordion({ items })
     );
   }
 }
 
-module.exports = connect(state => state)(App);
+module.exports = connect(state => state)(LayoutApp);
--- a/devtools/client/inspector/layout/components/moz.build
+++ b/devtools/client/inspector/layout/components/moz.build
@@ -2,10 +2,10 @@
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 DevToolsModules(
     'Accordion.css',
     'Accordion.js',
-    'App.js',
+    'LayoutApp.js',
 )
--- a/devtools/client/inspector/layout/layout.js
+++ b/devtools/client/inspector/layout/layout.js
@@ -2,34 +2,34 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const { createFactory, createElement } = require("devtools/client/shared/vendor/react");
 const { Provider } = require("devtools/client/shared/vendor/react-redux");
 
-const App = createFactory(require("./components/App"));
+const LayoutApp = createFactory(require("./components/LayoutApp"));
 
 const { LocalizationHelper } = require("devtools/shared/l10n");
 const INSPECTOR_L10N =
   new LocalizationHelper("devtools/client/locales/inspector.properties");
 
 loader.lazyRequireGetter(this, "FlexboxInspector", "devtools/client/inspector/flexbox/flexbox");
 loader.lazyRequireGetter(this, "GridInspector", "devtools/client/inspector/grids/grid-inspector");
 
-function LayoutView(inspector, window) {
-  this.document = window.document;
-  this.inspector = inspector;
-  this.store = inspector.store;
+class LayoutView {
 
-  this.init();
-}
+  constructor(inspector, window) {
+    this.document = window.document;
+    this.inspector = inspector;
+    this.store = inspector.store;
 
-LayoutView.prototype = {
+    this.init();
+  }
 
   init() {
     if (!this.inspector) {
       return;
     }
 
     let {
       setSelectedNode,
@@ -51,17 +51,17 @@ LayoutView.prototype = {
       onShowGridCellHighlight,
       onShowGridLineNamesHighlight,
       onToggleGridHighlighter,
       onToggleShowGridAreas,
       onToggleShowGridLineNumbers,
       onToggleShowInfiniteLines,
     } = this.gridInspector.getComponentProps();
 
-    let app = App({
+    let layoutApp = LayoutApp({
       getSwatchColorPickerTooltip,
       setSelectedNode,
       /**
        * Shows the box model properties under the box model if true, otherwise, hidden by
        * default.
        */
       showBoxModelProperties: true,
       onHideBoxModelHighlighter,
@@ -79,28 +79,28 @@ LayoutView.prototype = {
       onToggleShowInfiniteLines,
     });
 
     let provider = createElement(Provider, {
       id: "layoutview",
       key: "layoutview",
       store: this.store,
       title: INSPECTOR_L10N.getStr("inspector.sidebar.layoutViewTitle2"),
-    }, app);
+    }, layoutApp);
 
     // Expose the provider to let inspector.js use it in setupSidebar.
     this.provider = provider;
-  },
+  }
 
   /**
    * Destruction function called when the inspector is destroyed. Cleans up references.
    */
   destroy() {
     this.gridInspector.destroy();
 
     this.document = null;
     this.inspector = null;
     this.store = null;
-  },
+  }
 
-};
+}
 
 module.exports = LayoutView;
--- a/devtools/client/inspector/moz.build
+++ b/devtools/client/inspector/moz.build
@@ -1,16 +1,18 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 DIRS += [
     'boxmodel',
+    'changes',
     'components',
     'computed',
+    'events',
     'extensions',
     'flexbox',
     'fonts',
     'grids',
     'layout',
     'markup',
     'rules',
     'shared'
--- a/devtools/client/inspector/reducers.js
+++ b/devtools/client/inspector/reducers.js
@@ -3,14 +3,16 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 // This file exposes the Redux reducers of the box model, grid and grid highlighter
 // settings.
 
 exports.boxModel = require("devtools/client/inspector/boxmodel/reducers/box-model");
+exports.changes = require("devtools/client/inspector/changes/reducers/changes");
+exports.events = require("devtools/client/inspector/events/reducers/events");
 exports.extensionsSidebar = require("devtools/client/inspector/extensions/reducers/sidebar");
 exports.flexboxes = require("devtools/client/inspector/flexbox/reducers/flexboxes");
 exports.fontOptions = require("devtools/client/inspector/fonts/reducers/font-options");
 exports.fonts = require("devtools/client/inspector/fonts/reducers/fonts");
 exports.grids = require("devtools/client/inspector/grids/reducers/grids");
 exports.highlighterSettings = require("devtools/client/inspector/grids/reducers/highlighter-settings");
--- a/devtools/client/jsonview/test/browser.ini
+++ b/devtools/client/jsonview/test/browser.ini
@@ -2,21 +2,23 @@
 tags = devtools
 subsuite = devtools
 support-files =
   array_json.json
   array_json.json^headers^
   csp_json.json
   csp_json.json^headers^
   doc_frame_script.js
+  empty.html
   head.js
   invalid_json.json
   invalid_json.json^headers^
   manifest_json.json
   manifest_json.json^headers^
+  passthrough-sw.js
   simple_json.json
   simple_json.json^headers^
   valid_json.json
   valid_json.json^headers^
   !/devtools/client/commandline/test/head.js
   !/devtools/client/framework/test/head.js
   !/devtools/client/framework/test/shared-head.js
 
@@ -43,8 +45,9 @@ skip-if = (os == 'linux' && bits == 32 &
 [browser_jsonview_object-type.js]
 [browser_jsonview_save_json.js]
 support-files =
   !/toolkit/content/tests/browser/common/mockTransfer.js
 [browser_jsonview_theme.js]
 [browser_jsonview_slash.js]
 [browser_jsonview_valid_json.js]
 [browser_json_refresh.js]
+[browser_jsonview_serviceworker.js]
new file mode 100644
--- /dev/null
+++ b/devtools/client/jsonview/test/browser_jsonview_serviceworker.js
@@ -0,0 +1,73 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const TEST_JSON_URL = URL_ROOT + "valid_json.json";
+const EMPTY_PAGE = URL_ROOT + "empty.html";
+const SW = URL_ROOT + "passthrough-sw.js";
+
+add_task(async function () {
+  info("Test valid JSON with service worker started");
+
+  await SpecialPowers.pushPrefEnv({"set": [
+    ["dom.serviceWorkers.exemptFromPerDomainMax", true],
+    ["dom.serviceWorkers.enabled", true],
+    ["dom.serviceWorkers.testing.enabled", true],
+  ]});
+
+  let swTab = BrowserTestUtils.addTab(gBrowser, EMPTY_PAGE);
+  let browser = gBrowser.getBrowserForTab(swTab);
+  await BrowserTestUtils.browserLoaded(browser);
+  await ContentTask.spawn(browser, { script: SW, scope: TEST_JSON_URL }, async opts => {
+    let reg = await content.navigator.serviceWorker.register(opts.script,
+                                                             { scope: opts.scope });
+    return new content.window.Promise(resolve => {
+      let worker = reg.installing;
+      if (worker.state === "activated") {
+        resolve();
+        return;
+      }
+      worker.addEventListener("statechange", evt => {
+        if (worker.state === "activated") {
+          resolve();
+        }
+      });
+    });
+  });
+
+  let tab = await addJsonViewTab(TEST_JSON_URL);
+
+  ok(tab.linkedBrowser.contentPrincipal.isNullPrincipal, "Should have null principal");
+
+  is(await countRows(), 3, "There must be three rows");
+
+  let objectCellCount = await getElementCount(
+    ".jsonPanelBox .treeTable .objectCell");
+  is(objectCellCount, 1, "There must be one object cell");
+
+  let objectCellText = await getElementText(
+    ".jsonPanelBox .treeTable .objectCell");
+  is(objectCellText, "", "The summary is hidden when object is expanded");
+
+  // Clicking the value does not collapse it (so that it can be selected and copied).
+  await clickJsonNode(".jsonPanelBox .treeTable .treeValueCell");
+  is(await countRows(), 3, "There must still be three rows");
+
+  // Clicking the label collapses the auto-expanded node.
+  await clickJsonNode(".jsonPanelBox .treeTable .treeLabel");
+  is(await countRows(), 1, "There must be one row");
+
+  await ContentTask.spawn(browser, { script: SW, scope: TEST_JSON_URL }, async opts => {
+    let reg = await content.navigator.serviceWorker.getRegistration(opts.scope);
+    await reg.unregister();
+  });
+
+  await BrowserTestUtils.removeTab(swTab);
+});
+
+function countRows() {
+  return getElementCount(".jsonPanelBox .treeTable .treeRow");
+}
new file mode 100644
--- /dev/null
+++ b/devtools/client/jsonview/test/empty.html
@@ -0,0 +1,1 @@
+<!doctype html>
new file mode 100644
--- /dev/null
+++ b/devtools/client/jsonview/test/passthrough-sw.js
@@ -0,0 +1,5 @@
+"use strict";
+
+addEventListener("fetch", evt => {
+  evt.respondWith(fetch(evt.request));
+});
--- a/devtools/client/locales/en-US/inspector.properties
+++ b/devtools/client/locales/en-US/inspector.properties
@@ -344,16 +344,26 @@ inspector.sidebar.computedViewTitle=Comp
 # that corresponds to the tool displaying layout information defined in the page.
 inspector.sidebar.layoutViewTitle2=Layout
 
 # LOCALIZATION NOTE (inspector.sidebar.newBadge):
 # This is the text of a promotion badge showed in the inspector sidebar, next to a panel
 # name. Used to promote new/recent panels such as the layout panel.
 inspector.sidebar.newBadge=new!
 
+# LOCALIZATION NOTE (inspector.sidebar.changesViewTitle):
+# This is the title shown in a tab in the side panel of the Inspector panel
+# that corresponds to the tool displaying CSS changes.
+inspector.sidebar.changesViewTitle=Changes
+
+# LOCALIZATION NOTE (inspector.sidebar.eventsViewTitle):
+# This is the title shown in a tab in the side panel of the Inspector panel
+# that corresponds to the tool displaying the list of event listeners used in the page.
+inspector.sidebar.eventsViewTitle=Events
+
 # LOCALIZATION NOTE (inspector.sidebar.animationInspectorTitle):
 # This is the title shown in a tab in the side panel of the Inspector panel
 # that corresponds to the tool displaying animations defined in the page.
 inspector.sidebar.animationInspectorTitle=Animations
 
 # LOCALIZATION NOTE (inspector.eyedropper.label): A string displayed as the tooltip of
 # a button in the inspector which toggles the Eyedropper tool
 inspector.eyedropper.label=Grab a color from the page
--- a/devtools/client/preferences/devtools.js
+++ b/devtools/client/preferences/devtools.js
@@ -55,17 +55,20 @@ pref("devtools.inspector.imagePreviewToo
 // Enable user agent style inspection in rule-view
 pref("devtools.inspector.showUserAgentStyles", false);
 // Show all native anonymous content (like controls in <video> tags)
 pref("devtools.inspector.showAllAnonymousContent", false);
 // Enable the new color widget
 pref("devtools.inspector.colorWidget.enabled", false);
 // Enable the CSS shapes highlighter
 pref("devtools.inspector.shapesHighlighter.enabled", true);
-
+// Enable the Changes View
+pref("devtools.changesview.enabled", false);
+// Enable the Events View
+pref("devtools.eventsview.enabled", false);
 // Enable the Flexbox Inspector
 pref("devtools.flexboxinspector.enabled", false);
 
 // Grid highlighter preferences
 pref("devtools.gridinspector.gridOutlineMaxColumns", 50);
 pref("devtools.gridinspector.gridOutlineMaxRows", 50);
 pref("devtools.gridinspector.showGridAreas", false);
 pref("devtools.gridinspector.showGridLineNumbers", false);
--- a/devtools/client/shared/browser-loader.js
+++ b/devtools/client/shared/browser-loader.js
@@ -7,17 +7,19 @@ var Cu = Components.utils;
 const loaders = Cu.import("resource://devtools/shared/base-loader.js", {});
 const { devtools } = Cu.import("resource://devtools/shared/Loader.jsm", {});
 const { joinURI } = devtools.require("devtools/shared/path");
 const { assert } = devtools.require("devtools/shared/DevToolsUtils");
 const { AppConstants } = devtools.require("resource://gre/modules/AppConstants.jsm");
 
 const BROWSER_BASED_DIRS = [
   "resource://devtools/client/inspector/boxmodel",
+  "resource://devtools/client/inspector/changes",
   "resource://devtools/client/inspector/computed",
+  "resource://devtools/client/inspector/events",
   "resource://devtools/client/inspector/flexbox",
   "resource://devtools/client/inspector/fonts",
   "resource://devtools/client/inspector/grids",
   "resource://devtools/client/inspector/layout",
   "resource://devtools/client/jsonview",
   "resource://devtools/client/shared/source-map",
   "resource://devtools/client/shared/redux",
   "resource://devtools/client/shared/vendor",
--- a/dom/base/CustomElementRegistry.cpp
+++ b/dom/base/CustomElementRegistry.cpp
@@ -1046,18 +1046,22 @@ CustomElementReactionsStack::PopAndInvok
   const uint32_t lastIndex = mReactionsStack.Length() - 1;
   ElementQueue* elementQueue = mReactionsStack.ElementAt(lastIndex).get();
   // Check element queue size in order to reduce function call overhead.
   if (!elementQueue->IsEmpty()) {
     // It is still not clear what error reporting will look like in custom
     // element, see https://github.com/w3c/webcomponents/issues/635.
     // We usually report the error to entry global in gecko, so just follow the
     // same behavior here.
+    // This may be null if it's called from parser, see the case of
+    // attributeChangedCallback in
+    // https://html.spec.whatwg.org/multipage/parsing.html#create-an-element-for-the-token
+    // In that case, the exception of callback reactions will be automatically
+    // reported in CallSetup.
     nsIGlobalObject* global = GetEntryGlobal();
-    MOZ_ASSERT(global, "Should always have a entry global here!");
     InvokeReactions(elementQueue, global);
   }
 
   // InvokeReactions() might create other custom element reactions, but those
   // new reactions should be already consumed and removed at this point.
   MOZ_ASSERT(lastIndex == mReactionsStack.Length() - 1,
              "reactions created by InvokeReactions() should be consumed and removed");
 
--- a/dom/base/nsContentCreatorFunctions.h
+++ b/dom/base/nsContentCreatorFunctions.h
@@ -19,16 +19,17 @@
 class nsIContent;
 class imgRequestProxy;
 class nsGenericHTMLElement;
 
 namespace mozilla {
 namespace dom {
 class Element;
 class NodeInfo;
+struct CustomElementDefinition;
 } // namespace dom
 } // namespace mozilla
 
 nsresult
 NS_NewElement(mozilla::dom::Element** aResult,
               already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo,
               mozilla::dom::FromParser aFromParser,
               const nsAString* aIs = nullptr);
@@ -36,17 +37,18 @@ NS_NewElement(mozilla::dom::Element** aR
 nsresult
 NS_NewXMLElement(mozilla::dom::Element** aResult,
                  already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo);
 
 nsresult
 NS_NewHTMLElement(mozilla::dom::Element** aResult,
                   already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo,
                   mozilla::dom::FromParser aFromParser,
-                  const nsAString* aIs = nullptr);
+                  const nsAString* aIs = nullptr,
+                  mozilla::dom::CustomElementDefinition* aDefinition = nullptr);
 
 // First argument should be nsHTMLTag, but that adds dependency to parser
 // for a bunch of files.
 already_AddRefed<nsGenericHTMLElement>
 CreateHTMLElement(uint32_t aNodeType,
                   already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo,
                   mozilla::dom::FromParser aFromParser);
 
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -1591,17 +1591,18 @@ nsIDocument::nsIDocument()
     mBFCacheEntry(nullptr),
     mInSyncOperationCount(0),
     mBlockDOMContentLoaded(0),
     mUseCounters(0),
     mChildDocumentUseCounters(0),
     mNotifiedPageForUseCounter(0),
     mIncCounters(),
     mUserHasInteracted(false),
-    mServoRestyleRootDirtyBits(0)
+    mServoRestyleRootDirtyBits(0),
+    mThrowOnDynamicMarkupInsertionCounter(0)
 {
   SetIsInDocument();
   for (auto& cnt : mIncCounters) {
     cnt = 0;
   }
 }
 
 nsDocument::nsDocument(const char* aContentType)
@@ -2495,17 +2496,17 @@ nsDocument::MaybeDowngradePrincipal(nsIP
   // automatically downgrade it to the last principal it subsumes (which is the
   // extension principal, in the case of extension content scripts).
   auto* basePrin = BasePrincipal::Cast(aPrincipal);
   if (basePrin->Is<ExpandedPrincipal>()) {
     auto* expanded = basePrin->As<ExpandedPrincipal>();
 
     MOZ_ASSERT(expanded->WhiteList().Length() > 0);
 
-    return do_AddRef(expanded->WhiteList().LastElement().get());
+    return do_AddRef(expanded->WhiteList().LastElement());
   }
 
   if (!sChromeInContentPrefCached) {
     sChromeInContentPrefCached = true;
     Preferences::AddBoolVarCache(&sChromeInContentAllowed,
                                  kChromeInContentPref, false);
   }
   if (!sChromeInContentAllowed &&
--- a/dom/base/nsIDocument.h
+++ b/dom/base/nsIDocument.h
@@ -3190,16 +3190,32 @@ public:
   void ClearServoRestyleRoot()
   {
     mServoRestyleRoot = nullptr;
     mServoRestyleRootDirtyBits = 0;
   }
 
   inline void SetServoRestyleRoot(nsINode* aRoot, uint32_t aDirtyBits);
 
+  bool ShouldThrowOnDynamicMarkupInsertion()
+  {
+    return mThrowOnDynamicMarkupInsertionCounter;
+  }
+
+  void IncrementThrowOnDynamicMarkupInsertionCounter()
+  {
+    ++mThrowOnDynamicMarkupInsertionCounter;
+  }
+
+  void DecrementThrowOnDynamicMarkupInsertionCounter()
+  {
+    MOZ_ASSERT(mThrowOnDynamicMarkupInsertionCounter);
+    --mThrowOnDynamicMarkupInsertionCounter;
+  }
+
 protected:
   bool GetUseCounter(mozilla::UseCounter aUseCounter)
   {
     return mUseCounters[aUseCounter];
   }
 
   void SetChildDocumentUseCounter(mozilla::UseCounter aUseCounter)
   {
@@ -3731,16 +3747,21 @@ protected:
   // We store this as an nsINode, rather than as an Element, so that we can store
   // the Document node as the restyle root if the entire document (along with all
   // document-level native-anonymous content) needs to be restyled.
   //
   // We also track which "descendant" bits (normal/animation-only/lazy-fc) the
   // root corresponds to.
   nsCOMPtr<nsINode> mServoRestyleRoot;
   uint32_t mServoRestyleRootDirtyBits;
+
+  // Used in conjunction with the create-an-element-for-the-token algorithm to
+  // prevent custom element constructors from being able to use document.open(),
+  // document.close(), and document.write() when they are invoked by the parser.
+  uint32_t mThrowOnDynamicMarkupInsertionCounter;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(nsIDocument, NS_IDOCUMENT_IID)
 
 /**
  * mozAutoSubtreeModified batches DOM mutations so that a DOMSubtreeModified
  * event is dispatched, if necessary, when the outermost mozAutoSubtreeModified
  * object is deleted.
@@ -3787,16 +3808,33 @@ class MOZ_STACK_CLASS nsAutoSyncOperatio
 public:
   explicit nsAutoSyncOperation(nsIDocument* aDocument);
   ~nsAutoSyncOperation();
 private:
   nsCOMArray<nsIDocument> mDocuments;
   uint32_t                mMicroTaskLevel;
 };
 
+class MOZ_RAII AutoSetThrowOnDynamicMarkupInsertionCounter final {
+  public:
+    explicit AutoSetThrowOnDynamicMarkupInsertionCounter(
+      nsIDocument* aDocument)
+      : mDocument(aDocument)
+    {
+      mDocument->IncrementThrowOnDynamicMarkupInsertionCounter();
+    }
+
+    ~AutoSetThrowOnDynamicMarkupInsertionCounter() {
+      mDocument->DecrementThrowOnDynamicMarkupInsertionCounter();
+    }
+
+  private:
+    nsIDocument* mDocument;
+};
+
 // XXX These belong somewhere else
 nsresult
 NS_NewHTMLDocument(nsIDocument** aInstancePtrResult, bool aLoadedAsData = false);
 
 nsresult
 NS_NewXMLDocument(nsIDocument** aInstancePtrResult, bool aLoadedAsData = false,
                   bool aIsPlainDocument = false);
 
--- a/dom/canvas/TexUnpackBlob.cpp
+++ b/dom/canvas/TexUnpackBlob.cpp
@@ -697,20 +697,20 @@ TexUnpackImage::TexOrSubImage(bool isSub
         }
 
         const GLenum status = gl->fCheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER);
         if (status != LOCAL_GL_FRAMEBUFFER_COMPLETE) {
             fallbackReason = "bug: failed to confirm FB for blit";
             break;
         }
 
-        const gfx::IntSize destSize(mWidth, mHeight);
+        const gfx::IntSize dstSize(mWidth, mHeight);
         const auto dstOrigin = (webgl->mPixelStore_FlipY ? gl::OriginPos::TopLeft
                                                          : gl::OriginPos::BottomLeft);
-        if (!gl->BlitHelper()->BlitImageToFramebuffer(mImage, destSize, dstOrigin)) {
+        if (!gl->BlitHelper()->BlitImageToFramebuffer(mImage, dstSize, dstOrigin)) {
             fallbackReason = "likely bug: failed to blit";
             break;
         }
 
         // Blitting was successful, so we're done!
         *out_error = 0;
         return true;
     } while (false);
--- a/dom/html/HTMLBodyElement.cpp
+++ b/dom/html/HTMLBodyElement.cpp
@@ -89,21 +89,16 @@ HTMLBodyElement::MapAttributesIntoRule(c
 
     int32_t bodyMarginWidth  = -1;
     int32_t bodyMarginHeight = -1;
     int32_t bodyTopMargin = -1;
     int32_t bodyBottomMargin = -1;
     int32_t bodyLeftMargin = -1;
     int32_t bodyRightMargin = -1;
 
-    // check the mode (fortunately, the GenericSpecifiedValues has a presContext for us to use!)
-    NS_ASSERTION(aData->mPresContext, "null presContext in MapAttributesIntoRule was unexpected");
-    nsCompatibility mode = aData->mPresContext->CompatibilityMode();
-
-
     const nsAttrValue* value;
     // if marginwidth/marginheight are set, reflect them as 'margin'
     value = aAttributes->GetAttr(nsGkAtoms::marginwidth);
     if (value && value->Type() == nsAttrValue::eInteger) {
       bodyMarginWidth = value->GetIntegerValue();
       if (bodyMarginWidth < 0) {
         bodyMarginWidth = 0;
       }
@@ -172,30 +167,16 @@ HTMLBodyElement::MapAttributesIntoRule(c
     // reflect them as margin in the <body>
     if (bodyMarginWidth == -1 || bodyMarginHeight == -1) {
       nsCOMPtr<nsIDocShell> docShell(aData->mPresContext->GetDocShell());
       if (docShell) {
         nscoord frameMarginWidth=-1;  // default value
         nscoord frameMarginHeight=-1; // default value
         docShell->GetMarginWidth(&frameMarginWidth); // -1 indicates not set
         docShell->GetMarginHeight(&frameMarginHeight);
-        if (frameMarginWidth >= 0 && bodyMarginWidth == -1) { // set in <frame> & not in <body>
-          if (eCompatibility_NavQuirks == mode) {
-            if (bodyMarginHeight == -1 && 0 > frameMarginHeight) { // nav quirk
-              frameMarginHeight = 0;
-            }
-          }
-        }
-        if (frameMarginHeight >= 0 && bodyMarginHeight == -1) { // set in <frame> & not in <body>
-          if (eCompatibility_NavQuirks == mode) {
-            if (bodyMarginWidth == -1 && 0 > frameMarginWidth) { // nav quirk
-              frameMarginWidth = 0;
-            }
-          }
-        }
 
         if (bodyMarginWidth == -1 && frameMarginWidth >= 0) {
           if (bodyLeftMargin == -1) {
             aData->SetPixelValueIfUnset(eCSSProperty_margin_left, (float)frameMarginWidth);
           }
           if (bodyRightMargin == -1) {
             aData->SetPixelValueIfUnset(eCSSProperty_margin_right, (float)frameMarginWidth);
           }
--- a/dom/html/nsHTMLContentSink.cpp
+++ b/dom/html/nsHTMLContentSink.cpp
@@ -246,35 +246,36 @@ DoCustomElementCreate(Element** aElement
     return;
   }
 
   element.forget(aElement);
 }
 
 nsresult
 NS_NewHTMLElement(Element** aResult, already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo,
-                  FromParser aFromParser, const nsAString* aIs)
+                  FromParser aFromParser, const nsAString* aIs,
+                  mozilla::dom::CustomElementDefinition* aDefinition)
 {
   *aResult = nullptr;
 
   RefPtr<mozilla::dom::NodeInfo> nodeInfo = aNodeInfo;
 
   nsAtom *name = nodeInfo->NameAtom();
 
   NS_ASSERTION(nodeInfo->NamespaceEquals(kNameSpaceID_XHTML),
                "Trying to HTML elements that don't have the XHTML namespace");
 
   int32_t tag = nsHTMLTags::CaseSensitiveAtomTagToId(name);
 
   // https://dom.spec.whatwg.org/#concept-create-element
   // We only handle the "synchronous custom elements flag is set" now.
   // For the unset case (e.g. cloning a node), see bug 1319342 for that.
   // Step 4.
-  CustomElementDefinition* definition = nullptr;
-  if (CustomElementRegistry::IsCustomElementEnabled()) {
+  CustomElementDefinition* definition = aDefinition;
+  if (!definition && CustomElementRegistry::IsCustomElementEnabled()) {
     definition =
       nsContentUtils::LookupCustomElementDefinition(nodeInfo->GetDocument(),
                                                     nodeInfo->LocalName(),
                                                     nodeInfo->NamespaceID(),
                                                     aIs);
   }
 
   // It might be a problem that parser synchronously calls constructor, so filed
@@ -289,19 +290,30 @@ NS_NewHTMLElement(Element** aResult, alr
      *    NOT_FROM_PARSER.
      * 3) clone a node, our implementation will not go into this function.
      * For the unset case which is non-synchronous only applied for
      * inner/outerHTML.
      */
     bool synchronousCustomElements = aFromParser != dom::FROM_PARSER_FRAGMENT ||
                                      aFromParser == dom::NOT_FROM_PARSER;
     // Per discussion in https://github.com/w3c/webcomponents/issues/635,
-    // use entry global in those places that are called from JS APIs.
-    nsIGlobalObject* global = GetEntryGlobal();
-    MOZ_ASSERT(global);
+    // use entry global in those places that are called from JS APIs and use the
+    // node document's global object if it is called from parser.
+    nsIGlobalObject* global;
+    if (aFromParser == dom::NOT_FROM_PARSER) {
+      global = GetEntryGlobal();
+    } else {
+      global = nodeInfo->GetDocument()->GetScopeObject();
+    }
+    if (!global) {
+      // In browser chrome code, one may have access to a document which doesn't
+      // have scope object anymore.
+      return NS_ERROR_FAILURE;
+    }
+
     AutoEntryScript aes(global, "create custom elements");
     JSContext* cx = aes.cx();
     ErrorResult rv;
 
     // Step 5.
     if (definition->IsCustomBuiltIn()) {
       // SetupCustomElement() should be called with an element that don't have
       // CustomElementData setup, if not we will hit the assertion in
@@ -331,16 +343,17 @@ NS_NewHTMLElement(Element** aResult, alr
       if (rv.MaybeSetPendingException(cx)) {
         NS_IF_ADDREF(*aResult = NS_NewHTMLUnknownElement(nodeInfo.forget(), aFromParser));
       }
       return NS_OK;
     }
 
     // Step 6.2.
     NS_IF_ADDREF(*aResult = NS_NewHTMLElement(nodeInfo.forget(), aFromParser));
+    (*aResult)->SetCustomElementData(new CustomElementData(definition->mType));
     nsContentUtils::EnqueueUpgradeReaction(*aResult, definition);
     return NS_OK;
   }
 
   // Per the Custom Element specification, unknown tags that are valid custom
   // element names should be HTMLElement instead of HTMLUnknownElement.
   bool isCustomElementName = (tag == eHTMLTag_userdefined &&
                               nsContentUtils::IsCustomElementName(name));
--- a/dom/html/nsHTMLDocument.cpp
+++ b/dom/html/nsHTMLDocument.cpp
@@ -1497,16 +1497,21 @@ nsHTMLDocument::Open(JSContext* cx,
   NS_ASSERTION(nsContentUtils::CanCallerAccess(static_cast<nsIDOMHTMLDocument*>(this)),
                "XOW should have caught this!");
   if (!IsHTMLDocument() || mDisableDocWrite) {
     // No calling document.open() on XHTML
     aError.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return nullptr;
   }
 
+  if (ShouldThrowOnDynamicMarkupInsertion()) {
+    aError.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
+    return nullptr;
+  }
+
   nsAutoCString contentType;
   contentType.AssignLiteral("text/html");
 
   nsAutoString type;
   nsContentUtils::ASCIIToLower(aType, type);
   nsAutoCString actualType, dummy;
   NS_ParseRequestContentType(NS_ConvertUTF16toUTF8(type), actualType, dummy);
   if (!actualType.EqualsLiteral("text/html") &&
@@ -1836,16 +1841,21 @@ nsHTMLDocument::Close(ErrorResult& rv)
 {
   if (!IsHTMLDocument()) {
     // No calling document.close() on XHTML!
 
     rv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return;
   }
 
+  if (ShouldThrowOnDynamicMarkupInsertion()) {
+    rv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
+    return;
+  }
+
   if (!mParser || !mParser->IsScriptCreated()) {
     return;
   }
 
   ++mWriteLevel;
   rv = (static_cast<nsHtml5Parser*>(mParser.get()))->Parse(
     EmptyString(), nullptr, GetContentTypeInternal(), true);
   --mWriteLevel;
@@ -1920,16 +1930,21 @@ nsHTMLDocument::WriteCommon(JSContext *c
   NS_ENSURE_STATE(!mTooDeepWriteRecursion);
 
   if (!IsHTMLDocument() || mDisableDocWrite) {
     // No calling document.write*() on XHTML!
 
     return NS_ERROR_DOM_INVALID_STATE_ERR;
   }
 
+
+  if (ShouldThrowOnDynamicMarkupInsertion()) {
+    return NS_ERROR_DOM_INVALID_STATE_ERR;
+  }
+
   if (mParserAborted) {
     // Hixie says aborting the parser doesn't undefine the insertion point.
     // However, since we null out mParser in that case, we track the
     // theoretically defined insertion point using mParserAborted.
     return NS_OK;
   }
 
   nsresult rv = NS_OK;
--- a/dom/indexedDB/ActorsParent.cpp
+++ b/dom/indexedDB/ActorsParent.cpp
@@ -18597,16 +18597,17 @@ Maintenance::DirectoryWork()
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
 
       if (!idbDirEntries) {
         continue;
       }
 
+      nsCString suffix;
       nsCString group;
       nsCString origin;
       nsTArray<nsString> databasePaths;
 
       while (true) {
         // Loop over files in the "idb" directory.
         if (IsAborted()) {
           return NS_ERROR_ABORT;
@@ -18654,27 +18655,27 @@ Maintenance::DirectoryWork()
         }
 
         if (isDirectory) {
           continue;
         }
 
         // Found a database.
         if (databasePaths.IsEmpty()) {
+          MOZ_ASSERT(suffix.IsEmpty());
           MOZ_ASSERT(group.IsEmpty());
           MOZ_ASSERT(origin.IsEmpty());
 
           int64_t dummyTimeStamp;
           bool dummyPersisted;
-          nsCString dummySuffix;
           if (NS_WARN_IF(NS_FAILED(
                 quotaManager->GetDirectoryMetadata2(originDir,
                                                     &dummyTimeStamp,
                                                     &dummyPersisted,
-                                                    dummySuffix,
+                                                    suffix,
                                                     group,
                                                     origin)))) {
             // Not much we can do here...
             continue;
           }
         }
 
         MOZ_ASSERT(!databasePaths.Contains(idbFilePath));
@@ -18682,16 +18683,31 @@ Maintenance::DirectoryWork()
         databasePaths.AppendElement(idbFilePath);
       }
 
       if (!databasePaths.IsEmpty()) {
         mDirectoryInfos.AppendElement(DirectoryInfo(persistenceType,
                                                     group,
                                                     origin,
                                                     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,
+                                                     getter_AddRefs(directory));
+
+        if (NS_WARN_IF(NS_FAILED(rv))) {
+          return rv;
+        }
       }
     }
   }
 
   mState = State::BeginDatabaseMaintenance;
 
   MOZ_ALWAYS_SUCCEEDS(
     mQuotaClient->BackgroundThread()->Dispatch(this, NS_DISPATCH_NORMAL));
new file mode 100644
--- /dev/null
+++ b/dom/media/tests/crashtests/1185191.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script>
+
+function boom()
+{
+    var a = new AudioContext();
+    var b = new BroadcastChannel("x");
+    a.addEventListener("statechange", bye, false);
+}
+
+function bye()
+{
+    location = "data:text/html,2";
+}
+
+</script>
+</head>
+<body onload="boom();"></body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/media/tests/crashtests/1281695.html
@@ -0,0 +1,24 @@
+
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="UTF-8">
+<script>
+
+function zombieWindow()
+{
+    var frame = document.createElement("iframe");
+    document.body.appendChild(frame);
+    var frameWin = frame.contentWindow;
+    frame.remove();
+    return frameWin;
+}
+
+function boom() {
+    zombieWindow().navigator.mozGetUserMedia({ "fake": true, "audio": true }, function(stream) {}, function(e) {});
+}
+
+</script>
+</head>
+<body onload="boom();"></body>
+</html>
--- a/dom/media/tests/crashtests/crashtests.list
+++ b/dom/media/tests/crashtests/crashtests.list
@@ -9,12 +9,14 @@ load 802982.html
 load 812785.html
 load 834100.html
 load 836349.html
 load 837324.html
 load 855796.html
 load 860143.html
 load 861958.html
 load 863929.html
+load 1185191.html
+load 1281695.html
 load 1306476.html
 load 1348381.html
 load 1367930_1.html
 load 1367930_2.html
--- a/dom/tests/mochitest/webcomponents/mochitest.ini
+++ b/dom/tests/mochitest/webcomponents/mochitest.ini
@@ -5,41 +5,42 @@ support-files =
 
 [test_bug900724.html]
 [test_bug1017896.html]
 [test_bug1176757.html]
 [test_bug1276240.html]
 [test_content_element.html]
 [test_custom_element_adopt_callbacks.html]
 [test_custom_element_callback_innerhtml.html]
+skip-if = true # disabled - See bug 1390396
 [test_custom_element_clone_callbacks_extended.html]
 [test_custom_element_htmlconstructor.html]
 skip-if = os == 'android' # bug 1323645
 support-files =
   htmlconstructor_autonomous_tests.js
   htmlconstructor_builtin_tests.js
 [test_custom_element_import_node_created_callback.html]
 [test_custom_element_in_shadow.html]
 skip-if = true # disabled - See bug 1390396
 [test_custom_element_register_invalid_callbacks.html]
+[test_custom_element_throw_on_dynamic_markup_insertion.html]
 [test_custom_element_get.html]
 [test_custom_element_when_defined.html]
 [test_custom_element_upgrade.html]
 support-files =
   test_upgrade_page.html
   upgrade_tests.js
 [test_nested_content_element.html]
 [test_dest_insertion_points.html]
 [test_fallback_dest_insertion_points.html]
 [test_detached_style.html]
 [test_dynamic_content_element_matching.html]
 [test_document_adoptnode.html]
 [test_document_importnode.html]
 [test_document_register.html]
-[test_document_register_base_queue.html]
 [test_document_register_lifecycle.html]
 skip-if = true # disabled - See bug 1390396
 [test_document_register_parser.html]
 [test_document_register_stack.html]
 skip-if = true # disabled - See bug 1390396
 [test_document_shared_registry.html]
 [test_event_retarget.html]
 [test_event_stopping.html]
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/webcomponents/test_custom_element_throw_on_dynamic_markup_insertion.html
@@ -0,0 +1,66 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1378079
+-->
+<head>
+  <title>Test throw on dynamic markup insertion when creating element synchronously from parser</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1378079">Bug 1378079</a>
+<div id="container"></div>
+
+<script>
+
+class DoDocumentOpenInCtor extends HTMLElement {
+  constructor() {
+    super();
+    document.open();
+  }
+};
+customElements.define('x-document-open-in-ctor', DoDocumentOpenInCtor);
+
+class DoDocumentWriteInCtor extends HTMLElement {
+  constructor() {
+    super();
+    document.write('<div>This should not be shown</div>');
+  }
+};
+customElements.define('x-document-write-in-ctor', DoDocumentWriteInCtor);
+
+class DoDocumentCloseInCtor extends HTMLElement {
+  constructor() {
+    super();
+    document.close();
+  }
+};
+customElements.define('x-document-close-in-ctor', DoDocumentCloseInCtor);
+
+window.errors = [];
+window.onerror = function(message, url, lineNumber, columnNumber, error) {
+  errors.push(error.name);
+  return true;
+}
+var expectedErrorCount = 0;
+
+document.write("<x-document-open-in-ctor></x-document-open-in-ctor>");
+expectedErrorCount++;
+is(window.errors.length, expectedErrorCount, "expectedErrorCount should be " + expectedErrorCount);
+is(window.errors[expectedErrorCount - 1], 'InvalidStateError', "Error name should be 'InvalidStateError'");
+
+document.write("<x-document-write-in-ctor></x-document-write-in-ctor>");
+expectedErrorCount++;
+is(window.errors.length, expectedErrorCount, "expectedErrorCount should be " + expectedErrorCount);
+is(window.errors[expectedErrorCount - 1], 'InvalidStateError', "Error name should be 'InvalidStateError'");
+
+document.write("<x-document-close-in-ctor></x-document-close-in-ctor>");
+expectedErrorCount++;
+is(window.errors.length, expectedErrorCount, "expectedErrorCount should be " + expectedErrorCount);
+is(window.errors[expectedErrorCount - 1], 'InvalidStateError', "Error name should be 'InvalidStateError'");
+
+</script>
+
+</body>
+</html>
deleted file mode 100644
--- a/dom/tests/mochitest/webcomponents/test_document_register_base_queue.html
+++ /dev/null
@@ -1,48 +0,0 @@
-<!DOCTYPE HTML>
-<html>
-<!--
-https://bugzilla.mozilla.org/show_bug.cgi?id=783129
--->
-<head>
-  <title>Test for document.registerElement lifecycle callback</title>
-  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
-<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
-<script>
-var p = Object.create(HTMLElement.prototype);
-
-var createdCallbackCallCount = 0;
-
-// By the time the base element queue is processed via the microtask,
-// both x-hello elements should be in the document.
-p.createdCallback = function() {
-  var one = document.getElementById("one");
-  var two = document.getElementById("two");
-  isnot(one, null, "First x-hello element should be in the tree.");
-  isnot(two, null, "Second x-hello element should be in the tree.");
-  createdCallbackCallCount++;
-  if (createdCallbackCallCount == 2) {
-    SimpleTest.finish();
-  }
-
-  if (createdCallbackCallCount > 2) {
-    ok(false, "Created callback called too much.");
-  }
-};
-
-p.attributeChangedCallback = function(name, oldValue, newValue) {
-  ok(false, "Attribute changed callback should not be called because callbacks should not be queued until created callback invoked.");
-};
-
-document.registerElement("x-hello", { prototype: p });
-
-SimpleTest.waitForExplicitFinish();
-</script>
-</head>
-<body>
-<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=783129">Bug 783129</a>
-<x-hello id="one"></x-hello>
-<x-hello id="two"></x-hello>
-<script>
-</script>
-</body>
-</html>
--- a/dom/tests/mochitest/webcomponents/test_document_register_parser.html
+++ b/dom/tests/mochitest/webcomponents/test_document_register_parser.html
@@ -6,27 +6,27 @@ https://bugzilla.mozilla.org/show_bug.cg
 <head>
   <title>Test for document.registerElement for elements created by the parser</title>
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
 <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 <script>
 
 var extendedButtonProto = Object.create(HTMLButtonElement.prototype);
 var buttonCallbackCalled = false;
-extendedButtonProto.createdCallback = function() {
+extendedButtonProto.connectedCallback = function() {
   is(buttonCallbackCalled, false, "created callback for x-button should only be called once.");
   is(this.tagName, "BUTTON", "Only the <button> element should be upgraded.");
   buttonCallbackCalled = true;
 };
 
 document.registerElement("x-button", { prototype: extendedButtonProto, extends: "button" });
 
 var divProto = Object.create(HTMLDivElement.prototype);
 var divCallbackCalled = false;
-divProto.createdCallback = function() {
+divProto.connectedCallback = function() {
   is(divCallbackCalled, false, "created callback for x-div should only be called once.");
   is(buttonCallbackCalled, true, "crated callback should be called for x-button before x-div.");
   is(this.tagName, "X-DIV", "Only the <x-div> element should be upgraded.");
   divCallbackCalled = true;
   SimpleTest.finish();
 };
 
 document.registerElement("x-div", { prototype: divProto });
--- a/gfx/gl/GLBlitHelper.cpp
+++ b/gfx/gl/GLBlitHelper.cpp
@@ -51,78 +51,155 @@ const char* const kFragHeader_Tex2DRect 
         #define TEXTURE texture                                              \n\
     #else                                                                    \n\
         #define TEXTURE texture2DRect                                        \n\
     #endif                                                                   \n\
 ";
 const char* const kFragHeader_TexExt = "\
     #extension GL_OES_EGL_image_external : require                           \n\
     #define SAMPLER samplerExternalOES                                       \n\
-    #define TEXTURE texture2D                                                \n\
+    #if __VERSION__ >= 130                                                   \n\
+        #define TEXTURE texture                                              \n\
+    #else                                                                    \n\
+        #define TEXTURE texture2D                                            \n\
+    #endif                                                                   \n\
 ";
 
 const char* const kFragBody_RGBA = "\
     VARYING vec2 vTexCoord0;                                                 \n\
     uniform SAMPLER uTex0;                                                   \n\
                                                                              \n\
     void main(void)                                                          \n\
     {                                                                        \n\
         FRAG_COLOR = TEXTURE(uTex0, vTexCoord0);                             \n\
     }                                                                        \n\
 ";
 const char* const kFragBody_CrYCb = "\
     VARYING vec2 vTexCoord0;                                                 \n\
     uniform SAMPLER uTex0;                                                   \n\
-    uniform mat4 uColorMatrix;                                               \n\
+    uniform MAT4X3 uColorMatrix;                                             \n\
                                                                              \n\
     void main(void)                                                          \n\
     {                                                                        \n\
         vec4 yuv = vec4(TEXTURE(uTex0, vTexCoord0).gbr,                      \n\
                         1.0);                                                \n\
-        vec4 rgb = uColorMatrix * yuv;                                       \n\
-        FRAG_COLOR = vec4(rgb.rgb, 1.0);                                     \n\
+        FRAG_COLOR = vec4((uColorMatrix * yuv).rgb, 1.0);                    \n\
     }                                                                        \n\
 ";
 const char* const kFragBody_NV12 = "\
     VARYING vec2 vTexCoord0;                                                 \n\
     VARYING vec2 vTexCoord1;                                                 \n\
     uniform SAMPLER uTex0;                                                   \n\
     uniform SAMPLER uTex1;                                                   \n\
-    uniform mat4 uColorMatrix;                                               \n\
+    uniform MAT4X3 uColorMatrix;                                             \n\
                                                                              \n\
     void main(void)                                                          \n\
     {                                                                        \n\
         vec4 yuv = vec4(TEXTURE(uTex0, vTexCoord0).x,                        \n\
                         TEXTURE(uTex1, vTexCoord1).xy,                       \n\
                         1.0);                                                \n\
-        vec4 rgb = uColorMatrix * yuv;                                       \n\
-        FRAG_COLOR = vec4(rgb.rgb, 1.0);                                     \n\
+        FRAG_COLOR = vec4((uColorMatrix * yuv).rgb, 1.0);                    \n\
     }                                                                        \n\
 ";
 const char* const kFragBody_PlanarYUV = "\
     VARYING vec2 vTexCoord0;                                                 \n\
     VARYING vec2 vTexCoord1;                                                 \n\
     uniform SAMPLER uTex0;                                                   \n\
     uniform SAMPLER uTex1;                                                   \n\
     uniform SAMPLER uTex2;                                                   \n\
-    uniform mat4 uColorMatrix;                                               \n\
+    uniform MAT4X3 uColorMatrix;                                             \n\
                                                                              \n\
     void main(void)                                                          \n\
     {                                                                        \n\
         vec4 yuv = vec4(TEXTURE(uTex0, vTexCoord0).x,                        \n\
                         TEXTURE(uTex1, vTexCoord1).x,                        \n\
                         TEXTURE(uTex2, vTexCoord1).x,                        \n\
                         1.0);                                                \n\
-        vec4 rgb = uColorMatrix * yuv;                                       \n\
-        FRAG_COLOR = vec4(rgb.rgb, 1.0);                                     \n\
+        FRAG_COLOR = vec4((uColorMatrix * yuv).rgb, 1.0);                    \n\
     }                                                                        \n\
 ";
 
 // --
 
+template<uint8_t N>
+/*static*/ Mat<N>
+Mat<N>::Zero()
+{
+    Mat<N> ret;
+    for (auto& x : ret.m) {
+        x = 0.0f;
+    }
+    return ret;
+}
+
+template<uint8_t N>
+/*static*/ Mat<N>
+Mat<N>::I()
+{
+    auto ret = Mat<N>::Zero();
+    for (uint8_t i = 0; i < N; i++) {
+        ret.at(i,i) = 1.0f;
+    }
+    return ret;
+}
+
+template<uint8_t N>
+Mat<N>
+Mat<N>::operator*(const Mat<N>& r) const
+{
+    Mat<N> ret;
+    for (uint8_t x = 0; x < N; x++) {
+        for (uint8_t y = 0; y < N; y++) {
+            float sum = 0.0f;
+            for (uint8_t i = 0; i < N; i++) {
+                sum += at(i,y) * r.at(x,i);
+            }
+            ret.at(x,y) = sum;
+        }
+    }
+    return ret;
+}
+
+Mat3
+SubRectMat3(const float x, const float y, const float w, const float h)
+{
+    auto ret = Mat3::Zero();
+    ret.at(0,0) = w;
+    ret.at(1,1) = h;
+    ret.at(2,0) = x;
+    ret.at(2,1) = y;
+    ret.at(2,2) = 1.0f;
+    return ret;
+}
+
+Mat3
+SubRectMat3(const gfx::IntRect& subrect, const gfx::IntSize& size)
+{
+    return SubRectMat3(float(subrect.x) / size.width,
+                       float(subrect.y) / size.height,
+                       float(subrect.width) / size.width,
+                       float(subrect.height) / size.height);
+}
+
+Mat3
+SubRectMat3(const gfx::IntRect& bigSubrect, const gfx::IntSize& smallSize,
+            const gfx::IntSize& divisors)
+{
+    const float x = float(bigSubrect.x) / divisors.width;
+    const float y = float(bigSubrect.y) / divisors.height;
+    const float w = float(bigSubrect.width) / divisors.width;
+    const float h = float(bigSubrect.height) / divisors.height;
+    return SubRectMat3(x / smallSize.width,
+                       y / smallSize.height,
+                       w / smallSize.width,
+                       h / smallSize.height);
+}
+
+// --
+
 ScopedSaveMultiTex::ScopedSaveMultiTex(GLContext* const gl, const uint8_t texCount,
                                        const GLenum texTarget)
     : mGL(*gl)
     , mTexCount(texCount)
     , mTexTarget(texTarget)
     , mOldTexUnit(mGL.GetIntAs<GLenum>(LOCAL_GL_ACTIVE_TEXTURE))
 {
     GLenum texBinding;
@@ -261,16 +338,17 @@ public:
             // adds RASTERIZER_DISCARD.
             rasterizerDiscard = Some(mGL.PushEnabled(LOCAL_GL_RASTERIZER_DISCARD, false));
         }
 
         mGL.fGetBooleanv(LOCAL_GL_COLOR_WRITEMASK, colorMask);
         mGL.fColorMask(true, true, true, true);
 
         mGL.fGetIntegerv(LOCAL_GL_VIEWPORT, viewport);
+        MOZ_ASSERT(destSize.width && destSize.height);
         mGL.fViewport(0, 0, destSize.width, destSize.height);
     }
 
     ~ScopedDrawBlitState()
     {
         mGL.SetEnabled(LOCAL_GL_BLEND,                    blend       );
         mGL.SetEnabled(LOCAL_GL_CULL_FACE,                cullFace    );
         mGL.SetEnabled(LOCAL_GL_DEPTH_TEST,               depthTest   );
@@ -289,29 +367,43 @@ public:
     }
 };
 
 // --
 
 DrawBlitProg::DrawBlitProg(const GLBlitHelper* const parent, const GLuint prog)
     : mParent(*parent)
     , mProg(prog)
-    , mLoc_u1ForYFlip(mParent.mGL->fGetUniformLocation(mProg, "u1ForYFlip"))
-    , mLoc_uSrcRect(mParent.mGL->fGetUniformLocation(mProg, "uSrcRect"))
-    , mLoc_uTexSize0(mParent.mGL->fGetUniformLocation(mProg, "uTexSize0"))
-    , mLoc_uTexSize1(mParent.mGL->fGetUniformLocation(mProg, "uTexSize1"))
-    , mLoc_uDivisors(mParent.mGL->fGetUniformLocation(mProg, "uDivisors"))
+    , mLoc_uDestMatrix(mParent.mGL->fGetUniformLocation(mProg, "uDestMatrix"))
+    , mLoc_uTexMatrix0(mParent.mGL->fGetUniformLocation(mProg, "uTexMatrix0"))
+    , mLoc_uTexMatrix1(mParent.mGL->fGetUniformLocation(mProg, "uTexMatrix1"))
     , mLoc_uColorMatrix(mParent.mGL->fGetUniformLocation(mProg, "uColorMatrix"))
+    , mType_uColorMatrix(0)
 {
-    MOZ_ASSERT(mLoc_u1ForYFlip != -1);
-    MOZ_ASSERT(mLoc_uSrcRect != -1);
-    MOZ_ASSERT(mLoc_uTexSize0 != -1);
+    MOZ_ASSERT(mLoc_uDestMatrix != -1);
+    MOZ_ASSERT(mLoc_uTexMatrix0 != -1);
     if (mLoc_uColorMatrix != -1) {
-        MOZ_ASSERT(mLoc_uTexSize1 != -1);
-        MOZ_ASSERT(mLoc_uDivisors != -1);
+        MOZ_ASSERT(mLoc_uTexMatrix1 != -1);
+
+        const auto& gl = mParent.mGL;
+        int32_t numActiveUniforms = 0;
+        gl->fGetProgramiv(mProg, LOCAL_GL_ACTIVE_UNIFORMS, &numActiveUniforms);
+
+        const size_t kMaxNameSize = 32;
+        char name[kMaxNameSize] = {0};
+        GLint size = 0;
+        GLenum type = 0;
+        for (int32_t i = 0; i < numActiveUniforms; i++) {
+            gl->fGetActiveUniform(mProg, i, kMaxNameSize, nullptr, &size, &type, name);
+            if (strcmp("uColorMatrix", name) == 0) {
+                mType_uColorMatrix = type;
+                break;
+            }
+        }
+        MOZ_ASSERT(mType_uColorMatrix);
     }
 }
 
 DrawBlitProg::~DrawBlitProg()
 {
     const auto& gl = mParent.mGL;
     if (!gl->MakeCurrent())
         return;
@@ -324,28 +416,59 @@ DrawBlitProg::Draw(const BaseArgs& args,
 {
     const auto& gl = mParent.mGL;
 
     const SaveRestoreCurrentProgram oldProg(gl);
     gl->fUseProgram(mProg);
 
     // --
 
-    gl->fUniform1f(mLoc_u1ForYFlip, args.yFlip ? 1 : 0);
-    gl->fUniform4f(mLoc_uSrcRect,
-                   args.srcRect.x, args.srcRect.y,
-                   args.srcRect.width, args.srcRect.height);
-    gl->fUniform2f(mLoc_uTexSize0, args.texSize0.width, args.texSize0.height);
+    Mat3 destMatrix;
+    if (args.destRect) {
+        const auto& destRect = args.destRect.value();
+        destMatrix = SubRectMat3(destRect.x / args.destSize.width,
+                                 destRect.y / args.destSize.height,
+                                 destRect.width / args.destSize.width,
+                                 destRect.height / args.destSize.height);
+    } else {
+        destMatrix = Mat3::I();
+    }
+
+    if (args.yFlip) {
+        // Apply the y-flip matrix before the destMatrix.
+        // That is, flip y=[0-1] to y=[1-0] before we restrict to the destRect.
+        destMatrix.at(2,1) += destMatrix.at(1,1);
+        destMatrix.at(1,1) *= -1.0f;
+    }
+
+    gl->fUniformMatrix3fv(mLoc_uDestMatrix, 1, false, destMatrix.m);
+    gl->fUniformMatrix3fv(mLoc_uTexMatrix0, 1, false, args.texMatrix0.m);
 
     MOZ_ASSERT(bool(argsYUV) == (mLoc_uColorMatrix != -1));
     if (argsYUV) {
-        gl->fUniform2f(mLoc_uTexSize1, argsYUV->texSize1.width, argsYUV->texSize1.height);
-        gl->fUniform2f(mLoc_uDivisors, argsYUV->divisors.width, argsYUV->divisors.height);
+        gl->fUniformMatrix3fv(mLoc_uTexMatrix1, 1, false, argsYUV->texMatrix1.m);
+
         const auto& colorMatrix = gfxUtils::YuvToRgbMatrix4x4ColumnMajor(argsYUV->colorSpace);
-        gl->fUniformMatrix4fv(mLoc_uColorMatrix, 1, false, colorMatrix);
+        float mat4x3[4*3];
+        switch (mType_uColorMatrix) {
+        case LOCAL_GL_FLOAT_MAT4:
+            gl->fUniformMatrix4fv(mLoc_uColorMatrix, 1, false, colorMatrix);
+            break;
+        case LOCAL_GL_FLOAT_MAT4x3:
+            for (int x = 0; x < 4; x++) {
+                for (int y = 0; y < 3; y++) {
+                    mat4x3[3*x+y] = colorMatrix[4*x+y];
+                }
+            }
+            gl->fUniformMatrix4x3fv(mLoc_uColorMatrix, 1, false, mat4x3);
+            break;
+        default:
+            gfxCriticalError() << "Bad mType_uColorMatrix: "
+                               << gfx::hexa(mType_uColorMatrix);
+        }
     }
 
     // --
 
     const ScopedDrawBlitState drawState(gl, args.destSize);
 
     GLuint oldVAO;
     GLint vaa0Enabled;
@@ -419,57 +542,54 @@ GLBlitHelper::GLBlitHelper(GLContext* co
             mGL->fVertexAttribPointer(0, 2, LOCAL_GL_FLOAT, false, 0, 0);
 
             mGL->fBindVertexArray(prev);
         }
     }
 
     // --
 
-    if (!mGL->IsGLES()) {
-        const auto glslVersion = mGL->ShadingLanguageVersion();
+    const auto glslVersion = mGL->ShadingLanguageVersion();
+    if (mGL->IsGLES()) {
+        if (glslVersion >= 300) {
+            mDrawBlitProg_VersionLine = nsPrintfCString("#version %u es\n", glslVersion);
+        }
+    } else {
         if (glslVersion >= 130) {
             mDrawBlitProg_VersionLine = nsPrintfCString("#version %u\n", glslVersion);
         }
     }
 
     const char kVertSource[] = "\
         #if __VERSION__ >= 130                                               \n\
             #define ATTRIBUTE in                                             \n\
             #define VARYING out                                              \n\
         #else                                                                \n\
             #define ATTRIBUTE attribute                                      \n\
             #define VARYING varying                                          \n\
         #endif                                                               \n\
                                                                              \n\
-        ATTRIBUTE vec2 aVert;                                                \n\
+        ATTRIBUTE vec2 aVert; // [0.0-1.0]                                   \n\
                                                                              \n\
-        uniform float u1ForYFlip;                                            \n\
-        uniform vec4 uSrcRect;                                               \n\
-        uniform vec2 uTexSize0;                                              \n\
-        uniform vec2 uTexSize1;                                              \n\
-        uniform vec2 uDivisors;                                              \n\
+        uniform mat3 uDestMatrix;                                            \n\
+        uniform mat3 uTexMatrix0;                                            \n\
+        uniform mat3 uTexMatrix1;                                            \n\
                                                                              \n\
         VARYING vec2 vTexCoord0;                                             \n\
         VARYING vec2 vTexCoord1;                                             \n\
                                                                              \n\
         void main(void)                                                      \n\
         {                                                                    \n\
-            vec2 vertPos = aVert * 2.0 - 1.0;                                \n\
-            gl_Position = vec4(vertPos, 0.0, 1.0);                           \n\
+            vec2 destPos = (uDestMatrix * vec3(aVert, 1.0)).xy;              \n\
+            gl_Position = vec4(destPos * 2.0 - 1.0, 0.0, 1.0);               \n\
                                                                              \n\
-            vec2 texCoord = aVert;                                           \n\
-            texCoord.y = abs(u1ForYFlip - texCoord.y);                       \n\
-            texCoord = texCoord * uSrcRect.zw + uSrcRect.xy;                 \n\
-                                                                             \n\
-            vTexCoord0 = texCoord / uTexSize0;                               \n\
-            vTexCoord1 = texCoord / (uTexSize1 * uDivisors);                 \n\
+            vTexCoord0 = (uTexMatrix0 * vec3(aVert, 1.0)).xy;                \n\
+            vTexCoord1 = (uTexMatrix1 * vec3(aVert, 1.0)).xy;                \n\
         }                                                                    \n\
     ";
-
     const char* const parts[] = {
         mDrawBlitProg_VersionLine.get(),
         kVertSource
     };
     mGL->fShaderSource(mDrawBlitProg_VertShader, ArrayLength(parts), parts, nullptr);
     mGL->fCompileShader(mDrawBlitProg_VertShader);
 }
 
@@ -501,38 +621,42 @@ GLBlitHelper::GetDrawBlitProg(const Draw
     auto& pair = *(res.first);
     const auto& didInsert = res.second;
     if (didInsert) {
         pair.second = CreateDrawBlitProg(pair.first);
     }
     return pair.second;
 }
 
-
 const DrawBlitProg*
 GLBlitHelper::CreateDrawBlitProg(const DrawBlitProg::Key& key) const
 {
     const char kFragHeader_Global[] = "\
         #ifdef GL_ES                                                         \n\
             #ifdef GL_FRAGMENT_PRECISION_HIGH                                \n\
                 precision highp float;                                       \n\
             #else                                                            \n\
                 precision mediump float;                                     \n\
             #endif                                                           \n\
         #endif                                                               \n\
                                                                              \n\
         #if __VERSION__ >= 130                                               \n\