From 86873ad352961c2457d536690e8511eaa2ef3c41 Mon Sep 17 00:00:00 2001 From: Jonathan Watt Date: Mon, 29 Sep 2014 18:26:31 +0100 Subject: [PATCH 01/82] Bug 1071774 - Restore the current transform in a couple of places that fail to do so. r=seth --- dom/canvas/CanvasRenderingContext2D.cpp | 5 ++++ image/src/imgFrame.cpp | 7 ++++++ ...nvas-drawImage-transform-restored-ref.html | 18 ++++++++++++++ .../canvas-drawImage-transform-restored.html | 24 +++++++++++++++++++ layout/reftests/svg/as-image/reftest.list | 1 + 5 files changed, 55 insertions(+) create mode 100644 layout/reftests/svg/as-image/canvas-drawImage-transform-restored-ref.html create mode 100644 layout/reftests/svg/as-image/canvas-drawImage-transform-restored.html diff --git a/dom/canvas/CanvasRenderingContext2D.cpp b/dom/canvas/CanvasRenderingContext2D.cpp index 6aa1467d2e8f..d03eb82e4ac3 100644 --- a/dom/canvas/CanvasRenderingContext2D.cpp +++ b/dom/canvas/CanvasRenderingContext2D.cpp @@ -5,6 +5,7 @@ #include "CanvasRenderingContext2D.h" +#include "mozilla/gfx/Helpers.h" #include "nsXULElement.h" #include "nsIServiceManager.h" @@ -3983,6 +3984,10 @@ CanvasRenderingContext2D::DrawDirectlyToCanvas( std::ceil(imgSize.height * scale.height)); src.Scale(scale.width, scale.height); + // We're wrapping tempTarget's (our) DrawTarget here, so we need to restore + // the matrix even though this is a temp gfxContext. + AutoSaveTransform autoSR(mTarget); + nsRefPtr context = new gfxContext(tempTarget); context->SetMatrix(contextMatrix. Scale(1.0 / contextScale.width, diff --git a/image/src/imgFrame.cpp b/image/src/imgFrame.cpp index 9c2352c79e65..3f70652f45fa 100644 --- a/image/src/imgFrame.cpp +++ b/image/src/imgFrame.cpp @@ -530,6 +530,13 @@ bool imgFrame::Draw(gfxContext* aContext, const ImageRegion& aRegion, bool doTile = !imageRect.Contains(aRegion.Rect()) && !(aImageFlags & imgIContainer::FLAG_CLAMP); ImageRegion region(aRegion); + // SurfaceForDrawing changes the current transform, and we need it to still + // be changed when we call gfxUtils::DrawPixelSnapped. We still need to + // restore it before returning though. + // XXXjwatt In general having functions require someone further up the stack + // to undo transform changes that they make is bad practice. We should + // change how this code works. + gfxContextMatrixAutoSaveRestore autoSR(aContext); SurfaceWithFormat surfaceResult = SurfaceForDrawing(doPadding, doPartialDecode, doTile, aContext, aPadding, imageRect, region, surf); diff --git a/layout/reftests/svg/as-image/canvas-drawImage-transform-restored-ref.html b/layout/reftests/svg/as-image/canvas-drawImage-transform-restored-ref.html new file mode 100644 index 000000000000..226a6dd576de --- /dev/null +++ b/layout/reftests/svg/as-image/canvas-drawImage-transform-restored-ref.html @@ -0,0 +1,18 @@ + + + + + + + + diff --git a/layout/reftests/svg/as-image/canvas-drawImage-transform-restored.html b/layout/reftests/svg/as-image/canvas-drawImage-transform-restored.html new file mode 100644 index 000000000000..aedd6b38d848 --- /dev/null +++ b/layout/reftests/svg/as-image/canvas-drawImage-transform-restored.html @@ -0,0 +1,24 @@ + + + Test that drawImage() calls don't reset the canvas' transform + + + + + + + diff --git a/layout/reftests/svg/as-image/reftest.list b/layout/reftests/svg/as-image/reftest.list index 1616e0f0675a..353e3c6b0d18 100644 --- a/layout/reftests/svg/as-image/reftest.list +++ b/layout/reftests/svg/as-image/reftest.list @@ -54,6 +54,7 @@ skip-if(B2G) == canvas-drawImage-slice-1a.html lime100x100-ref.html == canvas-drawImage-slice-1b.html lime100x100-ref.html == canvas-drawImage-origin-clean-1.html lime100x100-ref.html +== canvas-drawImage-transform-restored.html canvas-drawImage-transform-restored-ref.html # Simple tests == img-simple-1.html lime100x100-ref.html From 70f7d2d4a29b62d575417d21a4572f8669d43ef0 Mon Sep 17 00:00:00 2001 From: Jonathan Watt Date: Mon, 29 Sep 2014 18:26:49 +0100 Subject: [PATCH 02/82] Bug 1074128, part 1 - Add support to Moz2D's AutoSaveTransform for setting the DrawTarget lazily. r=Bas --- gfx/2d/Helpers.h | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/gfx/2d/Helpers.h b/gfx/2d/Helpers.h index 4a755767ff34..d3e60c6adfeb 100644 --- a/gfx/2d/Helpers.h +++ b/gfx/2d/Helpers.h @@ -19,9 +19,21 @@ class AutoSaveTransform mOldTransform(aTarget->GetTransform()) { } + + void Init(DrawTarget *aTarget) + { + MOZ_ASSERT(!mDrawTarget || aTarget == mDrawTarget); + if (!mDrawTarget) { + mDrawTarget = aTarget; + mOldTransform = aTarget->GetTransform(); + } + } + ~AutoSaveTransform() { - mDrawTarget->SetTransform(mOldTransform); + if (mDrawTarget) { + mDrawTarget->SetTransform(mOldTransform); + } } private: From c5ecdd683d70a0f3890aac2954d0708a8d14bdc5 Mon Sep 17 00:00:00 2001 From: Jonathan Watt Date: Mon, 29 Sep 2014 18:26:56 +0100 Subject: [PATCH 03/82] Bug 1074128, part 2 - Rename Moz2D's AutoSaveTransform to AutoRestoreTransform. r=Bas --- gfx/2d/Helpers.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/gfx/2d/Helpers.h b/gfx/2d/Helpers.h index d3e60c6adfeb..dc93ff7fb460 100644 --- a/gfx/2d/Helpers.h +++ b/gfx/2d/Helpers.h @@ -11,10 +11,10 @@ namespace mozilla { namespace gfx { -class AutoSaveTransform +class AutoRestoreTransform { public: - explicit AutoSaveTransform(DrawTarget *aTarget) + explicit AutoRestoreTransform(DrawTarget *aTarget) : mDrawTarget(aTarget), mOldTransform(aTarget->GetTransform()) { @@ -29,7 +29,7 @@ class AutoSaveTransform } } - ~AutoSaveTransform() + ~AutoRestoreTransform() { if (mDrawTarget) { mDrawTarget->SetTransform(mOldTransform); From 243d1f8a0e7f5733183b8857bf0ee66a0ac89c8e Mon Sep 17 00:00:00 2001 From: Jonathan Watt Date: Mon, 29 Sep 2014 18:32:36 +0100 Subject: [PATCH 04/82] Bug 1074128, part 3 - Update the Mozilla code to use AutoSaveTransform's new name. r=Bas --- dom/canvas/CanvasRenderingContext2D.cpp | 4 ++-- gfx/layers/basic/BasicCompositor.cpp | 2 +- layout/svg/nsFilterInstance.cpp | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/dom/canvas/CanvasRenderingContext2D.cpp b/dom/canvas/CanvasRenderingContext2D.cpp index d03eb82e4ac3..0c1a96d8324b 100644 --- a/dom/canvas/CanvasRenderingContext2D.cpp +++ b/dom/canvas/CanvasRenderingContext2D.cpp @@ -377,7 +377,7 @@ public: RefPtr strokePaint = DoSourcePaint(mStrokePaintRect, CanvasRenderingContext2D::Style::STROKE); - AutoSaveTransform autoSaveTransform(mFinalTarget); + AutoRestoreTransform autoRestoreTransform(mFinalTarget); mFinalTarget->SetTransform(Matrix()); mgfx::FilterSupport::RenderFilterDescription( @@ -3986,7 +3986,7 @@ CanvasRenderingContext2D::DrawDirectlyToCanvas( // We're wrapping tempTarget's (our) DrawTarget here, so we need to restore // the matrix even though this is a temp gfxContext. - AutoSaveTransform autoSR(mTarget); + AutoRestoreTransform autoRestoreTransform(mTarget); nsRefPtr context = new gfxContext(tempTarget); context->SetMatrix(contextMatrix. diff --git a/gfx/layers/basic/BasicCompositor.cpp b/gfx/layers/basic/BasicCompositor.cpp index 6075d57e4246..b2d446b50926 100644 --- a/gfx/layers/basic/BasicCompositor.cpp +++ b/gfx/layers/basic/BasicCompositor.cpp @@ -251,7 +251,7 @@ BasicCompositor::DrawQuad(const gfx::Rect& aRect, RefPtr dest = buffer; buffer->PushClipRect(aClipRect); - AutoSaveTransform autoSaveTransform(dest); + AutoRestoreTransform autoRestoreTransform(dest); Matrix newTransform; Rect transformBounds; diff --git a/layout/svg/nsFilterInstance.cpp b/layout/svg/nsFilterInstance.cpp index bc08c7f87738..eaca8db5cb66 100644 --- a/layout/svg/nsFilterInstance.cpp +++ b/layout/svg/nsFilterInstance.cpp @@ -472,7 +472,7 @@ nsFilterInstance::Render(gfxContext* aContext) RefPtr dt = aContext->GetDrawTarget(); - AutoSaveTransform autoSR(dt); + AutoRestoreTransform autoRestoreTransform(dt); Matrix newTM = ToMatrix(ctm).PreTranslate(filterRect.x, filterRect.y) * dt->GetTransform(); dt->SetTransform(newTM); From ab09f4e969d50315b02a13371795c14ecd434c81 Mon Sep 17 00:00:00 2001 From: Nikhil Marathe Date: Mon, 29 Sep 2014 10:28:52 -0700 Subject: [PATCH 05/82] Bug 1074286 - Fix Headers::Fill(const Headers&, ErrorResult&). r=bkelly --HG-- extra : transplant_source : %D1%01q%8E%B5%CAmG%16%D75tb%FC%1CM%A22%B5Z --- dom/fetch/Headers.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/dom/fetch/Headers.cpp b/dom/fetch/Headers.cpp index c16712202f56..3b348db72afc 100644 --- a/dom/fetch/Headers.cpp +++ b/dom/fetch/Headers.cpp @@ -297,9 +297,13 @@ Headers::IsForbiddenResponseHeader(const nsACString& aName) const } void -Headers::Fill(const Headers& aInit, ErrorResult&) +Headers::Fill(const Headers& aInit, ErrorResult& aRv) { - mList = aInit.mList; + const nsTArray& list = aInit.mList; + for (uint32_t i = 0; i < list.Length() && !aRv.Failed(); ++i) { + const Entry& entry = list[i]; + Append(entry.mName, entry.mValue, aRv); + } } void From 5d3bd1ee52a5b0374dd02e678b2f870fb9104e69 Mon Sep 17 00:00:00 2001 From: Ehsan Akhgari Date: Mon, 29 Sep 2014 14:28:48 -0400 Subject: [PATCH 06/82] Bug 1073820 - Remove an unused variable from accessible; r=surkov --- accessible/generic/Accessible.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/accessible/generic/Accessible.cpp b/accessible/generic/Accessible.cpp index a5ff011c470b..94ab78929fde 100644 --- a/accessible/generic/Accessible.cpp +++ b/accessible/generic/Accessible.cpp @@ -240,7 +240,7 @@ Accessible::Description(nsString& aDescription) if (!aDescription.IsEmpty()) { aDescription.CompressWhitespace(); nsAutoString name; - ENameValueFlag nameFlag = Name(name); + Name(name); // Don't expose a description if it is the same as the name. if (aDescription.Equals(name)) aDescription.Truncate(); From eee03eff661794832e2fb203db5a9f4a57c75dff Mon Sep 17 00:00:00 2001 From: Trevor Saunders Date: Fri, 7 Mar 2014 16:35:19 -0500 Subject: [PATCH 07/82] bug 982842 - initial a11y ipc impl r=davidb, bent --- accessible/atk/AccessibleWrap.cpp | 94 +++++++++++--- accessible/atk/AccessibleWrap.h | 2 +- accessible/atk/moz.build | 1 + accessible/atk/nsMai.h | 7 + accessible/base/AccEvent.h | 2 + accessible/base/DocManager.cpp | 9 ++ accessible/base/DocManager.h | 25 ++++ accessible/base/EventQueue.cpp | 11 ++ accessible/base/NotificationController.cpp | 15 ++- accessible/base/Platform.h | 13 ++ accessible/base/Role.h | 4 +- accessible/base/moz.build | 3 + accessible/generic/DocAccessible.cpp | 16 ++- accessible/generic/DocAccessible.h | 18 +++ accessible/generic/moz.build | 3 + accessible/ipc/DocAccessibleChild.cpp | 40 ++++++ accessible/ipc/DocAccessibleChild.h | 43 +++++++ accessible/ipc/DocAccessibleParent.cpp | 112 ++++++++++++++++ accessible/ipc/DocAccessibleParent.h | 141 +++++++++++++++++++++ accessible/ipc/PDocAccessible.ipdl | 44 +++++++ accessible/ipc/ProxyAccessible.cpp | 42 ++++++ accessible/ipc/ProxyAccessible.h | 77 +++++++++++ accessible/ipc/moz.build | 28 ++++ accessible/mac/Platform.mm | 9 ++ accessible/moz.build | 2 +- accessible/other/Platform.cpp | 10 ++ accessible/windows/msaa/Platform.cpp | 9 ++ dom/ipc/ContentChild.cpp | 15 +++ dom/ipc/ContentChild.h | 2 + dom/ipc/ContentParent.cpp | 30 +++++ dom/ipc/ContentParent.h | 5 + dom/ipc/PContent.ipdl | 10 ++ 32 files changed, 821 insertions(+), 21 deletions(-) create mode 100644 accessible/ipc/DocAccessibleChild.cpp create mode 100644 accessible/ipc/DocAccessibleChild.h create mode 100644 accessible/ipc/DocAccessibleParent.cpp create mode 100644 accessible/ipc/DocAccessibleParent.h create mode 100644 accessible/ipc/PDocAccessible.ipdl create mode 100644 accessible/ipc/ProxyAccessible.cpp create mode 100644 accessible/ipc/ProxyAccessible.h create mode 100644 accessible/ipc/moz.build diff --git a/accessible/atk/AccessibleWrap.cpp b/accessible/atk/AccessibleWrap.cpp index 5f98faac2cc5..cca059e9b34c 100644 --- a/accessible/atk/AccessibleWrap.cpp +++ b/accessible/atk/AccessibleWrap.cpp @@ -12,6 +12,7 @@ #include "nsAccUtils.h" #include "nsIAccessibleRelation.h" #include "nsIAccessibleTable.h" +#include "ProxyAccessible.h" #include "RootAccessible.h" #include "nsIAccessibleValue.h" #include "nsMai.h" @@ -20,6 +21,7 @@ #include "nsAutoPtr.h" #include "prprf.h" #include "nsStateMap.h" +#include "mozilla/a11y/Platform.h" #include "Relation.h" #include "RootAccessible.h" #include "States.h" @@ -133,9 +135,13 @@ struct MaiAtkObject * The AccessibleWrap whose properties and features are exported * via this object instance. */ - AccessibleWrap* accWrap; + uintptr_t accWrap; }; +// This is or'd with the pointer in MaiAtkObject::accWrap if the wrap-ee is a +// proxy. +static const uintptr_t IS_PROXY = 1; + struct MaiAtkObjectClass { AtkObjectClass parent_class; @@ -248,7 +254,7 @@ AccessibleWrap::ShutdownAtkObject() { if (mAtkObject) { if (IS_MAI_OBJECT(mAtkObject)) { - MAI_ATK_OBJECT(mAtkObject)->accWrap = nullptr; + MAI_ATK_OBJECT(mAtkObject)->accWrap = 0; } SetMaiHyperlink(nullptr); g_object_unref(mAtkObject); @@ -582,8 +588,7 @@ initializeCB(AtkObject *aAtkObj, gpointer aData) ATK_OBJECT_CLASS(parent_class)->initialize(aAtkObj, aData); /* initialize object */ - MAI_ATK_OBJECT(aAtkObj)->accWrap = - static_cast(aData); + MAI_ATK_OBJECT(aAtkObj)->accWrap = reinterpret_cast(aData); } void @@ -591,7 +596,7 @@ finalizeCB(GObject *aObj) { if (!IS_MAI_OBJECT(aObj)) return; - NS_ASSERTION(MAI_ATK_OBJECT(aObj)->accWrap == nullptr, "AccWrap NOT null"); + NS_ASSERTION(MAI_ATK_OBJECT(aObj)->accWrap == 0, "AccWrap NOT null"); // call parent finalize function // finalize of GObjectClass will unref the accessible parent if has @@ -663,25 +668,33 @@ getDescriptionCB(AtkObject *aAtkObj) AtkRole getRoleCB(AtkObject *aAtkObj) { - AccessibleWrap* accWrap = GetAccessibleWrap(aAtkObj); - if (!accWrap) - return ATK_ROLE_INVALID; - -#ifdef DEBUG - NS_ASSERTION(nsAccUtils::IsTextInterfaceSupportCorrect(accWrap), - "Does not support nsIAccessibleText when it should"); -#endif - if (aAtkObj->role != ATK_ROLE_INVALID) return aAtkObj->role; + AccessibleWrap* accWrap = GetAccessibleWrap(aAtkObj); + a11y::role role; + if (!accWrap) { + ProxyAccessible* proxy = GetProxy(aAtkObj); + if (!proxy) + return ATK_ROLE_INVALID; + + role = proxy->Role(); + } else { +#ifdef DEBUG + NS_ASSERTION(nsAccUtils::IsTextInterfaceSupportCorrect(accWrap), + "Does not support nsIAccessibleText when it should"); +#endif + + role = accWrap->Role(); + } + #define ROLE(geckoRole, stringRole, atkRole, macRole, \ msaaRole, ia2Role, nameRule) \ case roles::geckoRole: \ aAtkObj->role = atkRole; \ break; - switch (accWrap->Role()) { + switch (role) { #include "RoleMap.h" default: MOZ_CRASH("Unknown role."); @@ -946,7 +959,13 @@ AccessibleWrap* GetAccessibleWrap(AtkObject* aAtkObj) { NS_ENSURE_TRUE(IS_MAI_OBJECT(aAtkObj), nullptr); - AccessibleWrap* accWrap = MAI_ATK_OBJECT(aAtkObj)->accWrap; + + // Make sure its native is an AccessibleWrap not a proxy. + if (MAI_ATK_OBJECT(aAtkObj)->accWrap & IS_PROXY) + return nullptr; + + AccessibleWrap* accWrap = + reinterpret_cast(MAI_ATK_OBJECT(aAtkObj)->accWrap); // Check if the accessible was deconstructed. if (!accWrap) @@ -961,6 +980,49 @@ GetAccessibleWrap(AtkObject* aAtkObj) return accWrap; } +ProxyAccessible* +GetProxy(AtkObject* aObj) +{ + if (!aObj || !(MAI_ATK_OBJECT(aObj)->accWrap & IS_PROXY)) + return nullptr; + + return reinterpret_cast(MAI_ATK_OBJECT(aObj)->accWrap + & ~IS_PROXY); +} + +static uint16_t +GetInterfacesForProxy(ProxyAccessible* aProxy) +{ + return MAI_INTERFACE_COMPONENT; +} + +void +a11y::ProxyCreated(ProxyAccessible* aProxy) +{ + GType type = GetMaiAtkType(GetInterfacesForProxy(aProxy)); + NS_ASSERTION(type, "why don't we have a type!"); + + AtkObject* obj = + reinterpret_cast + (g_object_new(type, nullptr)); + if (!obj) + return; + + atk_object_initialize(obj, aProxy); + obj->role = ATK_ROLE_INVALID; + obj->layer = ATK_LAYER_INVALID; + aProxy->SetWrapper(reinterpret_cast(obj) | IS_PROXY); +} + +void +a11y::ProxyDestroyed(ProxyAccessible* aProxy) +{ + auto obj = reinterpret_cast(aProxy->GetWrapper() & ~IS_PROXY); + obj->accWrap = 0; + g_object_unref(obj); + aProxy->SetWrapper(0); +} + nsresult AccessibleWrap::HandleAccEvent(AccEvent* aEvent) { diff --git a/accessible/atk/AccessibleWrap.h b/accessible/atk/AccessibleWrap.h index 5518a80bd5d8..95d03c5fc16c 100644 --- a/accessible/atk/AccessibleWrap.h +++ b/accessible/atk/AccessibleWrap.h @@ -97,7 +97,7 @@ private: static EAvailableAtkSignals gAvailableAtkSignals; - uint16_t CreateMaiInterfaces(void); + uint16_t CreateMaiInterfaces(); }; } // namespace a11y diff --git a/accessible/atk/moz.build b/accessible/atk/moz.build index 37ec9e8fded5..96fc8cee15d7 100644 --- a/accessible/atk/moz.build +++ b/accessible/atk/moz.build @@ -35,6 +35,7 @@ LOCAL_INCLUDES += [ '/accessible/base', '/accessible/generic', '/accessible/html', + '/accessible/ipc', '/accessible/xpcom', '/accessible/xul', '/other-licenses/atk-1.0', diff --git a/accessible/atk/nsMai.h b/accessible/atk/nsMai.h index 5de552ac67a5..6338a3492bae 100644 --- a/accessible/atk/nsMai.h +++ b/accessible/atk/nsMai.h @@ -13,6 +13,12 @@ #include "AccessibleWrap.h" +namespace mozilla { +namespace a11y { +class ProxyAccessible; +} +} + #define MAI_TYPE_ATK_OBJECT (mai_atk_object_get_type ()) #define MAI_ATK_OBJECT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ MAI_TYPE_ATK_OBJECT, MaiAtkObject)) @@ -29,6 +35,7 @@ GType mai_atk_object_get_type(void); GType mai_util_get_type(); mozilla::a11y::AccessibleWrap* GetAccessibleWrap(AtkObject* aAtkObj); +mozilla::a11y::ProxyAccessible* GetProxy(AtkObject* aAtkObj); extern int atkMajorVersion, atkMinorVersion; diff --git a/accessible/base/AccEvent.h b/accessible/base/AccEvent.h index f97b1fc4e01b..ee28efa511b6 100644 --- a/accessible/base/AccEvent.h +++ b/accessible/base/AccEvent.h @@ -232,6 +232,8 @@ public: bool IsShow() const { return mEventType == nsIAccessibleEvent::EVENT_SHOW; } bool IsHide() const { return mEventType == nsIAccessibleEvent::EVENT_HIDE; } + Accessible* Parent() const { return mParent; } + protected: nsCOMPtr mNode; nsRefPtr mParent; diff --git a/accessible/base/DocManager.cpp b/accessible/base/DocManager.cpp index eed51d5ca01a..985ba64f5853 100644 --- a/accessible/base/DocManager.cpp +++ b/accessible/base/DocManager.cpp @@ -8,6 +8,7 @@ #include "ApplicationAccessible.h" #include "ARIAMap.h" #include "DocAccessible-inl.h" +#include "DocAccessibleChild.h" #include "nsAccessibilityService.h" #include "RootAccessibleWrap.h" @@ -27,6 +28,8 @@ #include "nsServiceManagerUtils.h" #include "nsIWebProgress.h" #include "nsCoreUtils.h" +#include "nsXULAppAPI.h" +#include "mozilla/dom/ContentChild.h" using namespace mozilla; using namespace mozilla::a11y; @@ -418,6 +421,12 @@ DocManager::CreateDocOrRootAccessible(nsIDocument* aDocument) docAcc->FireDelayedEvent(nsIAccessibleEvent::EVENT_REORDER, ApplicationAcc()); + if (XRE_GetProcessType() != GeckoProcessType_Default) { + DocAccessibleChild* ipcDoc = new DocAccessibleChild(docAcc); + docAcc->SetIPCDoc(ipcDoc); + auto contentChild = dom::ContentChild::GetSingleton(); + contentChild->SendPDocAccessibleConstructor(ipcDoc, nullptr, 0); + } } else { parentDocAcc->BindChildDocument(docAcc); } diff --git a/accessible/base/DocManager.h b/accessible/base/DocManager.h index 13e80a602372..69193820eb67 100644 --- a/accessible/base/DocManager.h +++ b/accessible/base/DocManager.h @@ -17,6 +17,7 @@ namespace a11y { class Accessible; class DocAccessible; +class DocAccessibleParent; /** * Manage the document accessible life cycle. @@ -65,6 +66,25 @@ public: RemoveListeners(aDocument); } + /* + * Notification that a top level document in a content process has gone away. + */ + void RemoteDocShutdown(DocAccessibleParent* aDoc) + { + DebugOnly result = mRemoteDocuments.RemoveElement(aDoc); + MOZ_ASSERT(result, "Why didn't we find the document!"); + } + + /* + * Notify of a new top level document in a content process. + */ + void RemoteDocAdded(DocAccessibleParent* aDoc) + { + MOZ_ASSERT(!mRemoteDocuments.Contains(aDoc), + "How did we already have the doc!"); + mRemoteDocuments.AppendElement(aDoc); + } + #ifdef DEBUG bool IsProcessingRefreshDriverNotification() const; #endif @@ -144,6 +164,11 @@ private: #endif DocAccessibleHashtable mDocAccessibleCache; + + /* + * The list of remote top level documents. + */ + nsTArray mRemoteDocuments; }; /** diff --git a/accessible/base/EventQueue.cpp b/accessible/base/EventQueue.cpp index 6338785e49c9..65102917efef 100644 --- a/accessible/base/EventQueue.cpp +++ b/accessible/base/EventQueue.cpp @@ -8,6 +8,7 @@ #include "Accessible-inl.h" #include "nsEventShell.h" #include "DocAccessible.h" +#include "DocAccessibleChild.h" #include "nsAccessibilityService.h" #include "nsTextEquivUtils.h" #ifdef A11Y_LOG @@ -555,5 +556,15 @@ EventQueue::ProcessEventQueue() if (!mDocument) return; + + if (XRE_GetProcessType() != GeckoProcessType_Default) { + DocAccessibleChild* ipcDoc = mDocument->IPCDoc(); + if (event->mEventType == nsIAccessibleEvent::EVENT_SHOW) + ipcDoc->ShowEvent(downcast_accEvent(event)); + else if (event->mEventType == nsIAccessibleEvent::EVENT_HIDE) + ipcDoc->SendHideEvent(reinterpret_cast(event->GetAccessible())); + else + ipcDoc->SendEvent(event->GetEventType()); + } } } diff --git a/accessible/base/NotificationController.cpp b/accessible/base/NotificationController.cpp index 82a591ab72c1..b45e23a2ab69 100644 --- a/accessible/base/NotificationController.cpp +++ b/accessible/base/NotificationController.cpp @@ -6,9 +6,11 @@ #include "NotificationController.h" #include "DocAccessible-inl.h" +#include "DocAccessibleChild.h" #include "TextLeafAccessible.h" #include "TextUpdater.h" +#include "mozilla/dom/ContentChild.h" #include "mozilla/dom/Element.h" #include "mozilla/Telemetry.h" @@ -217,8 +219,19 @@ NotificationController::WillRefresh(mozilla::TimeStamp aTime) if (ownerContent) { Accessible* outerDocAcc = mDocument->GetAccessible(ownerContent); if (outerDocAcc && outerDocAcc->AppendChild(childDoc)) { - if (mDocument->AppendChildDocument(childDoc)) + if (mDocument->AppendChildDocument(childDoc)) { + if (XRE_GetProcessType() != GeckoProcessType_Default) { + DocAccessibleChild* ipcDoc = new DocAccessibleChild(childDoc); + childDoc->SetIPCDoc(ipcDoc); + auto contentChild = dom::ContentChild::GetSingleton(); + DocAccessibleChild* parentIPCDoc = mDocument->IPCDoc(); + uint64_t id = reinterpret_cast(outerDocAcc->UniqueID()); + contentChild->SendPDocAccessibleConstructor(ipcDoc, parentIPCDoc, + id); + } + continue; + } outerDocAcc->RemoveChild(childDoc); } diff --git a/accessible/base/Platform.h b/accessible/base/Platform.h index fa56963fdbed..41954be440d8 100644 --- a/accessible/base/Platform.h +++ b/accessible/base/Platform.h @@ -7,6 +7,8 @@ namespace mozilla { namespace a11y { +class ProxyAccessible; + enum EPlatformDisabledState { ePlatformIsForceEnabled = -1, ePlatformIsEnabled = 0, @@ -47,6 +49,17 @@ void PlatformInit(); */ void PlatformShutdown(); +/** + * called when a new ProxyAccessible is created, so the platform may setup a + * wrapper for it, or take other action. + */ +void ProxyCreated(ProxyAccessible*); + +/** + * Called just before a ProxyAccessible is destroyed so its wrapper can be + * disposed of and other action taken. + */ +void ProxyDestroyed(ProxyAccessible*); } // namespace a11y } // namespace mozilla diff --git a/accessible/base/Role.h b/accessible/base/Role.h index 2cd378d64482..0d5746f3dc13 100644 --- a/accessible/base/Role.h +++ b/accessible/base/Role.h @@ -783,7 +783,9 @@ enum Role { /** * Represent a keyboard or keypad key (ARIA role "key"). */ - KEY = 129 + KEY = 129, + + LAST_ROLE = KEY }; } // namespace role diff --git a/accessible/base/moz.build b/accessible/base/moz.build index e31cf3e5c4c3..0dad363cc445 100644 --- a/accessible/base/moz.build +++ b/accessible/base/moz.build @@ -60,6 +60,7 @@ if CONFIG['A11Y_LOG']: LOCAL_INCLUDES += [ '/accessible/generic', '/accessible/html', + '/accessible/ipc', '/accessible/xpcom', '/accessible/xul', '/dom/xbl', @@ -93,3 +94,5 @@ FINAL_LIBRARY = 'xul' if CONFIG['MOZ_ENABLE_GTK']: CXXFLAGS += CONFIG['MOZ_CAIRO_CFLAGS'] + +include('/ipc/chromium/chromium-config.mozbuild') diff --git a/accessible/generic/DocAccessible.cpp b/accessible/generic/DocAccessible.cpp index 8e1a02cfe52f..369700260b2a 100644 --- a/accessible/generic/DocAccessible.cpp +++ b/accessible/generic/DocAccessible.cpp @@ -6,6 +6,7 @@ #include "Accessible-inl.h" #include "AccIterator.h" #include "DocAccessible-inl.h" +#include "DocAccessibleChild.h" #include "HTMLImageMapAccessible.h" #include "nsAccCache.h" #include "nsAccessiblePivot.h" @@ -83,7 +84,7 @@ DocAccessible:: mScrollPositionChangedTicks(0), mLoadState(eTreeConstructionPending), mDocFlags(0), mLoadEventType(0), mVirtualCursor(nullptr), - mPresShell(aPresShell) + mPresShell(aPresShell), mIPCDoc(nullptr) { mGenericTypes |= eDocument; mStateFlags |= eNotNodeMapEntry; @@ -473,6 +474,12 @@ DocAccessible::Shutdown() mChildDocuments.Clear(); + // XXX thinking about ordering? + if (XRE_GetProcessType() != GeckoProcessType_Default) { + DocAccessibleChild::Send__delete__(mIPCDoc); + MOZ_ASSERT(!mIPCDoc); + } + if (mVirtualCursor) { mVirtualCursor->RemoveObserver(this); mVirtualCursor = nullptr; @@ -1436,6 +1443,13 @@ DocAccessible::DoInitialUpdate() nsRefPtr reorderEvent = new AccReorderEvent(Parent()); ParentDocument()->FireDelayedEvent(reorderEvent); } + + uint32_t childCount = ChildCount(); + for (uint32_t i = 0; i < childCount; i++) { + Accessible* child = GetChildAt(i); + nsRefPtr event = new AccShowEvent(child, child->GetContent()); + FireDelayedEvent(event); + } } void diff --git a/accessible/generic/DocAccessible.h b/accessible/generic/DocAccessible.h index 4d7858c1cfa0..42b655e69a55 100644 --- a/accessible/generic/DocAccessible.h +++ b/accessible/generic/DocAccessible.h @@ -33,6 +33,7 @@ namespace a11y { class DocManager; class NotificationController; +class DocAccessibleChild; class RelatedAccIterator; template class TNotification; @@ -519,6 +520,20 @@ protected: */ bool IsLoadEventTarget() const; + /** + * If this document is in a content process return the object responsible for + * communicating with the main process for it. + */ + DocAccessibleChild* IPCDoc() const { return mIPCDoc; } + + /* + * Set the object responsible for communicating with the main process on + * behalf of this document. + */ + void SetIPCDoc(DocAccessibleChild* aIPCDoc) { mIPCDoc = aIPCDoc; } + + friend class DocAccessibleChild; + /** * Used to fire scrolling end event after page scroll. * @@ -642,6 +657,9 @@ protected: private: nsIPresShell* mPresShell; + + // Exclusively owned by IPDL so don't manually delete it! + DocAccessibleChild* mIPCDoc; }; inline DocAccessible* diff --git a/accessible/generic/moz.build b/accessible/generic/moz.build index 236715bda01d..3b393c650fb6 100644 --- a/accessible/generic/moz.build +++ b/accessible/generic/moz.build @@ -28,6 +28,7 @@ UNIFIED_SOURCES += [ LOCAL_INCLUDES += [ '/accessible/base', '/accessible/html', + '/accessible/ipc', '/accessible/xpcom', '/accessible/xul', '/content/base/src', @@ -54,3 +55,5 @@ else: ] FINAL_LIBRARY = 'xul' + +include('/ipc/chromium/chromium-config.mozbuild') diff --git a/accessible/ipc/DocAccessibleChild.cpp b/accessible/ipc/DocAccessibleChild.cpp new file mode 100644 index 000000000000..5070b1fe9739 --- /dev/null +++ b/accessible/ipc/DocAccessibleChild.cpp @@ -0,0 +1,40 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "DocAccessibleChild.h" + +#include "Accessible-inl.h" + +namespace mozilla { +namespace a11y { + +void +SerializeTree(Accessible* aRoot, nsTArray& aTree) +{ + uint64_t id = reinterpret_cast(aRoot->UniqueID()); + uint32_t role = aRoot->Role(); + uint32_t childCount = aRoot->ChildCount(); + + nsString name; + aRoot->Name(name); + aTree.AppendElement(AccessibleData(id, role, childCount, name)); + for (uint32_t i = 0; i < childCount; i++) + SerializeTree(aRoot->GetChildAt(i), aTree); +} + +void +DocAccessibleChild::ShowEvent(AccShowEvent* aShowEvent) +{ + Accessible* parent = aShowEvent->Parent(); + uint64_t parentID = parent->IsDoc() ? 0 : reinterpret_cast(parent->UniqueID()); + uint32_t idxInParent = aShowEvent->GetAccessible()->IndexInParent(); + nsTArray shownTree; + ShowEventData data(parentID, idxInParent, shownTree); + SerializeTree(aShowEvent->GetAccessible(), data.NewTree()); + SendShowEvent(data); +} +} +} diff --git a/accessible/ipc/DocAccessibleChild.h b/accessible/ipc/DocAccessibleChild.h new file mode 100644 index 000000000000..50fdcc7cfd1f --- /dev/null +++ b/accessible/ipc/DocAccessibleChild.h @@ -0,0 +1,43 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_a11y_DocAccessibleChild_h +#define mozilla_a11y_DocAccessibleChild_h + +#include "mozilla/a11y/DocAccessible.h" +#include "mozilla/a11y/PDocAccessibleChild.h" +#include "nsISupportsImpl.h" + +namespace mozilla { +namespace a11y { +class AccShowEvent; + + /* + * These objects handle content side communication for an accessible document, + * and their lifetime is the same as the document they represent. + */ +class DocAccessibleChild : public PDocAccessibleChild +{ +public: + DocAccessibleChild(DocAccessible* aDoc) : + mDoc(aDoc) + { MOZ_COUNT_CTOR(DocAccessibleChild); } + ~DocAccessibleChild() + { + mDoc->SetIPCDoc(nullptr); + MOZ_COUNT_DTOR(DocAccessibleChild); + } + + void ShowEvent(AccShowEvent* aShowEvent); + +private: + DocAccessible* mDoc; +}; + +} +} + +#endif diff --git a/accessible/ipc/DocAccessibleParent.cpp b/accessible/ipc/DocAccessibleParent.cpp new file mode 100644 index 000000000000..0a60f6ddbee2 --- /dev/null +++ b/accessible/ipc/DocAccessibleParent.cpp @@ -0,0 +1,112 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "DocAccessibleParent.h" +#include "nsAutoPtr.h" +#include "mozilla/a11y/Platform.h" + +namespace mozilla { +namespace a11y { + +bool +DocAccessibleParent::RecvShowEvent(const ShowEventData& aData) +{ + if (aData.NewTree().IsEmpty()) { + NS_ERROR("no children being added"); + return false; + } + + ProxyAccessible* parent = nullptr; + if (aData.ID()) { + ProxyEntry* e = mAccessibles.GetEntry(aData.ID()); + if (e) + parent = e->mProxy; + } else { + parent = this; + } + + // XXX This should really never happen, but sometimes we fail to fire the + // required show events. + if (!parent) { + NS_ERROR("adding child to unknown accessible"); + return false; + } + + uint32_t newChildIdx = aData.Idx(); + if (newChildIdx > parent->ChildrenCount()) { + NS_ERROR("invalid index to add child at"); + return false; + } + + uint32_t consumed = AddSubtree(parent, aData.NewTree(), 0, newChildIdx); + MOZ_ASSERT(consumed == aData.NewTree().Length()); + for (uint32_t i = 0; i < consumed; i++) { + uint64_t id = aData.NewTree()[i].ID(); + MOZ_ASSERT(mAccessibles.GetEntry(id)); + } + + return consumed; +} + +uint32_t +DocAccessibleParent::AddSubtree(ProxyAccessible* aParent, + const nsTArray& aNewTree, + uint32_t aIdx, uint32_t aIdxInParent) +{ + if (aNewTree.Length() <= aIdx) { + NS_ERROR("bad index in serialized tree!"); + return 0; + } + + const AccessibleData& newChild = aNewTree[aIdx]; + if (newChild.Role() > roles::LAST_ROLE) { + NS_ERROR("invalid role"); + return 0; + } + + auto role = static_cast(newChild.Role()); + ProxyAccessible* newProxy = + new ProxyAccessible(newChild.ID(), aParent, this, role, newChild.Name()); + aParent->AddChildAt(aIdxInParent, newProxy); + mAccessibles.PutEntry(newChild.ID())->mProxy = newProxy; + ProxyCreated(newProxy); + + uint32_t accessibles = 1; + uint32_t kids = newChild.ChildrenCount(); + for (uint32_t i = 0; i < kids; i++) { + uint32_t consumed = AddSubtree(newProxy, aNewTree, aIdx + accessibles, i); + if (!consumed) + return 0; + + accessibles += consumed; + } + + MOZ_ASSERT(newProxy->ChildrenCount() == kids); + + return accessibles; +} + +bool +DocAccessibleParent::RecvHideEvent(const uint64_t& aRootID) +{ + ProxyEntry* rootEntry = mAccessibles.GetEntry(aRootID); + if (!rootEntry) { + NS_ERROR("invalid root being removed!"); + return true; + } + + ProxyAccessible* root = rootEntry->mProxy; + if (!root) { + NS_ERROR("invalid root being removed!"); + return true; + } + + root->Shutdown(); + + return true; +} +} +} diff --git a/accessible/ipc/DocAccessibleParent.h b/accessible/ipc/DocAccessibleParent.h new file mode 100644 index 000000000000..b92a5e0ac127 --- /dev/null +++ b/accessible/ipc/DocAccessibleParent.h @@ -0,0 +1,141 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_a11y_DocAccessibleParent_h +#define mozilla_a11y_DocAccessibleParent_h + +#include "nsAccessibilityService.h" +#include "ProxyAccessible.h" +#include "mozilla/a11y/PDocAccessibleParent.h" +#include "nsClassHashtable.h" +#include "nsHashKeys.h" +#include "nsISupportsImpl.h" + +namespace mozilla { +namespace a11y { + +/* + * These objects live in the main process and comunicate with and represent + * an accessible document in a content process. + */ +class DocAccessibleParent : public ProxyAccessible, + public PDocAccessibleParent +{ +public: + DocAccessibleParent() : + mParentDoc(nullptr) + { MOZ_COUNT_CTOR_INHERITED(DocAccessibleParent, ProxyAccessible); } + ~DocAccessibleParent() + { + MOZ_COUNT_DTOR_INHERITED(DocAccessibleParent, ProxyAccessible); + MOZ_ASSERT(mChildDocs.Length() == 0); + MOZ_ASSERT(!mParentDoc); + } + + /* + * Called when a message from a document in a child process notifies the main + * process it is firing an event. + */ + virtual bool RecvEvent(const uint32_t& aType) MOZ_OVERRIDE + { + return true; + } + + virtual bool RecvShowEvent(const ShowEventData& aData) MOZ_OVERRIDE; + virtual bool RecvHideEvent(const uint64_t& aRootID) MOZ_OVERRIDE; + + virtual void ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE + { + MOZ_ASSERT(mChildDocs.IsEmpty(), + "why wheren't the child docs destroyed already?"); + mParentDoc ? mParentDoc->RemoveChildDoc(this) + : GetAccService()->RemoteDocShutdown(this); + } + + /* + * Return the main processes representation of the parent document (if any) + * of the document this object represents. + */ + DocAccessibleParent* Parent() const { return mParentDoc; } + + /* + * Called when a document in a content process notifies the main process of a + * new child document. + */ + bool AddChildDoc(DocAccessibleParent* aChildDoc, uint64_t aParentID) + { + ProxyAccessible* outerDoc = mAccessibles.GetEntry(aParentID)->mProxy; + if (!outerDoc) + return false; + + aChildDoc->mParent = outerDoc; + outerDoc->SetChildDoc(aChildDoc); + mChildDocs.AppendElement(aChildDoc); + aChildDoc->mParentDoc = this; + return true; + } + + /* + * Called when the document in the content process this object represents + * notifies the main process a child document has been removed. + */ + void RemoveChildDoc(DocAccessibleParent* aChildDoc) + { + aChildDoc->mParent->SetChildDoc(nullptr); + mChildDocs.RemoveElement(aChildDoc); + aChildDoc->mParentDoc = nullptr; + MOZ_ASSERT(aChildDoc->mChildDocs.Length() == 0); + } + + void RemoveAccessible(ProxyAccessible* aAccessible) + { + MOZ_ASSERT(mAccessibles.GetEntry(aAccessible->ID())); + mAccessibles.RemoveEntry(aAccessible->ID()); + } + +private: + + class ProxyEntry : public PLDHashEntryHdr + { + public: + ProxyEntry(const void*) : mProxy(nullptr) {} + ProxyEntry(ProxyEntry&& aOther) : + mProxy(aOther.mProxy) { aOther.mProxy = nullptr; } + ~ProxyEntry() { delete mProxy; } + + typedef uint64_t KeyType; + typedef const void* KeyTypePointer; + + bool KeyEquals(const void* aKey) const + { return mProxy->ID() == (uint64_t)aKey; } + + static const void* KeyToPointer(uint64_t aKey) { return (void*)aKey; } + + static PLDHashNumber HashKey(const void* aKey) { return (uint64_t)aKey; } + + enum { ALLOW_MEMMOVE = true }; + + ProxyAccessible* mProxy; + }; + + uint32_t AddSubtree(ProxyAccessible* aParent, + const nsTArray& aNewTree, uint32_t aIdx, + uint32_t aIdxInParent); + + nsTArray mChildDocs; + DocAccessibleParent* mParentDoc; + + /* + * Conceptually this is a map from IDs to proxies, but we store the ID in the + * proxy object so we can't use a real map. + */ + nsTHashtable mAccessibles; +}; + +} +} + +#endif diff --git a/accessible/ipc/PDocAccessible.ipdl b/accessible/ipc/PDocAccessible.ipdl new file mode 100644 index 000000000000..b3328b31b680 --- /dev/null +++ b/accessible/ipc/PDocAccessible.ipdl @@ -0,0 +1,44 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +include protocol PContent; + +namespace mozilla { +namespace a11y { + +struct AccessibleData +{ + uint64_t ID; + uint32_t Role; + uint32_t ChildrenCount; + nsString Name; +}; + +struct ShowEventData +{ + uint64_t ID; + uint32_t Idx; + AccessibleData[] NewTree; +}; + +protocol PDocAccessible +{ + manager PContent; + +parent: + __delete__(); + + /* + * Notify the parent process the document in the child process is firing an + * event. + */ + Event(uint32_t type); + ShowEvent(ShowEventData data); + HideEvent(uint64_t aRootID); +}; + +} +} diff --git a/accessible/ipc/ProxyAccessible.cpp b/accessible/ipc/ProxyAccessible.cpp new file mode 100644 index 000000000000..5af1aa41d8fb --- /dev/null +++ b/accessible/ipc/ProxyAccessible.cpp @@ -0,0 +1,42 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "ProxyAccessible.h" +#include "DocAccessibleParent.h" +#include "mozilla/a11y/Platform.h" + +namespace mozilla { +namespace a11y { + +void +ProxyAccessible::Shutdown() +{ + MOZ_ASSERT(!mOuterDoc); + + uint32_t childCount = mChildren.Length(); + for (uint32_t idx = 0; idx < childCount; idx++) + mChildren[idx]->Shutdown(); + + mChildren.Clear(); + ProxyDestroyed(this); + mDoc->RemoveAccessible(this); +} + +void +ProxyAccessible::SetChildDoc(DocAccessibleParent* aParent) +{ + if (aParent) { + MOZ_ASSERT(mChildren.IsEmpty()); + mChildren.AppendElement(aParent); + mOuterDoc = true; + } else { + MOZ_ASSERT(mChildren.Length() == 1); + mChildren.Clear(); + mOuterDoc = false; + } +} +} +} diff --git a/accessible/ipc/ProxyAccessible.h b/accessible/ipc/ProxyAccessible.h new file mode 100644 index 000000000000..39fd379d1e24 --- /dev/null +++ b/accessible/ipc/ProxyAccessible.h @@ -0,0 +1,77 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_a11y_ProxyAccessible_h +#define mozilla_a11y_ProxyAccessible_h + +#include "mozilla/a11y/Role.h" +#include "nsString.h" +#include "nsTArray.h" + +namespace mozilla { +namespace a11y { + +class DocAccessibleParent; + +class ProxyAccessible +{ +public: + + ProxyAccessible(uint64_t aID, ProxyAccessible* aParent, + DocAccessibleParent* aDoc, role aRole, + const nsString& aName) : + mParent(aParent), mDoc(aDoc), mID(aID), mRole(aRole), mOuterDoc(false), mName(aName) + { + MOZ_COUNT_CTOR(ProxyAccessible); + } + ~ProxyAccessible() { MOZ_COUNT_DTOR(ProxyAccessible); } + + void AddChildAt(uint32_t aIdx, ProxyAccessible* aChild) + { mChildren.InsertElementAt(aIdx, aChild); } + + uint32_t ChildrenCount() const { return mChildren.Length(); } + + void Shutdown(); + + void SetChildDoc(DocAccessibleParent*); + + /** + * Get the role of the accessible we're proxying. + */ + role Role() const { return mRole; } + + /** + * Allow the platform to store a pointers worth of data on us. + */ + uintptr_t GetWrapper() const { return mWrapper; } + void SetWrapper(uintptr_t aWrapper) { mWrapper = aWrapper; } + + /* + * Return the ID of the accessible being proxied. + */ + uint64_t ID() const { return mID; } + +protected: + ProxyAccessible() : + mParent(nullptr), mDoc(nullptr) { MOZ_COUNT_CTOR(ProxyAccessible); } + +protected: + ProxyAccessible* mParent; + +private: + nsTArray mChildren; + DocAccessibleParent* mDoc; + uintptr_t mWrapper; + uint64_t mID; + role mRole : 31; + bool mOuterDoc : 1; + nsString mName; +}; + +} +} + +#endif diff --git a/accessible/ipc/moz.build b/accessible/ipc/moz.build new file mode 100644 index 000000000000..64a45e9980eb --- /dev/null +++ b/accessible/ipc/moz.build @@ -0,0 +1,28 @@ +# -*- Mode: python; c-basic-offset: 4; 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/. + +IPDL_SOURCES += ['PDocAccessible.ipdl'] + +EXPORTS.mozilla.a11y += [ + 'DocAccessibleChild.h', + 'DocAccessibleParent.h', + 'ProxyAccessible.h' +] + +SOURCES += [ + 'DocAccessibleChild.cpp', + 'DocAccessibleParent.cpp', + 'ProxyAccessible.cpp' +] + +LOCAL_INCLUDES += [ + '../base', + '../generic', + ] + +FINAL_LIBRARY = 'xul' + +include('/ipc/chromium/chromium-config.mozbuild') diff --git a/accessible/mac/Platform.mm b/accessible/mac/Platform.mm index 98bbac02a9b9..4d82f2c5e836 100644 --- a/accessible/mac/Platform.mm +++ b/accessible/mac/Platform.mm @@ -33,6 +33,15 @@ PlatformShutdown() { } +void +ProxyCreated(ProxyAccessible*) +{ +} + +void +ProxyDestroyed(ProxyAccessible*) +{ +} } } diff --git a/accessible/moz.build b/accessible/moz.build index 44f345780018..993d2202937f 100644 --- a/accessible/moz.build +++ b/accessible/moz.build @@ -15,7 +15,7 @@ elif toolkit == 'cocoa': else: DIRS += ['other'] -DIRS += ['base', 'generic', 'html', 'interfaces', 'jsat', 'xpcom'] +DIRS += ['base', 'generic', 'html', 'interfaces', 'ipc', 'jsat', 'xpcom'] if CONFIG['MOZ_XUL']: DIRS += ['xul'] diff --git a/accessible/other/Platform.cpp b/accessible/other/Platform.cpp index 87c9fc6071f8..768f687c738c 100644 --- a/accessible/other/Platform.cpp +++ b/accessible/other/Platform.cpp @@ -18,3 +18,13 @@ void a11y::PlatformShutdown() { } + +void +a11y::ProxyCreated(ProxyAccessible*) +{ +} + +void +a11y::ProxyDestroyed(ProxyAccessible*) +{ +} diff --git a/accessible/windows/msaa/Platform.cpp b/accessible/windows/msaa/Platform.cpp index 263eb1a4d130..0ed1f5ffc073 100644 --- a/accessible/windows/msaa/Platform.cpp +++ b/accessible/windows/msaa/Platform.cpp @@ -34,3 +34,12 @@ a11y::PlatformShutdown() nsWinUtils::ShutdownWindowEmulation(); } +void +a11y::ProxyCreated(ProxyAccessible*) +{ +} + +void +a11y::ProxyDestroyed(ProxyAccessible*) +{ +} diff --git a/dom/ipc/ContentChild.cpp b/dom/ipc/ContentChild.cpp index 0ee98f5b1a61..a48c741c6ee3 100644 --- a/dom/ipc/ContentChild.cpp +++ b/dom/ipc/ContentChild.cpp @@ -19,6 +19,7 @@ #include "TabChild.h" #include "mozilla/Attributes.h" +#include "mozilla/a11y/DocAccessibleChild.h" #include "mozilla/Preferences.h" #include "mozilla/dom/ContentBridgeChild.h" #include "mozilla/dom/ContentBridgeParent.h" @@ -705,6 +706,20 @@ ContentChild::InitXPCOM() InitOnContentProcessCreated(); } +a11y::PDocAccessibleChild* +ContentChild::AllocPDocAccessibleChild(PDocAccessibleChild*, const uint64_t&) +{ + MOZ_ASSERT(false, "should never call this!"); + return nullptr; +} + +bool +ContentChild::DeallocPDocAccessibleChild(a11y::PDocAccessibleChild* aChild) +{ + delete static_cast(aChild); + return true; +} + PMemoryReportRequestChild* ContentChild::AllocPMemoryReportRequestChild(const uint32_t& aGeneration, const bool &aAnonymize, diff --git a/dom/ipc/ContentChild.h b/dom/ipc/ContentChild.h index 216b8c06f086..5f30146a01f5 100644 --- a/dom/ipc/ContentChild.h +++ b/dom/ipc/ContentChild.h @@ -376,6 +376,8 @@ public: const uint64_t& aID, const bool& aIsForApp, const bool& aIsForBrowser) MOZ_OVERRIDE; + virtual PDocAccessibleChild* AllocPDocAccessibleChild(PDocAccessibleChild*, const uint64_t&) MOZ_OVERRIDE; + virtual bool DeallocPDocAccessibleChild(PDocAccessibleChild*) MOZ_OVERRIDE; private: virtual void ActorDestroy(ActorDestroyReason why) MOZ_OVERRIDE; diff --git a/dom/ipc/ContentParent.cpp b/dom/ipc/ContentParent.cpp index ec8bd6ee23e9..954e33de0271 100755 --- a/dom/ipc/ContentParent.cpp +++ b/dom/ipc/ContentParent.cpp @@ -30,6 +30,8 @@ #include "CrashReporterParent.h" #include "IHistory.h" #include "mozIApplication.h" +#include "mozilla/a11y/DocAccessibleParent.h" +#include "nsAccessibilityService.h" #include "mozilla/ClearOnShutdown.h" #include "mozilla/dom/asmjscache/AsmJSCache.h" #include "mozilla/dom/Element.h" @@ -2695,6 +2697,34 @@ ContentParent::Observe(nsISupports* aSubject, return NS_OK; } + a11y::PDocAccessibleParent* +ContentParent::AllocPDocAccessibleParent(PDocAccessibleParent* aParent, const uint64_t&) +{ + return new a11y::DocAccessibleParent(); +} + +bool +ContentParent::DeallocPDocAccessibleParent(PDocAccessibleParent* aParent) +{ + delete static_cast(aParent); + return true; +} + +bool +ContentParent::RecvPDocAccessibleConstructor(PDocAccessibleParent* aDoc, PDocAccessibleParent* aParentDoc, const uint64_t& aParentID) +{ + auto doc = static_cast(aDoc); + if (aParentDoc) { + MOZ_ASSERT(aParentID); + auto parentDoc = static_cast(aParentDoc); + return parentDoc->AddChildDoc(doc, aParentID); + } else { + MOZ_ASSERT(!aParentID); + GetAccService()->RemoteDocAdded(doc); + } + return true; +} + PCompositorParent* ContentParent::AllocPCompositorParent(mozilla::ipc::Transport* aTransport, base::ProcessId aOtherProcess) diff --git a/dom/ipc/ContentParent.h b/dom/ipc/ContentParent.h index 833380f059e3..e287e81f2568 100644 --- a/dom/ipc/ContentParent.h +++ b/dom/ipc/ContentParent.h @@ -662,6 +662,11 @@ private: int32_t* aSliceRefCnt, bool* aResult) MOZ_OVERRIDE; + virtual PDocAccessibleParent* AllocPDocAccessibleParent(PDocAccessibleParent*, const uint64_t&) MOZ_OVERRIDE; + virtual bool DeallocPDocAccessibleParent(PDocAccessibleParent*) MOZ_OVERRIDE; + virtual bool RecvPDocAccessibleConstructor(PDocAccessibleParent* aDoc, + PDocAccessibleParent* aParentDoc, const uint64_t& aParentID) MOZ_OVERRIDE; + // If you add strong pointers to cycle collected objects here, be sure to // release these objects in ShutDownProcess. See the comment there for more // details. diff --git a/dom/ipc/PContent.ipdl b/dom/ipc/PContent.ipdl index e664b634dd80..e60a287e5ee3 100644 --- a/dom/ipc/PContent.ipdl +++ b/dom/ipc/PContent.ipdl @@ -13,6 +13,7 @@ include protocol PCompositor; include protocol PContentBridge; include protocol PCycleCollectWithLogs; include protocol PCrashReporter; +include protocol PDocAccessible; include protocol PExternalHelperApp; include protocol PDeviceStorageRequest; include protocol PFileDescriptorSet; @@ -325,6 +326,7 @@ intr protocol PContent manages PBrowser; manages PCrashReporter; manages PCycleCollectWithLogs; + manages PDocAccessible; manages PDeviceStorageRequest; manages PFileSystemRequest; manages PExternalHelperApp; @@ -481,6 +483,14 @@ child: OnAppThemeChanged(); parent: + /** + * Tell the parent process a new accessible document has been created. + * aParentDoc is the accessible document it was created in if any, and + * aParentAcc is the id of the accessible in that document the new document + * is a child of. + */ + PDocAccessible(nullable PDocAccessible aParentDoc, uint64_t aParentAcc); + /** * Tell the content process some attributes of itself. This is * among the first information queried by content processes after From 2ef4ef49ffb380dfb5b7fd68e05f06c5f7dd0d6d Mon Sep 17 00:00:00 2001 From: Trevor Saunders Date: Mon, 29 Sep 2014 14:38:09 -0400 Subject: [PATCH 08/82] no bug - fix nullptr to bool conversion in TypedArrayCommon.h r=themaid --- js/src/vm/TypedArrayCommon.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/src/vm/TypedArrayCommon.h b/js/src/vm/TypedArrayCommon.h index cc7b688caec0..7d264af43ff6 100644 --- a/js/src/vm/TypedArrayCommon.h +++ b/js/src/vm/TypedArrayCommon.h @@ -511,7 +511,7 @@ class TypedArrayMethods } if (!AnyTypedArray::ensureHasBuffer(cx, tarray)) - return nullptr; + return false; Rooted bufobj(cx, tarray->buffer()); MOZ_ASSERT(bufobj); From a0756ffd6817cc4a855ac51736e3f93d3eccdb40 Mon Sep 17 00:00:00 2001 From: Eric Rahm Date: Mon, 29 Sep 2014 11:46:29 -0700 Subject: [PATCH 09/82] Bug 1068981 - Acquire monitor before calling WakeUp. r=roc --- content/media/MediaStreamGraph.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/content/media/MediaStreamGraph.cpp b/content/media/MediaStreamGraph.cpp index d388fa235152..fc7f1e6577bb 100644 --- a/content/media/MediaStreamGraph.cpp +++ b/content/media/MediaStreamGraph.cpp @@ -2859,8 +2859,11 @@ MediaStreamGraphImpl::CollectReports(nsIHandleReportCallback* aHandleReport, MonitorAutoLock memoryReportLock(mMemoryReportMonitor); mNeedsMemoryReport = true; - // Wake up the MSG thread. - CurrentDriver()->WakeUp(); + { + // Wake up the MSG thread. + MonitorAutoLock monitorLock(mMonitor); + CurrentDriver()->WakeUp(); + } if (mLifecycleState >= LIFECYCLE_WAITING_FOR_THREAD_SHUTDOWN) { // Shutting down, nothing to report. From 57cbb9dd21a7f62d66d66dba62f46b9faba6c34d Mon Sep 17 00:00:00 2001 From: William Lachance Date: Mon, 29 Sep 2014 14:49:49 -0400 Subject: [PATCH 10/82] Bug 1073698 - mozversion should accept binary_path without .exe extension on windows;r=davehunt --- testing/mozbase/mozversion/mozversion/mozversion.py | 6 +++++- testing/mozbase/mozversion/setup.py | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/testing/mozbase/mozversion/mozversion/mozversion.py b/testing/mozbase/mozversion/mozversion/mozversion.py index d24fa9c1c091..64a1e7ee13aa 100644 --- a/testing/mozbase/mozversion/mozversion/mozversion.py +++ b/testing/mozbase/mozversion/mozversion/mozversion.py @@ -93,7 +93,11 @@ class LocalVersion(Version): if binary: if not os.path.exists(binary): - raise IOError('Binary path does not exist: %s' % binary) + # on windows we should also try extending the binary path with + # .exe and see if that's valid + if (sys.platform == 'win32' and not binary.endswith('.exe') and + not os.path.exists(binary + '.exe')): + raise IOError('Binary path does not exist: %s' % binary) path = find_location(os.path.dirname(os.path.realpath(binary))) else: path = find_location(os.getcwd()) diff --git a/testing/mozbase/mozversion/setup.py b/testing/mozbase/mozversion/setup.py index c3e4d65852a9..b12b64bd4677 100644 --- a/testing/mozbase/mozversion/setup.py +++ b/testing/mozbase/mozversion/setup.py @@ -4,7 +4,7 @@ from setuptools import setup -PACKAGE_VERSION = '0.7' +PACKAGE_VERSION = '0.8' dependencies = ['mozdevice >= 0.29', 'mozfile >= 1.0', From 9aa0d3ba405e5be79653ac031245c2fbdfc13679 Mon Sep 17 00:00:00 2001 From: Eric Rahm Date: Mon, 29 Sep 2014 11:51:20 -0700 Subject: [PATCH 11/82] Bug 1073712 - Include prprf.h in WebMReader. r=cpearce --- content/media/webm/WebMReader.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/content/media/webm/WebMReader.cpp b/content/media/webm/WebMReader.cpp index f2c0cf847062..0ae1402d038f 100644 --- a/content/media/webm/WebMReader.cpp +++ b/content/media/webm/WebMReader.cpp @@ -39,6 +39,7 @@ using namespace layers; //#define SEEK_LOGGING #ifdef PR_LOGGING +#include "prprf.h" extern PRLogModuleInfo* gMediaDecoderLog; PRLogModuleInfo* gNesteggLog; #define LOG(type, msg) PR_LOG(gMediaDecoderLog, type, msg) From 3f4c3e3d5ef45d88ac31f0965f38c9ff973bcd04 Mon Sep 17 00:00:00 2001 From: Jan de Mooij Date: Mon, 29 Sep 2014 20:52:55 +0200 Subject: [PATCH 12/82] Bug 1073652 - Baseline-compile JSOP_THROWING. r=bhackett --- js/src/jit-test/tests/ion/throw.js | 15 +++++++++++++++ js/src/jit/BaselineCompiler.cpp | 15 +++++++++++++++ js/src/jit/BaselineCompiler.h | 1 + js/src/vm/Interpreter.cpp | 16 +++++++++++++--- js/src/vm/Interpreter.h | 3 +++ 5 files changed, 47 insertions(+), 3 deletions(-) diff --git a/js/src/jit-test/tests/ion/throw.js b/js/src/jit-test/tests/ion/throw.js index bf0b5c731570..808345f882ae 100644 --- a/js/src/jit-test/tests/ion/throw.js +++ b/js/src/jit-test/tests/ion/throw.js @@ -94,3 +94,18 @@ for (var i = 0; i < 100; i++) { assertEq(getException(test4_4), count-1); } assertEq(count, 4500); + +function test5() { + var res = 0; + for (var i=0; i<40; i++) { + try { + throw i; + } catch (e if e % 2) { + res += e; + } catch (e) { + res += e * 3; + } + } + return res; +} +assertEq(test5(), 1540); diff --git a/js/src/jit/BaselineCompiler.cpp b/js/src/jit/BaselineCompiler.cpp index 9e2cd15c1a82..966d2d812732 100644 --- a/js/src/jit/BaselineCompiler.cpp +++ b/js/src/jit/BaselineCompiler.cpp @@ -2776,6 +2776,21 @@ BaselineCompiler::emit_JSOP_THROW() return callVM(ThrowInfo); } +typedef bool (*ThrowingFn)(JSContext *, HandleValue); +static const VMFunction ThrowingInfo = FunctionInfo(js::ThrowingOperation); + +bool +BaselineCompiler::emit_JSOP_THROWING() +{ + // Keep value to throw in R0. + frame.popRegsAndSync(1); + + prepareVMCall(); + pushArg(R0); + + return callVM(ThrowingInfo); +} + bool BaselineCompiler::emit_JSOP_TRY() { diff --git a/js/src/jit/BaselineCompiler.h b/js/src/jit/BaselineCompiler.h index b4e1f11a9aa9..7304308d35d9 100644 --- a/js/src/jit/BaselineCompiler.h +++ b/js/src/jit/BaselineCompiler.h @@ -154,6 +154,7 @@ namespace jit { _(JSOP_TYPEOFEXPR) \ _(JSOP_SETCALL) \ _(JSOP_THROW) \ + _(JSOP_THROWING) \ _(JSOP_TRY) \ _(JSOP_FINALLY) \ _(JSOP_GOSUB) \ diff --git a/js/src/vm/Interpreter.cpp b/js/src/vm/Interpreter.cpp index d38ea18ee282..04deb6752390 100644 --- a/js/src/vm/Interpreter.cpp +++ b/js/src/vm/Interpreter.cpp @@ -3290,10 +3290,9 @@ END_CASE(JSOP_FINALLY) CASE(JSOP_THROWING) { - JS_ASSERT(!cx->isExceptionPending()); - Value v; + RootedValue &v = rootValue0; POP_COPY_TO(v); - cx->setPendingException(v); + MOZ_ALWAYS_TRUE(ThrowingOperation(cx, v)); } END_CASE(JSOP_THROWING) @@ -3503,6 +3502,17 @@ js::Throw(JSContext *cx, HandleValue v) return false; } +bool +js::ThrowingOperation(JSContext *cx, HandleValue v) +{ + // Like js::Throw, but returns |true| instead of |false| to continue + // execution instead of calling the (JIT) exception handler. + + MOZ_ASSERT(!cx->isExceptionPending()); + cx->setPendingException(v); + return true; +} + bool js::GetProperty(JSContext *cx, HandleValue v, HandlePropertyName name, MutableHandleValue vp) { diff --git a/js/src/vm/Interpreter.h b/js/src/vm/Interpreter.h index 9110adfdc629..6f80c53ea620 100644 --- a/js/src/vm/Interpreter.h +++ b/js/src/vm/Interpreter.h @@ -366,6 +366,9 @@ class TryNoteIter bool Throw(JSContext *cx, HandleValue v); +bool +ThrowingOperation(JSContext *cx, HandleValue v); + bool GetProperty(JSContext *cx, HandleValue value, HandlePropertyName name, MutableHandleValue vp); From 06a9724510eb7577c2c0291d9fb6a3b8384f02fd Mon Sep 17 00:00:00 2001 From: Jan de Mooij Date: Mon, 29 Sep 2014 20:52:57 +0200 Subject: [PATCH 13/82] Bug 909389 - Remove --ion-compile-try-catch shell flag. r=djvj --- js/src/jit/IonBuilder.cpp | 3 --- js/src/jit/JitOptions.cpp | 3 --- js/src/jit/JitOptions.h | 1 - js/src/shell/js.cpp | 4 ---- 4 files changed, 11 deletions(-) diff --git a/js/src/jit/IonBuilder.cpp b/js/src/jit/IonBuilder.cpp index 59a216041211..48880345043f 100644 --- a/js/src/jit/IonBuilder.cpp +++ b/js/src/jit/IonBuilder.cpp @@ -3846,9 +3846,6 @@ IonBuilder::jsop_try() { JS_ASSERT(JSOp(*pc) == JSOP_TRY); - if (!js_JitOptions.compileTryCatch) - return abort("Try-catch support disabled"); - // Try-finally is not yet supported. if (analysis().hasTryFinally()) return abort("Has try-finally"); diff --git a/js/src/jit/JitOptions.cpp b/js/src/jit/JitOptions.cpp index 9b3367ac8333..b7f435bfdedb 100644 --- a/js/src/jit/JitOptions.cpp +++ b/js/src/jit/JitOptions.cpp @@ -63,9 +63,6 @@ JitOptions::JitOptions() // RangeAnalysis results. SET_DEFAULT(checkRangeAnalysis, false); - // Whether Ion should compile try-catch statements. - SET_DEFAULT(compileTryCatch, true); - // Toggle whether eager scalar replacement is globally disabled. SET_DEFAULT(disableScalarReplacement, true); // experimental diff --git a/js/src/jit/JitOptions.h b/js/src/jit/JitOptions.h index 09ea09e694bf..9158dab9b7d6 100644 --- a/js/src/jit/JitOptions.h +++ b/js/src/jit/JitOptions.h @@ -32,7 +32,6 @@ struct JitOptions bool checkOsiPointRegisters; #endif bool checkRangeAnalysis; - bool compileTryCatch; bool disableScalarReplacement; bool disableGvn; bool disableLicm; diff --git a/js/src/shell/js.cpp b/js/src/shell/js.cpp index 2abd5957cbc1..4666209e62d4 100644 --- a/js/src/shell/js.cpp +++ b/js/src/shell/js.cpp @@ -5729,9 +5729,6 @@ SetRuntimeOptions(JSRuntime *rt, const OptionParser &op) if (op.getBoolOption("ion-eager")) jit::js_JitOptions.setEagerCompilation(); - if (op.getBoolOption("ion-compile-try-catch")) - jit::js_JitOptions.compileTryCatch = true; - bool offthreadCompilation = true; if (const char *str = op.getStringOption("ion-offthread-compile")) { if (strcmp(str, "off") == 0) @@ -5974,7 +5971,6 @@ main(int argc, char **argv, char **envp) " backtracking: Priority based backtracking register allocation\n" " stupid: Simple block local register allocation") || !op.addBoolOption('\0', "ion-eager", "Always ion-compile methods (implies --baseline-eager)") - || !op.addBoolOption('\0', "ion-compile-try-catch", "Ion-compile try-catch statements") || !op.addStringOption('\0', "ion-offthread-compile", "on/off", "Compile scripts off thread (default: on)") || !op.addStringOption('\0', "ion-parallel-compile", "on/off", From 784b5084678fbb7f6628611b0fcacf8b6855a6cb Mon Sep 17 00:00:00 2001 From: Jan de Mooij Date: Mon, 29 Sep 2014 20:53:00 +0200 Subject: [PATCH 14/82] Bug 1046751 - Assert the frontend does not leave pending exceptions. r=jorendorff --- js/src/frontend/BytecodeCompiler.cpp | 1 + js/src/frontend/Parser.cpp | 2 ++ 2 files changed, 3 insertions(+) diff --git a/js/src/frontend/BytecodeCompiler.cpp b/js/src/frontend/BytecodeCompiler.cpp index 3ba9c9d576a0..f1d703ef8b9d 100644 --- a/js/src/frontend/BytecodeCompiler.cpp +++ b/js/src/frontend/BytecodeCompiler.cpp @@ -457,6 +457,7 @@ frontend::CompileScript(ExclusiveContext *cx, LifoAlloc *alloc, HandleObject sco if (sct && !extraSct && !sct->complete()) return nullptr; + MOZ_ASSERT_IF(cx->isJSContext(), !cx->asJSContext()->isExceptionPending()); return script; } diff --git a/js/src/frontend/Parser.cpp b/js/src/frontend/Parser.cpp index dde253ad25ae..1453b8f8f640 100644 --- a/js/src/frontend/Parser.cpp +++ b/js/src/frontend/Parser.cpp @@ -2198,6 +2198,8 @@ Parser::functionArgsAndBody(ParseNode *pn, HandleFunction fun, if (parser->hadAbortedSyntaxParse()) { // Try again with a full parse. parser->clearAbortedSyntaxParse(); + MOZ_ASSERT_IF(parser->context->isJSContext(), + !parser->context->asJSContext()->isExceptionPending()); break; } return false; From bd8cb95c0cda776530540962a49e4a1795d9b0f6 Mon Sep 17 00:00:00 2001 From: William Lachance Date: Mon, 29 Sep 2014 14:55:18 -0400 Subject: [PATCH 15/82] Bug 1073698 - fix logic error in previous patch;r=davehunt --- testing/mozbase/mozversion/mozversion/mozversion.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/testing/mozbase/mozversion/mozversion/mozversion.py b/testing/mozbase/mozversion/mozversion/mozversion.py index 64a1e7ee13aa..2ffe5f80a65c 100644 --- a/testing/mozbase/mozversion/mozversion/mozversion.py +++ b/testing/mozbase/mozversion/mozversion/mozversion.py @@ -95,8 +95,9 @@ class LocalVersion(Version): if not os.path.exists(binary): # on windows we should also try extending the binary path with # .exe and see if that's valid - if (sys.platform == 'win32' and not binary.endswith('.exe') and - not os.path.exists(binary + '.exe')): + if (sys.platform != 'win32' or + (not binary.endswith('.exe') and + not os.path.exists(binary + '.exe'))): raise IOError('Binary path does not exist: %s' % binary) path = find_location(os.path.dirname(os.path.realpath(binary))) else: From 2a34d55fd77764217bde4621b40e1130cf144409 Mon Sep 17 00:00:00 2001 From: Ryan VanderMeulen Date: Mon, 29 Sep 2014 15:37:22 -0400 Subject: [PATCH 16/82] Backed out changesets 8be8d3dd116b and 6abcab74130a (bug 1073698) for checktest failures. --- testing/mozbase/mozversion/mozversion/mozversion.py | 7 +------ testing/mozbase/mozversion/setup.py | 2 +- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/testing/mozbase/mozversion/mozversion/mozversion.py b/testing/mozbase/mozversion/mozversion/mozversion.py index 2ffe5f80a65c..d24fa9c1c091 100644 --- a/testing/mozbase/mozversion/mozversion/mozversion.py +++ b/testing/mozbase/mozversion/mozversion/mozversion.py @@ -93,12 +93,7 @@ class LocalVersion(Version): if binary: if not os.path.exists(binary): - # on windows we should also try extending the binary path with - # .exe and see if that's valid - if (sys.platform != 'win32' or - (not binary.endswith('.exe') and - not os.path.exists(binary + '.exe'))): - raise IOError('Binary path does not exist: %s' % binary) + raise IOError('Binary path does not exist: %s' % binary) path = find_location(os.path.dirname(os.path.realpath(binary))) else: path = find_location(os.getcwd()) diff --git a/testing/mozbase/mozversion/setup.py b/testing/mozbase/mozversion/setup.py index b12b64bd4677..c3e4d65852a9 100644 --- a/testing/mozbase/mozversion/setup.py +++ b/testing/mozbase/mozversion/setup.py @@ -4,7 +4,7 @@ from setuptools import setup -PACKAGE_VERSION = '0.8' +PACKAGE_VERSION = '0.7' dependencies = ['mozdevice >= 0.29', 'mozfile >= 1.0', From 0c3606834abe00c2f563244b8dc2dab1f1783680 Mon Sep 17 00:00:00 2001 From: Kartikaya Gupta Date: Mon, 29 Sep 2014 15:32:11 -0400 Subject: [PATCH 17/82] Bug 1074030 - Copy the OS X overlay scrollbar fade parameters to B2G. r=fabrice --- b2g/app/b2g.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/b2g/app/b2g.js b/b2g/app/b2g.js index 4c7ffcfef89a..998054a207e3 100644 --- a/b2g/app/b2g.js +++ b/b2g/app/b2g.js @@ -677,6 +677,8 @@ pref("javascript.options.mem.gc_max_empty_chunk_count", 2); // Show/Hide scrollbars when active/inactive pref("ui.showHideScrollbars", 1); pref("ui.useOverlayScrollbars", 1); +pref("ui.scrollbarFadeBeginDelay", 450); +pref("ui.scrollbarFadeDuration", 200); // Enable the ProcessPriorityManager, and give processes with no visible // documents a 1s grace period before they're eligible to be marked as From 3f7c79fa9f2e1fb1472b1e346c7205249ef4e1ff Mon Sep 17 00:00:00 2001 From: Kartikaya Gupta Date: Mon, 29 Sep 2014 15:32:44 -0400 Subject: [PATCH 18/82] Bug 1071758 - Don't send two click events when doing a medium length tap. r=bnicholson --- mobile/android/base/gfx/JavaPanZoomController.java | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/mobile/android/base/gfx/JavaPanZoomController.java b/mobile/android/base/gfx/JavaPanZoomController.java index 0f977b9c7861..13c26d9e5024 100644 --- a/mobile/android/base/gfx/JavaPanZoomController.java +++ b/mobile/android/base/gfx/JavaPanZoomController.java @@ -1348,13 +1348,17 @@ class JavaPanZoomController GeckoAppShell.sendEventToGecko(e); } + private boolean waitForDoubleTap() { + return !mMediumPress && mTarget.getZoomConstraints().getAllowDoubleTapZoom(); + } + @Override public boolean onSingleTapUp(MotionEvent motionEvent) { // When double-tapping is allowed, we have to wait to see if this is // going to be a double-tap. // However, if mMediumPress is true then we know there will be no // double-tap so we treat this as a click. - if (mMediumPress || !mTarget.getZoomConstraints().getAllowDoubleTapZoom()) { + if (!waitForDoubleTap()) { sendPointToGecko("Gesture:SingleTap", motionEvent); } // return false because we still want to get the ACTION_UP event that triggers this @@ -1363,8 +1367,8 @@ class JavaPanZoomController @Override public boolean onSingleTapConfirmed(MotionEvent motionEvent) { - // When zooming is disabled, we handle this in onSingleTapUp. - if (mTarget.getZoomConstraints().getAllowDoubleTapZoom()) { + // In cases where we don't wait for double-tap, we handle this in onSingleTapUp. + if (waitForDoubleTap()) { sendPointToGecko("Gesture:SingleTap", motionEvent); } return true; From ca7f3b096ef4667956a5e02b8e0d85c419f27c1e Mon Sep 17 00:00:00 2001 From: Bill McCloskey Date: Mon, 29 Sep 2014 13:05:22 -0700 Subject: [PATCH 19/82] Bug 1048968 - Don't use e10s shims when add-on contains unprivileged content (r=bholley) --- js/xpconnect/src/XPCWrappedNativeScope.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/js/xpconnect/src/XPCWrappedNativeScope.cpp b/js/xpconnect/src/XPCWrappedNativeScope.cpp index 833c68e3626d..06df13fe4818 100644 --- a/js/xpconnect/src/XPCWrappedNativeScope.cpp +++ b/js/xpconnect/src/XPCWrappedNativeScope.cpp @@ -379,6 +379,12 @@ xpc::GetAddonScope(JSContext *cx, JS::HandleObject contentScope, JSAddonId *addo JSAutoCompartment ac(cx, contentScope); XPCWrappedNativeScope *nativeScope = CompartmentPrivate::Get(contentScope)->scope; + if (nativeScope->GetPrincipal() != nsXPConnect::SystemPrincipal()) { + // This can happen if, for example, Jetpack loads an unprivileged HTML + // page from the add-on. It's not clear what to do there, so we just use + // the normal global. + return js::GetGlobalForObjectCrossCompartment(contentScope); + } JSObject *scope = nativeScope->EnsureAddonScope(cx, addonId); NS_ENSURE_TRUE(scope, nullptr); From 4465d2b3ffaa1543dd48b8764babda66b0dc26af Mon Sep 17 00:00:00 2001 From: Bill McCloskey Date: Mon, 29 Sep 2014 13:05:22 -0700 Subject: [PATCH 20/82] Bug 1039528 - [e10s] Make "Inspect element" context menu item work (r=pbrosset) --- browser/base/content/nsContextMenu.js | 9 ++++- toolkit/devtools/server/actors/inspector.js | 41 +++++++++++++++++++++ toolkit/devtools/server/child.js | 10 +++++ 3 files changed, 59 insertions(+), 1 deletion(-) diff --git a/browser/base/content/nsContextMenu.js b/browser/base/content/nsContextMenu.js index ff3cc8d10fb8..fcc4e50eb8f9 100644 --- a/browser/base/content/nsContextMenu.js +++ b/browser/base/content/nsContextMenu.js @@ -502,7 +502,14 @@ nsContextMenu.prototype = { let tt = devtools.TargetFactory.forTab(gBrowser.selectedTab); return gDevTools.showToolbox(tt, "inspector").then(function(toolbox) { let inspector = toolbox.getCurrentPanel(); - inspector.selection.setNode(this.target, "browser-context-menu"); + if (this.isRemote) { + this.browser.messageManager.sendAsyncMessage("debug:inspect", {}, {node: this.target}); + inspector.walker.findInspectingNode().then(nodeFront => { + inspector.selection.setNodeFront(nodeFront, "browser-context-menu"); + }); + } else { + inspector.selection.setNode(this.target, "browser-context-menu"); + } }.bind(this)); }, diff --git a/toolkit/devtools/server/actors/inspector.js b/toolkit/devtools/server/actors/inspector.js index d736fe9b4b7a..59a22f1ee84d 100644 --- a/toolkit/devtools/server/actors/inspector.js +++ b/toolkit/devtools/server/actors/inspector.js @@ -170,6 +170,20 @@ exports.setValueSummaryLength = function(val) { gValueSummaryLength = val; }; +// When the user selects a node to inspect in e10s, the parent process +// has a CPOW that wraps the node being inspected. It uses the +// message manager to send this node to the child, which stores the +// node in gInspectingNode. Then a findInspectingNode request is sent +// over the remote debugging protocol, and gInspectingNode is returned +// to the parent as a NodeFront. +var gInspectingNode = null; + +// We expect this function to be called from the child.js frame script +// when it receives the node to be inspected over the message manager. +exports.setInspectingNode = function(val) { + gInspectingNode = val; +}; + /** * Server side of the node actor. */ @@ -1617,6 +1631,25 @@ var WalkerActor = protocol.ActorClass({ return ret; }, + /** + * Return the node that the parent process has asked to + * inspect. This node is expected to be stored in gInspectingNode + * (which is set by a message manager message to the child.js frame + * script). The node is returned over the remote debugging protocol + * as a NodeFront. + */ + findInspectingNode: method(function() { + let node = gInspectingNode; + if (!node) { + return {} + }; + + return this.attachElement(node); + }, { + request: {}, + response: RetVal("disconnectedNode") + }), + /** * Return the first node in the document that matches the given selector. * See https://developer.mozilla.org/en-US/docs/Web/API/Element.querySelector @@ -2536,6 +2569,14 @@ var WalkerFront = exports.WalkerFront = protocol.FrontClass(WalkerActor, { impl: "_releaseNode" }), + findInspectingNode: protocol.custom(function() { + return this._findInspectingNode().then(response => { + return response.node; + }); + }, { + impl: "_findInspectingNode" + }), + querySelector: protocol.custom(function(queryNode, selector) { return this._querySelector(queryNode, selector).then(response => { return response.node; diff --git a/toolkit/devtools/server/child.js b/toolkit/devtools/server/child.js index e2b3e70b99b2..4f55d81fcf99 100644 --- a/toolkit/devtools/server/child.js +++ b/toolkit/devtools/server/child.js @@ -65,6 +65,16 @@ let chromeGlobal = this; } }); addMessageListener("debug:disconnect", onDisconnect); + + let onInspect = DevToolsUtils.makeInfallible(function(msg) { + // Store the node to be inspected in a global variable + // (gInspectingNode). Later we'll fetch this variable again using + // the findInspectingNode request over the remote debugging + // protocol. + let inspector = devtools.require("devtools/server/actors/inspector"); + inspector.setInspectingNode(msg.objects.node); + }); + addMessageListener("debug:inspect", onInspect); })(); } catch(e) { From 44e57f85b7f131806f095bf16ddff642abc6af06 Mon Sep 17 00:00:00 2001 From: Bill McCloskey Date: Mon, 29 Sep 2014 13:05:23 -0700 Subject: [PATCH 21/82] Bug 1072472 - [e10s] Make sandbox shim work with COWs (r=mconley) --- .../addoncompat/RemoteAddonsParent.jsm | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/toolkit/components/addoncompat/RemoteAddonsParent.jsm b/toolkit/components/addoncompat/RemoteAddonsParent.jsm index bbcb34c4d02a..9e5c724205be 100644 --- a/toolkit/components/addoncompat/RemoteAddonsParent.jsm +++ b/toolkit/components/addoncompat/RemoteAddonsParent.jsm @@ -581,6 +581,22 @@ let SandboxParent = { .QueryInterface(Ci.nsIInterfaceRequestor) .getInterface(Ci.nsIContentFrameMessageManager); + if (rest.length) { + // Do a shallow copy of the options object into the child + // process. This way we don't have to access it through a Chrome + // object wrapper, which would require __exposedProps__. + // + // The only object property here is sandboxPrototype. We assume + // it's a child process object (since that's what Greasemonkey + // does) and leave it alone. + let options = rest[0]; + let optionsCopy = new chromeGlobal.Object(); + for (let prop in options) { + optionsCopy[prop] = options[prop]; + } + rest[0] = optionsCopy; + } + // Make a sandbox in the child. let cu = chromeGlobal.Components.utils; let sandbox = cu.Sandbox(principal, ...rest); From f4bdb21ae2851d9ff196087aa56348a5d53aaf33 Mon Sep 17 00:00:00 2001 From: Bill McCloskey Date: Mon, 29 Sep 2014 13:05:23 -0700 Subject: [PATCH 22/82] Bug 1072607 - [e10s] Handle removeEventListener correctly in EventTarget shim (r=mconley) --- toolkit/components/addoncompat/RemoteAddonsChild.jsm | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/toolkit/components/addoncompat/RemoteAddonsChild.jsm b/toolkit/components/addoncompat/RemoteAddonsChild.jsm index 68c48a0deca0..3f66dde3417b 100644 --- a/toolkit/components/addoncompat/RemoteAddonsChild.jsm +++ b/toolkit/components/addoncompat/RemoteAddonsChild.jsm @@ -361,6 +361,8 @@ let ObserverChild = { function EventTargetChild(childGlobal) { this._childGlobal = childGlobal; + this.capturingHandler = (event) => this.handleEvent(true, event); + this.nonCapturingHandler = (event) => this.handleEvent(false, event); NotificationTracker.watch("event", this); } @@ -372,7 +374,7 @@ EventTargetChild.prototype = { track: function(path, register) { let eventType = path[1]; let useCapture = path[2]; - let listener = (event) => this.handleEvent(useCapture, event); + let listener = useCapture ? this.capturingHandler : this.nonCapturingHandler; if (register) { this._childGlobal.addEventListener(eventType, listener, useCapture, true); } else { From e1eed39699289f96c37b71e26b0bc8b404b210fb Mon Sep 17 00:00:00 2001 From: Bill McCloskey Date: Mon, 29 Sep 2014 13:05:23 -0700 Subject: [PATCH 23/82] Bug 1072477 - [e10s] Make EventTarget shim work when removeEventListener called from listener (r=mconley) --- .../addoncompat/RemoteAddonsParent.jsm | 23 ++++++++++++------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/toolkit/components/addoncompat/RemoteAddonsParent.jsm b/toolkit/components/addoncompat/RemoteAddonsParent.jsm index 9e5c724205be..761c21917a3c 100644 --- a/toolkit/components/addoncompat/RemoteAddonsParent.jsm +++ b/toolkit/components/addoncompat/RemoteAddonsParent.jsm @@ -464,17 +464,24 @@ let EventTargetParent = { continue; } let forType = setDefault(listeners, type, []); + + // Make a copy in case they call removeEventListener in the listener. + let handlers = []; for (let {listener, wantsUntrusted, useCapture} of forType) { if ((wantsUntrusted || isTrusted) && useCapture == capturing) { - try { - if ("handleEvent" in listener) { - listener.handleEvent(event); - } else { - listener.call(event.target, event); - } - } catch (e) { - Cu.reportError(e); + handlers.push(listener); + } + } + + for (let handler of handlers) { + try { + if ("handleEvent" in handler) { + handler.handleEvent(event); + } else { + handler.call(event.target, event); } + } catch (e) { + Cu.reportError(e); } } } From f22a7a863cc9dcd5282d826b7cfd154c34e198b6 Mon Sep 17 00:00:00 2001 From: Bill McCloskey Date: Mon, 29 Sep 2014 13:05:23 -0700 Subject: [PATCH 24/82] Bug 1071920 - Make sure we don't time out incorrectly with chaos mode (r=roc) --- testing/mochitest/browser-test.js | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/testing/mochitest/browser-test.js b/testing/mochitest/browser-test.js index 59090fc25883..93049f06bc7d 100644 --- a/testing/mochitest/browser-test.js +++ b/testing/mochitest/browser-test.js @@ -676,7 +676,18 @@ Tester.prototype = { } else { var self = this; + var timeoutExpires = Date.now() + gTimeoutSeconds * 1000; + var waitUntilAtLeast = timeoutExpires - 1000; this.currentTest.scope.__waitTimer = setTimeout(function timeoutFn() { + // We sometimes get woken up long before the gTimeoutSeconds + // have elapsed (when running in chaos mode for example). This + // code ensures that we don't wrongly time out in that case. + if (Date.now() < waitUntilAtLeast) { + self.currentTest.scope.__waitTimer = + setTimeout(timeoutFn, timeoutExpires - Date.now()); + return; + } + if (--self.currentTest.scope.__timeoutFactor > 0) { // We were asked to wait a bit longer. self.currentTest.scope.info( From 353958d0cc75736e9066acde0d1525b1f9ee07d3 Mon Sep 17 00:00:00 2001 From: Terrence Cole Date: Mon, 22 Sep 2014 10:23:32 -0700 Subject: [PATCH 25/82] Bug 1064578 - Part 1: move gc sweep phase guards above loop heads; r=jonco --HG-- extra : rebase_source : ccc8700f784e6b73c9a87a1f8524b4cf206aa9b0 --- js/src/jsgc.cpp | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/js/src/jsgc.cpp b/js/src/jsgc.cpp index 8e7720210e6e..cfb3d168b563 100644 --- a/js/src/jsgc.cpp +++ b/js/src/jsgc.cpp @@ -4659,22 +4659,28 @@ GCRuntime::beginSweepingZoneGroup() { gcstats::AutoPhase ap(stats, gcstats::PHASE_SWEEP_COMPARTMENTS); + gcstats::AutoPhase apdc(stats, gcstats::PHASE_SWEEP_DISCARD_CODE); for (GCZoneGroupIter zone(rt); !zone.done(); zone.next()) { - gcstats::AutoPhase ap(stats, gcstats::PHASE_SWEEP_DISCARD_CODE); zone->discardJitCode(&fop); } + } + + { + gcstats::AutoPhase ap(stats, gcstats::PHASE_SWEEP_COMPARTMENTS); + gcstats::AutoSCC scc(stats, zoneGroupIndex); + gcstats::AutoPhase apst(stats, gcstats::PHASE_SWEEP_TABLES); for (GCCompartmentGroupIter c(rt); !c.done(); c.next()) { - gcstats::AutoSCC scc(stats, zoneGroupIndex); - gcstats::AutoPhase ap(stats, gcstats::PHASE_SWEEP_TABLES); - c->sweep(&fop, releaseObservedTypes && !c->zone()->isPreservingCode()); } + } + + { + gcstats::AutoPhase ap(stats, gcstats::PHASE_SWEEP_COMPARTMENTS); + gcstats::AutoSCC scc(stats, zoneGroupIndex); for (GCZoneGroupIter zone(rt); !zone.done(); zone.next()) { - gcstats::AutoSCC scc(stats, zoneGroupIndex); - // If there is an OOM while sweeping types, the type information // will be deoptimized so that it is still correct (i.e. // overapproximates the possible types in the zone), but the From d1a9a441e239e89dd3a378b98175101da8bdac07 Mon Sep 17 00:00:00 2001 From: Terrence Cole Date: Mon, 22 Sep 2014 10:41:23 -0700 Subject: [PATCH 26/82] Bug 1064578 - Part 2: make compartment sweeping fine-grained; r=jonco --HG-- extra : rebase_source : 8cc9f3f4781b74a053de8c199128e8ff03ff8332 --- js/src/jscompartment.cpp | 78 ++++++++++++++++++++++++++-------------- js/src/jscompartment.h | 13 ++++++- js/src/jsgc.cpp | 15 +++++++- 3 files changed, 78 insertions(+), 28 deletions(-) diff --git a/js/src/jscompartment.cpp b/js/src/jscompartment.cpp index 85e397b4a580..9ba295dbeaf9 100644 --- a/js/src/jscompartment.cpp +++ b/js/src/jscompartment.cpp @@ -542,64 +542,86 @@ JSCompartment::markRoots(JSTracer *trc) } void -JSCompartment::sweep(FreeOp *fop, bool releaseTypes) +JSCompartment::sweepInnerViews() { - JS_ASSERT(!activeAnalysis); JSRuntime *rt = runtimeFromMainThread(); + gcstats::MaybeAutoPhase ap(rt->gc.stats, !rt->isHeapCompacting(), + gcstats::PHASE_SWEEP_TABLES_INNER_VIEWS); + innerViews.sweep(rt); +} - { - gcstats::MaybeAutoPhase ap(rt->gc.stats, !rt->isHeapCompacting(), - gcstats::PHASE_SWEEP_TABLES_INNER_VIEWS); - innerViews.sweep(rt); - } +void +JSCompartment::sweepTypeObjectTables() +{ + JSRuntime *rt = runtimeFromMainThread(); + gcstats::MaybeAutoPhase ap(rt->gc.stats, !rt->isHeapCompacting(), + gcstats::PHASE_SWEEP_TABLES_TYPE_OBJECT); + sweepNewTypeObjectTable(newTypeObjects); + sweepNewTypeObjectTable(lazyTypeObjects); +} - { - gcstats::MaybeAutoPhase ap(rt->gc.stats, !rt->isHeapCompacting(), - gcstats::PHASE_SWEEP_TABLES_WRAPPER); - sweepCrossCompartmentWrappers(); - } - - /* Remove dead references held weakly by the compartment. */ - - sweepBaseShapeTable(); - sweepInitialShapeTable(); - { - gcstats::MaybeAutoPhase ap(rt->gc.stats, !rt->isHeapCompacting(), - gcstats::PHASE_SWEEP_TABLES_TYPE_OBJECT); - sweepNewTypeObjectTable(newTypeObjects); - sweepNewTypeObjectTable(lazyTypeObjects); - } - sweepCallsiteClones(); - savedStacks_.sweep(rt); +void +JSCompartment::sweepSavedStacks() +{ + savedStacks_.sweep(runtimeFromMainThread()); +} +void +JSCompartment::sweepGlobalObject(FreeOp *fop) +{ if (global_ && IsObjectAboutToBeFinalized(global_.unsafeGet())) { if (debugMode()) Debugger::detachAllDebuggersFromGlobal(fop, global_); global_.set(nullptr); } +} +void +JSCompartment::sweepSelfHostingScriptSource() +{ if (selfHostingScriptSource && IsObjectAboutToBeFinalized((JSObject **) selfHostingScriptSource.unsafeGet())) { selfHostingScriptSource.set(nullptr); } +} +void +JSCompartment::sweepJitCompartment(FreeOp *fop) +{ if (jitCompartment_) jitCompartment_->sweep(fop, this); +} +void +JSCompartment::sweepRegExps() +{ /* * JIT code increments activeWarmUpCounter for any RegExpShared used by jit * code for the lifetime of the JIT script. Thus, we must perform * sweeping after clearing jit code. */ - regExps.sweep(rt); + regExps.sweep(runtimeFromMainThread()); +} +void +JSCompartment::sweepDebugScopes() +{ + JSRuntime *rt = runtimeFromMainThread(); if (debugScopes) debugScopes->sweep(rt); +} +void +JSCompartment::sweepWeakMaps() +{ /* Finalize unreachable (key,value) pairs in all weak maps. */ WeakMapBase::sweepCompartment(this); +} +void +JSCompartment::sweepNativeIterators() +{ /* Sweep list of native iterators. */ NativeIterator *ni = enumerators->next(); while (ni != enumerators) { @@ -619,6 +641,10 @@ JSCompartment::sweep(FreeOp *fop, bool releaseTypes) void JSCompartment::sweepCrossCompartmentWrappers() { + JSRuntime *rt = runtimeFromMainThread(); + gcstats::MaybeAutoPhase ap(rt->gc.stats, !rt->isHeapCompacting(), + gcstats::PHASE_SWEEP_TABLES_WRAPPER); + /* Remove dead wrappers from the table. */ for (WrapperMap::Enum e(crossCompartmentWrappers); !e.empty(); e.popFront()) { CrossCompartmentKey key = e.front().key(); diff --git a/js/src/jscompartment.h b/js/src/jscompartment.h index 29933c44e70c..c97fd12897a0 100644 --- a/js/src/jscompartment.h +++ b/js/src/jscompartment.h @@ -348,8 +348,19 @@ struct JSCompartment void trace(JSTracer *trc); void markRoots(JSTracer *trc); bool preserveJitCode() { return gcPreserveJitCode; } - void sweep(js::FreeOp *fop, bool releaseTypes); + + void sweepInnerViews(); void sweepCrossCompartmentWrappers(); + void sweepTypeObjectTables(); + void sweepSavedStacks(); + void sweepGlobalObject(js::FreeOp *fop); + void sweepSelfHostingScriptSource(); + void sweepJitCompartment(js::FreeOp *fop); + void sweepRegExps(); + void sweepDebugScopes(); + void sweepWeakMaps(); + void sweepNativeIterators(); + void purge(); void clearTables(); diff --git a/js/src/jsgc.cpp b/js/src/jsgc.cpp index cfb3d168b563..447ce1db3117 100644 --- a/js/src/jsgc.cpp +++ b/js/src/jsgc.cpp @@ -4672,7 +4672,20 @@ GCRuntime::beginSweepingZoneGroup() gcstats::AutoPhase apst(stats, gcstats::PHASE_SWEEP_TABLES); for (GCCompartmentGroupIter c(rt); !c.done(); c.next()) { - c->sweep(&fop, releaseObservedTypes && !c->zone()->isPreservingCode()); + c->sweepInnerViews(); + c->sweepCrossCompartmentWrappers(); + c->sweepBaseShapeTable(); + c->sweepInitialShapeTable(); + c->sweepTypeObjectTables(); + c->sweepCallsiteClones(); + c->sweepSavedStacks(); + c->sweepGlobalObject(&fop); + c->sweepSelfHostingScriptSource(); + c->sweepJitCompartment(&fop); + c->sweepRegExps(); + c->sweepDebugScopes(); + c->sweepWeakMaps(); + c->sweepNativeIterators(); } } From 1e308472a9d5a986f3d5be6bdab3df3e1bbd349b Mon Sep 17 00:00:00 2001 From: Terrence Cole Date: Mon, 22 Sep 2014 11:16:09 -0700 Subject: [PATCH 27/82] Bug 1064578 - Part 3: move the table sweeping sections into a separate block; r=jonco --HG-- extra : rebase_source : d01822fc937cc2fe7230eae338aa15cda1ae2f73 --- js/src/jsgc.cpp | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/js/src/jsgc.cpp b/js/src/jsgc.cpp index 447ce1db3117..f3bd2cee5fd4 100644 --- a/js/src/jsgc.cpp +++ b/js/src/jsgc.cpp @@ -4651,12 +4651,6 @@ GCRuntime::beginSweepingZoneGroup() } } - /* Collect watch points associated with unreachable objects. */ - WatchpointMap::sweepAll(rt); - - /* Detach unreachable debuggers and global objects from each other. */ - Debugger::sweepAll(&fop); - { gcstats::AutoPhase ap(stats, gcstats::PHASE_SWEEP_COMPARTMENTS); gcstats::AutoPhase apdc(stats, gcstats::PHASE_SWEEP_DISCARD_CODE); @@ -4677,18 +4671,33 @@ GCRuntime::beginSweepingZoneGroup() c->sweepBaseShapeTable(); c->sweepInitialShapeTable(); c->sweepTypeObjectTables(); + c->sweepRegExps(); + } + } + + { + gcstats::AutoPhase ap(stats, gcstats::PHASE_SWEEP_COMPARTMENTS); + gcstats::AutoSCC scc(stats, zoneGroupIndex); + gcstats::AutoPhase apst(stats, gcstats::PHASE_SWEEP_TABLES); + + for (GCCompartmentGroupIter c(rt); !c.done(); c.next()) { c->sweepCallsiteClones(); c->sweepSavedStacks(); c->sweepGlobalObject(&fop); c->sweepSelfHostingScriptSource(); c->sweepJitCompartment(&fop); - c->sweepRegExps(); c->sweepDebugScopes(); c->sweepWeakMaps(); c->sweepNativeIterators(); } } + /* Collect watch points associated with unreachable objects. */ + WatchpointMap::sweepAll(rt); + + /* Detach unreachable debuggers and global objects from each other. */ + Debugger::sweepAll(&fop); + { gcstats::AutoPhase ap(stats, gcstats::PHASE_SWEEP_COMPARTMENTS); gcstats::AutoSCC scc(stats, zoneGroupIndex); From d8b71ddee493400923c1708c938e6ef644cca039 Mon Sep 17 00:00:00 2001 From: Terrence Cole Date: Mon, 22 Sep 2014 11:47:53 -0700 Subject: [PATCH 28/82] Bug 1064578 - Part 4: remove the arbitrary phase distinction we make for tables; r=jonco --HG-- extra : rebase_source : 6116163ca5f6ca9a3eccddf434c9782fc457094c --- js/src/gc/Statistics.cpp | 16 ++++++------ js/src/gc/Statistics.h | 16 ++++++------ js/src/gc/Zone.cpp | 4 +-- js/src/jscompartment.cpp | 12 +-------- js/src/jsgc.cpp | 56 ++++++++++++++++++++++++++++++++++++++-- js/src/vm/Shape.cpp | 8 ------ 6 files changed, 72 insertions(+), 40 deletions(-) diff --git a/js/src/gc/Statistics.cpp b/js/src/gc/Statistics.cpp index 343742fc191b..d28f249c861b 100644 --- a/js/src/gc/Statistics.cpp +++ b/js/src/gc/Statistics.cpp @@ -292,14 +292,14 @@ static const PhaseInfo phases[] = { { PHASE_SWEEP_SYMBOL_REGISTRY, "Sweep Symbol Registry", PHASE_SWEEP }, { PHASE_SWEEP_COMPARTMENTS, "Sweep Compartments", PHASE_SWEEP }, { PHASE_SWEEP_DISCARD_CODE, "Sweep Discard Code", PHASE_SWEEP_COMPARTMENTS }, - { PHASE_SWEEP_TABLES, "Sweep Tables", PHASE_SWEEP_COMPARTMENTS }, - { PHASE_SWEEP_TABLES_INNER_VIEWS, "Sweep Inner Views", PHASE_SWEEP_TABLES }, - { PHASE_SWEEP_TABLES_WRAPPER, "Sweep Cross Compartment Wrappers", PHASE_SWEEP_TABLES }, - { PHASE_SWEEP_TABLES_BASE_SHAPE, "Sweep Base Shapes", PHASE_SWEEP_TABLES }, - { PHASE_SWEEP_TABLES_INITIAL_SHAPE, "Sweep Initial Shapes", PHASE_SWEEP_TABLES }, - { PHASE_SWEEP_TABLES_TYPE_OBJECT, "Sweep Type Objects", PHASE_SWEEP_TABLES }, - { PHASE_SWEEP_TABLES_BREAKPOINT, "Sweep Breakpoints", PHASE_SWEEP_TABLES }, - { PHASE_SWEEP_TABLES_REGEXP, "Sweep Regexps", PHASE_SWEEP_TABLES }, + { PHASE_SWEEP_INNER_VIEWS, "Sweep Inner Views", PHASE_SWEEP_COMPARTMENTS }, + { PHASE_SWEEP_CC_WRAPPER, "Sweep Cross Compartment Wrappers", PHASE_SWEEP_COMPARTMENTS }, + { PHASE_SWEEP_BASE_SHAPE, "Sweep Base Shapes", PHASE_SWEEP_COMPARTMENTS }, + { PHASE_SWEEP_INITIAL_SHAPE, "Sweep Initial Shapes", PHASE_SWEEP_COMPARTMENTS }, + { PHASE_SWEEP_TYPE_OBJECT, "Sweep Type Objects", PHASE_SWEEP_COMPARTMENTS }, + { PHASE_SWEEP_BREAKPOINT, "Sweep Breakpoints", PHASE_SWEEP_COMPARTMENTS }, + { PHASE_SWEEP_REGEXP, "Sweep Regexps", PHASE_SWEEP_COMPARTMENTS }, + { PHASE_SWEEP_MISC, "Sweep Miscellaneous", PHASE_SWEEP_COMPARTMENTS }, { PHASE_DISCARD_ANALYSIS, "Discard Analysis", PHASE_SWEEP_COMPARTMENTS }, { PHASE_DISCARD_TI, "Discard TI", PHASE_DISCARD_ANALYSIS }, { PHASE_FREE_TI_ARENA, "Free TI Arena", PHASE_DISCARD_ANALYSIS }, diff --git a/js/src/gc/Statistics.h b/js/src/gc/Statistics.h index c2ec1b5bd457..43ee1a238a3a 100644 --- a/js/src/gc/Statistics.h +++ b/js/src/gc/Statistics.h @@ -43,14 +43,14 @@ enum Phase { PHASE_SWEEP_SYMBOL_REGISTRY, PHASE_SWEEP_COMPARTMENTS, PHASE_SWEEP_DISCARD_CODE, - PHASE_SWEEP_TABLES, - PHASE_SWEEP_TABLES_INNER_VIEWS, - PHASE_SWEEP_TABLES_WRAPPER, - PHASE_SWEEP_TABLES_BASE_SHAPE, - PHASE_SWEEP_TABLES_INITIAL_SHAPE, - PHASE_SWEEP_TABLES_TYPE_OBJECT, - PHASE_SWEEP_TABLES_BREAKPOINT, - PHASE_SWEEP_TABLES_REGEXP, + PHASE_SWEEP_INNER_VIEWS, + PHASE_SWEEP_CC_WRAPPER, + PHASE_SWEEP_BASE_SHAPE, + PHASE_SWEEP_INITIAL_SHAPE, + PHASE_SWEEP_TYPE_OBJECT, + PHASE_SWEEP_BREAKPOINT, + PHASE_SWEEP_REGEXP, + PHASE_SWEEP_MISC, PHASE_DISCARD_ANALYSIS, PHASE_DISCARD_TI, PHASE_FREE_TI_ARENA, diff --git a/js/src/gc/Zone.cpp b/js/src/gc/Zone.cpp index 11cf4eeabe1c..d310354ca867 100644 --- a/js/src/gc/Zone.cpp +++ b/js/src/gc/Zone.cpp @@ -122,10 +122,8 @@ Zone::sweep(FreeOp *fop, bool releaseTypes, bool *oom) } if (!fop->runtime()->debuggerList.isEmpty()) { - gcstats::MaybeAutoPhase ap1(gc.stats, !gc.isHeapCompacting(), - gcstats::PHASE_SWEEP_TABLES); gcstats::MaybeAutoPhase ap2(gc.stats, !gc.isHeapCompacting(), - gcstats::PHASE_SWEEP_TABLES_BREAKPOINT); + gcstats::PHASE_SWEEP_BREAKPOINT); sweepBreakpoints(fop); } } diff --git a/js/src/jscompartment.cpp b/js/src/jscompartment.cpp index 9ba295dbeaf9..56829754c5b9 100644 --- a/js/src/jscompartment.cpp +++ b/js/src/jscompartment.cpp @@ -544,18 +544,12 @@ JSCompartment::markRoots(JSTracer *trc) void JSCompartment::sweepInnerViews() { - JSRuntime *rt = runtimeFromMainThread(); - gcstats::MaybeAutoPhase ap(rt->gc.stats, !rt->isHeapCompacting(), - gcstats::PHASE_SWEEP_TABLES_INNER_VIEWS); - innerViews.sweep(rt); + innerViews.sweep(runtimeFromMainThread()); } void JSCompartment::sweepTypeObjectTables() { - JSRuntime *rt = runtimeFromMainThread(); - gcstats::MaybeAutoPhase ap(rt->gc.stats, !rt->isHeapCompacting(), - gcstats::PHASE_SWEEP_TABLES_TYPE_OBJECT); sweepNewTypeObjectTable(newTypeObjects); sweepNewTypeObjectTable(lazyTypeObjects); } @@ -641,10 +635,6 @@ JSCompartment::sweepNativeIterators() void JSCompartment::sweepCrossCompartmentWrappers() { - JSRuntime *rt = runtimeFromMainThread(); - gcstats::MaybeAutoPhase ap(rt->gc.stats, !rt->isHeapCompacting(), - gcstats::PHASE_SWEEP_TABLES_WRAPPER); - /* Remove dead wrappers from the table. */ for (WrapperMap::Enum e(crossCompartmentWrappers); !e.empty(); e.popFront()) { CrossCompartmentKey key = e.front().key(); diff --git a/js/src/jsgc.cpp b/js/src/jsgc.cpp index f3bd2cee5fd4..a797f284170b 100644 --- a/js/src/jsgc.cpp +++ b/js/src/jsgc.cpp @@ -4663,14 +4663,65 @@ GCRuntime::beginSweepingZoneGroup() { gcstats::AutoPhase ap(stats, gcstats::PHASE_SWEEP_COMPARTMENTS); gcstats::AutoSCC scc(stats, zoneGroupIndex); - gcstats::AutoPhase apst(stats, gcstats::PHASE_SWEEP_TABLES); + gcstats::MaybeAutoPhase apiv(stats, !isHeapCompacting(), + gcstats::PHASE_SWEEP_INNER_VIEWS); for (GCCompartmentGroupIter c(rt); !c.done(); c.next()) { c->sweepInnerViews(); + } + } + + { + gcstats::AutoPhase ap(stats, gcstats::PHASE_SWEEP_COMPARTMENTS); + gcstats::AutoSCC scc(stats, zoneGroupIndex); + gcstats::MaybeAutoPhase apccw(stats, !isHeapCompacting(), + gcstats::PHASE_SWEEP_CC_WRAPPER); + + for (GCCompartmentGroupIter c(rt); !c.done(); c.next()) { c->sweepCrossCompartmentWrappers(); + } + } + + { + gcstats::AutoPhase ap(stats, gcstats::PHASE_SWEEP_COMPARTMENTS); + gcstats::AutoSCC scc(stats, zoneGroupIndex); + gcstats::MaybeAutoPhase apbs(stats, !isHeapCompacting(), + gcstats::PHASE_SWEEP_BASE_SHAPE); + + for (GCCompartmentGroupIter c(rt); !c.done(); c.next()) { c->sweepBaseShapeTable(); + } + } + + { + gcstats::AutoPhase ap(stats, gcstats::PHASE_SWEEP_COMPARTMENTS); + gcstats::AutoSCC scc(stats, zoneGroupIndex); + gcstats::MaybeAutoPhase apis(stats, !isHeapCompacting(), + gcstats::PHASE_SWEEP_INITIAL_SHAPE); + + for (GCCompartmentGroupIter c(rt); !c.done(); c.next()) { c->sweepInitialShapeTable(); + } + } + + { + gcstats::AutoPhase ap(stats, gcstats::PHASE_SWEEP_COMPARTMENTS); + gcstats::AutoSCC scc(stats, zoneGroupIndex); + gcstats::MaybeAutoPhase apto(stats, !isHeapCompacting(), + gcstats::PHASE_SWEEP_TYPE_OBJECT); + + for (GCCompartmentGroupIter c(rt); !c.done(); c.next()) { c->sweepTypeObjectTables(); + } + } + + { + gcstats::AutoPhase ap(stats, gcstats::PHASE_SWEEP_COMPARTMENTS); + gcstats::AutoSCC scc(stats, zoneGroupIndex); + gcstats::MaybeAutoPhase apre(stats, !isHeapCompacting(), + gcstats::PHASE_SWEEP_REGEXP); + + for (GCCompartmentGroupIter c(rt); !c.done(); c.next()) { c->sweepRegExps(); } } @@ -4678,7 +4729,8 @@ GCRuntime::beginSweepingZoneGroup() { gcstats::AutoPhase ap(stats, gcstats::PHASE_SWEEP_COMPARTMENTS); gcstats::AutoSCC scc(stats, zoneGroupIndex); - gcstats::AutoPhase apst(stats, gcstats::PHASE_SWEEP_TABLES); + gcstats::MaybeAutoPhase apmisc(stats, !isHeapCompacting(), + gcstats::PHASE_SWEEP_MISC); for (GCCompartmentGroupIter c(rt); !c.done(); c.next()) { c->sweepCallsiteClones(); diff --git a/js/src/vm/Shape.cpp b/js/src/vm/Shape.cpp index 1ce68e85050c..dc872779eb89 100644 --- a/js/src/vm/Shape.cpp +++ b/js/src/vm/Shape.cpp @@ -1545,10 +1545,6 @@ BaseShape::assertConsistency() void JSCompartment::sweepBaseShapeTable() { - GCRuntime &gc = runtimeFromMainThread()->gc; - gcstats::MaybeAutoPhase ap(gc.stats, !gc.isHeapCompacting(), - gcstats::PHASE_SWEEP_TABLES_BASE_SHAPE); - if (baseShapes.initialized()) { for (BaseShapeSet::Enum e(baseShapes); !e.empty(); e.popFront()) { UnownedBaseShape *base = e.front().unbarrieredGet(); @@ -1843,10 +1839,6 @@ EmptyShape::insertInitialShape(ExclusiveContext *cx, HandleShape shape, HandleOb void JSCompartment::sweepInitialShapeTable() { - GCRuntime &gc = runtimeFromMainThread()->gc; - gcstats::MaybeAutoPhase ap(gc.stats, !gc.isHeapCompacting(), - gcstats::PHASE_SWEEP_TABLES_INITIAL_SHAPE); - if (initialShapes.initialized()) { for (InitialShapeSet::Enum e(initialShapes); !e.empty(); e.popFront()) { const InitialShapeEntry &entry = e.front(); From bb969e6fa0e87dd6620f99f3df1c61671bca73c1 Mon Sep 17 00:00:00 2001 From: Terrence Cole Date: Mon, 22 Sep 2014 12:45:42 -0700 Subject: [PATCH 29/82] Bug 1064578 - Part 5: common up the redudant phase guards; r=jonco --HG-- extra : rebase_source : 2e9f44f99f3f45746b9d7aca0b632da62bd798b5 --- js/src/jsgc.cpp | 232 ++++++++++++++++++++++-------------------------- 1 file changed, 105 insertions(+), 127 deletions(-) diff --git a/js/src/jsgc.cpp b/js/src/jsgc.cpp index a797f284170b..676afbfa3db3 100644 --- a/js/src/jsgc.cpp +++ b/js/src/jsgc.cpp @@ -4641,136 +4641,114 @@ GCRuntime::beginSweepingZoneGroup() } if (sweepingAtoms) { + gcstats::AutoPhase ap(stats, gcstats::PHASE_SWEEP_ATOMS); + rt->sweepAtoms(); + } + + { + gcstats::AutoPhase ap(stats, gcstats::PHASE_SWEEP_COMPARTMENTS); + gcstats::AutoSCC scc(stats, zoneGroupIndex); + { - gcstats::AutoPhase ap(stats, gcstats::PHASE_SWEEP_ATOMS); - rt->sweepAtoms(); - } - { - gcstats::AutoPhase ap(stats, gcstats::PHASE_SWEEP_SYMBOL_REGISTRY); - rt->symbolRegistry().sweep(); - } - } - - { - gcstats::AutoPhase ap(stats, gcstats::PHASE_SWEEP_COMPARTMENTS); - gcstats::AutoPhase apdc(stats, gcstats::PHASE_SWEEP_DISCARD_CODE); - - for (GCZoneGroupIter zone(rt); !zone.done(); zone.next()) { - zone->discardJitCode(&fop); - } - } - - { - gcstats::AutoPhase ap(stats, gcstats::PHASE_SWEEP_COMPARTMENTS); - gcstats::AutoSCC scc(stats, zoneGroupIndex); - gcstats::MaybeAutoPhase apiv(stats, !isHeapCompacting(), - gcstats::PHASE_SWEEP_INNER_VIEWS); - - for (GCCompartmentGroupIter c(rt); !c.done(); c.next()) { - c->sweepInnerViews(); - } - } - - { - gcstats::AutoPhase ap(stats, gcstats::PHASE_SWEEP_COMPARTMENTS); - gcstats::AutoSCC scc(stats, zoneGroupIndex); - gcstats::MaybeAutoPhase apccw(stats, !isHeapCompacting(), - gcstats::PHASE_SWEEP_CC_WRAPPER); - - for (GCCompartmentGroupIter c(rt); !c.done(); c.next()) { - c->sweepCrossCompartmentWrappers(); - } - } - - { - gcstats::AutoPhase ap(stats, gcstats::PHASE_SWEEP_COMPARTMENTS); - gcstats::AutoSCC scc(stats, zoneGroupIndex); - gcstats::MaybeAutoPhase apbs(stats, !isHeapCompacting(), - gcstats::PHASE_SWEEP_BASE_SHAPE); - - for (GCCompartmentGroupIter c(rt); !c.done(); c.next()) { - c->sweepBaseShapeTable(); - } - } - - { - gcstats::AutoPhase ap(stats, gcstats::PHASE_SWEEP_COMPARTMENTS); - gcstats::AutoSCC scc(stats, zoneGroupIndex); - gcstats::MaybeAutoPhase apis(stats, !isHeapCompacting(), - gcstats::PHASE_SWEEP_INITIAL_SHAPE); - - for (GCCompartmentGroupIter c(rt); !c.done(); c.next()) { - c->sweepInitialShapeTable(); - } - } - - { - gcstats::AutoPhase ap(stats, gcstats::PHASE_SWEEP_COMPARTMENTS); - gcstats::AutoSCC scc(stats, zoneGroupIndex); - gcstats::MaybeAutoPhase apto(stats, !isHeapCompacting(), - gcstats::PHASE_SWEEP_TYPE_OBJECT); - - for (GCCompartmentGroupIter c(rt); !c.done(); c.next()) { - c->sweepTypeObjectTables(); - } - } - - { - gcstats::AutoPhase ap(stats, gcstats::PHASE_SWEEP_COMPARTMENTS); - gcstats::AutoSCC scc(stats, zoneGroupIndex); - gcstats::MaybeAutoPhase apre(stats, !isHeapCompacting(), - gcstats::PHASE_SWEEP_REGEXP); - - for (GCCompartmentGroupIter c(rt); !c.done(); c.next()) { - c->sweepRegExps(); - } - } - - { - gcstats::AutoPhase ap(stats, gcstats::PHASE_SWEEP_COMPARTMENTS); - gcstats::AutoSCC scc(stats, zoneGroupIndex); - gcstats::MaybeAutoPhase apmisc(stats, !isHeapCompacting(), - gcstats::PHASE_SWEEP_MISC); - - for (GCCompartmentGroupIter c(rt); !c.done(); c.next()) { - c->sweepCallsiteClones(); - c->sweepSavedStacks(); - c->sweepGlobalObject(&fop); - c->sweepSelfHostingScriptSource(); - c->sweepJitCompartment(&fop); - c->sweepDebugScopes(); - c->sweepWeakMaps(); - c->sweepNativeIterators(); - } - } - - /* Collect watch points associated with unreachable objects. */ - WatchpointMap::sweepAll(rt); - - /* Detach unreachable debuggers and global objects from each other. */ - Debugger::sweepAll(&fop); - - { - gcstats::AutoPhase ap(stats, gcstats::PHASE_SWEEP_COMPARTMENTS); - gcstats::AutoSCC scc(stats, zoneGroupIndex); - - for (GCZoneGroupIter zone(rt); !zone.done(); zone.next()) { - // If there is an OOM while sweeping types, the type information - // will be deoptimized so that it is still correct (i.e. - // overapproximates the possible types in the zone), but the - // constraints might not have been triggered on the deoptimization - // or even copied over completely. In this case, destroy all JIT - // code and new script information in the zone, the only things - // whose correctness depends on the type constraints. - bool oom = false; - zone->sweep(&fop, releaseObservedTypes && !zone->isPreservingCode(), &oom); - - if (oom) { - zone->setPreservingCode(false); - zone->discardJitCode(&fop); - zone->types.clearAllNewScriptsOnOOM(); + gcstats::MaybeAutoPhase apiv(stats, !isHeapCompacting(), + gcstats::PHASE_SWEEP_INNER_VIEWS); + for (GCCompartmentGroupIter c(rt); !c.done(); c.next()) { + c->sweepInnerViews(); } } + + { + gcstats::MaybeAutoPhase apccw(stats, !isHeapCompacting(), + gcstats::PHASE_SWEEP_CC_WRAPPER); + for (GCCompartmentGroupIter c(rt); !c.done(); c.next()) { + c->sweepCrossCompartmentWrappers(); + } + } + + { + gcstats::MaybeAutoPhase apbs(stats, !isHeapCompacting(), + gcstats::PHASE_SWEEP_BASE_SHAPE); + for (GCCompartmentGroupIter c(rt); !c.done(); c.next()) { + c->sweepBaseShapeTable(); + } + } + + { + gcstats::MaybeAutoPhase apis(stats, !isHeapCompacting(), + gcstats::PHASE_SWEEP_INITIAL_SHAPE); + for (GCCompartmentGroupIter c(rt); !c.done(); c.next()) { + c->sweepInitialShapeTable(); + } + } + + { + gcstats::MaybeAutoPhase apto(stats, !isHeapCompacting(), + gcstats::PHASE_SWEEP_TYPE_OBJECT); + for (GCCompartmentGroupIter c(rt); !c.done(); c.next()) { + c->sweepTypeObjectTables(); + } + } + + { + gcstats::MaybeAutoPhase apre(stats, !isHeapCompacting(), + gcstats::PHASE_SWEEP_REGEXP); + for (GCCompartmentGroupIter c(rt); !c.done(); c.next()) { + c->sweepRegExps(); + } + } + + { + gcstats::MaybeAutoPhase apmisc(stats, !isHeapCompacting(), + gcstats::PHASE_SWEEP_MISC); + for (GCCompartmentGroupIter c(rt); !c.done(); c.next()) { + c->sweepCallsiteClones(); + c->sweepSavedStacks(); + c->sweepGlobalObject(&fop); + c->sweepSelfHostingScriptSource(); + c->sweepJitCompartment(&fop); + c->sweepDebugScopes(); + c->sweepWeakMaps(); + c->sweepNativeIterators(); + } + + // Collect watch points associated with unreachable objects. + WatchpointMap::sweepAll(rt); + + // Detach unreachable debuggers and global objects from each other. + Debugger::sweepAll(&fop); + } + + { + gcstats::AutoPhase apdc(stats, gcstats::PHASE_SWEEP_DISCARD_CODE); + for (GCZoneGroupIter zone(rt); !zone.done(); zone.next()) { + zone->discardJitCode(&fop); + } + } + + { + for (GCZoneGroupIter zone(rt); !zone.done(); zone.next()) { + // If there is an OOM while sweeping types, the type information + // will be deoptimized so that it is still correct (i.e. + // overapproximates the possible types in the zone), but the + // constraints might not have been triggered on the deoptimization + // or even copied over completely. In this case, destroy all JIT + // code and new script information in the zone, the only things + // whose correctness depends on the type constraints. + bool oom = false; + zone->sweep(&fop, releaseObservedTypes && !zone->isPreservingCode(), &oom); + + if (oom) { + zone->setPreservingCode(false); + zone->discardJitCode(&fop); + zone->types.clearAllNewScriptsOnOOM(); + } + } + } + } + + if (sweepingAtoms) { + gcstats::AutoPhase ap(stats, gcstats::PHASE_SWEEP_SYMBOL_REGISTRY); + rt->symbolRegistry().sweep(); } /* From ed249d7942834930bc993f3cf2379675d8c65ecb Mon Sep 17 00:00:00 2001 From: Terrence Cole Date: Mon, 22 Sep 2014 13:22:30 -0700 Subject: [PATCH 30/82] Bug 1064578 - Part 6: make zone sweeping fine-grained too; r=jonco --HG-- extra : rebase_source : 002d5e61254440f939e5f35bb35937481189f046 --- js/src/gc/Zone.cpp | 34 ++++++++++++++++++---------------- js/src/gc/Zone.h | 2 +- js/src/jsgc.cpp | 29 ++++++++++++++--------------- 3 files changed, 33 insertions(+), 32 deletions(-) diff --git a/js/src/gc/Zone.cpp b/js/src/gc/Zone.cpp index d310354ca867..50b6999e023d 100644 --- a/js/src/gc/Zone.cpp +++ b/js/src/gc/Zone.cpp @@ -104,33 +104,35 @@ Zone::onTooMuchMalloc() } void -Zone::sweep(FreeOp *fop, bool releaseTypes, bool *oom) +Zone::sweepAnalysis(FreeOp *fop, bool releaseTypes) { - /* - * Periodically release observed types for all scripts. This is safe to - * do when there are no frames for the zone on the stack. - */ + // Periodically release observed types for all scripts. This is safe to + // do when there are no frames for the zone on the stack. if (active) releaseTypes = false; - GCRuntime &gc = fop->runtime()->gc; + bool oom = false; + types.sweep(fop, releaseTypes, &oom); - { - gcstats::MaybeAutoPhase ap(gc.stats, !gc.isHeapCompacting(), - gcstats::PHASE_DISCARD_ANALYSIS); - types.sweep(fop, releaseTypes, oom); - } - - if (!fop->runtime()->debuggerList.isEmpty()) { - gcstats::MaybeAutoPhase ap2(gc.stats, !gc.isHeapCompacting(), - gcstats::PHASE_SWEEP_BREAKPOINT); - sweepBreakpoints(fop); + // If there was an OOM while sweeping types, the type information needs to + // be deoptimized so that it will still correct (i.e. overapproximates the + // possible types in the zone), but the constraints might not have been + // triggered on the deoptimization or even copied over completely. In this + // case, destroy all JIT code and new script information in the zone, the + // only things whose correctness depends on the type constraints. + if (oom) { + setPreservingCode(false); + discardJitCode(fop); + types.clearAllNewScriptsOnOOM(); } } void Zone::sweepBreakpoints(FreeOp *fop) { + if (fop->runtime()->debuggerList.isEmpty()) + return; + /* * Sweep all compartments in a zone at the same time, since there is no way * to iterate over the scripts belonging to a single compartment in a zone. diff --git a/js/src/gc/Zone.h b/js/src/gc/Zone.h index 2e3a624a005b..92005c4fe4dc 100644 --- a/js/src/gc/Zone.h +++ b/js/src/gc/Zone.h @@ -157,7 +157,7 @@ struct Zone : public JS::shadow::Zone, } void reportAllocationOverflow() { js_ReportAllocationOverflow(nullptr); } - void sweep(js::FreeOp *fop, bool releaseTypes, bool *oom); + void sweepAnalysis(js::FreeOp *fop, bool releaseTypes); bool hasMarkedCompartments(); diff --git a/js/src/jsgc.cpp b/js/src/jsgc.cpp index 676afbfa3db3..010f2c4ad0a0 100644 --- a/js/src/jsgc.cpp +++ b/js/src/jsgc.cpp @@ -4705,12 +4705,15 @@ GCRuntime::beginSweepingZoneGroup() c->sweepSavedStacks(); c->sweepGlobalObject(&fop); c->sweepSelfHostingScriptSource(); - c->sweepJitCompartment(&fop); c->sweepDebugScopes(); + c->sweepJitCompartment(&fop); c->sweepWeakMaps(); c->sweepNativeIterators(); } + // Bug 1071218: the following two methods have not yet been + // refactored to work on a single zone-group at once. + // Collect watch points associated with unreachable objects. WatchpointMap::sweepAll(rt); @@ -4726,22 +4729,18 @@ GCRuntime::beginSweepingZoneGroup() } { + gcstats::MaybeAutoPhase ap(stats, !isHeapCompacting(), + gcstats::PHASE_DISCARD_ANALYSIS); for (GCZoneGroupIter zone(rt); !zone.done(); zone.next()) { - // If there is an OOM while sweeping types, the type information - // will be deoptimized so that it is still correct (i.e. - // overapproximates the possible types in the zone), but the - // constraints might not have been triggered on the deoptimization - // or even copied over completely. In this case, destroy all JIT - // code and new script information in the zone, the only things - // whose correctness depends on the type constraints. - bool oom = false; - zone->sweep(&fop, releaseObservedTypes && !zone->isPreservingCode(), &oom); + zone->sweepAnalysis(&fop, releaseObservedTypes && !zone->isPreservingCode()); + } + } - if (oom) { - zone->setPreservingCode(false); - zone->discardJitCode(&fop); - zone->types.clearAllNewScriptsOnOOM(); - } + { + gcstats::MaybeAutoPhase ap(stats, !isHeapCompacting(), + gcstats::PHASE_SWEEP_BREAKPOINT); + for (GCZoneGroupIter zone(rt); !zone.done(); zone.next()) { + zone->sweepBreakpoints(&fop); } } } From fb8adde1a7ad524c438d96a2521d3d2e2cb40134 Mon Sep 17 00:00:00 2001 From: Terrence Cole Date: Tue, 23 Sep 2014 15:16:50 -0700 Subject: [PATCH 31/82] Bug 1064578 - Part 7: make MaybeAutoPhase a special case of AutoPhase; r=jonco --HG-- extra : rebase_source : 1072489364c163a665e88c76c561b33d2ffb3055 --- js/src/gc/Statistics.h | 37 ++++++++++++------------------------- js/src/gc/Tracer.cpp | 2 +- js/src/jsgc.cpp | 27 +++++++++------------------ js/src/jsinfer.cpp | 12 ++++++------ 4 files changed, 28 insertions(+), 50 deletions(-) diff --git a/js/src/gc/Statistics.h b/js/src/gc/Statistics.h index 43ee1a238a3a..373661abc355 100644 --- a/js/src/gc/Statistics.h +++ b/js/src/gc/Statistics.h @@ -234,40 +234,27 @@ struct AutoPhase { AutoPhase(Statistics &stats, Phase phase MOZ_GUARD_OBJECT_NOTIFIER_PARAM) - : stats(stats), phase(phase) + : stats(stats), phase(phase), enabled(true) { MOZ_GUARD_OBJECT_NOTIFIER_INIT; stats.beginPhase(phase); } + AutoPhase(Statistics &stats, bool condition, Phase phase + MOZ_GUARD_OBJECT_NOTIFIER_PARAM) + : stats(stats), phase(phase), enabled(condition) + { + MOZ_GUARD_OBJECT_NOTIFIER_INIT; + if (enabled) + stats.beginPhase(phase); + } ~AutoPhase() { - stats.endPhase(phase); + if (enabled) + stats.endPhase(phase); } Statistics &stats; Phase phase; - MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER -}; - -struct MaybeAutoPhase -{ - explicit MaybeAutoPhase(Statistics &statsArg, bool condition, Phase phaseArg - MOZ_GUARD_OBJECT_NOTIFIER_PARAM) - : stats(nullptr) - { - MOZ_GUARD_OBJECT_NOTIFIER_INIT; - if (condition) { - stats = &statsArg; - phase = phaseArg; - stats->beginPhase(phase); - } - } - ~MaybeAutoPhase() { - if (stats) - stats->endPhase(phase); - } - - Statistics *stats; - Phase phase; + bool enabled; MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER }; diff --git a/js/src/gc/Tracer.cpp b/js/src/gc/Tracer.cpp index 9e895305c0c3..e0b920909135 100644 --- a/js/src/gc/Tracer.cpp +++ b/js/src/gc/Tracer.cpp @@ -555,7 +555,7 @@ bool GCMarker::markDelayedChildren(SliceBudget &budget) { GCRuntime &gc = runtime()->gc; - gcstats::MaybeAutoPhase ap(gc.stats, gc.state() == MARK, gcstats::PHASE_MARK_DELAYED); + gcstats::AutoPhase ap(gc.stats, gc.state() == MARK, gcstats::PHASE_MARK_DELAYED); JS_ASSERT(unmarkedArenaStackTop); do { diff --git a/js/src/jsgc.cpp b/js/src/jsgc.cpp index 010f2c4ad0a0..bce7b332c061 100644 --- a/js/src/jsgc.cpp +++ b/js/src/jsgc.cpp @@ -4650,56 +4650,49 @@ GCRuntime::beginSweepingZoneGroup() gcstats::AutoSCC scc(stats, zoneGroupIndex); { - gcstats::MaybeAutoPhase apiv(stats, !isHeapCompacting(), - gcstats::PHASE_SWEEP_INNER_VIEWS); + gcstats::AutoPhase apiv(stats, gcstats::PHASE_SWEEP_INNER_VIEWS); for (GCCompartmentGroupIter c(rt); !c.done(); c.next()) { c->sweepInnerViews(); } } { - gcstats::MaybeAutoPhase apccw(stats, !isHeapCompacting(), - gcstats::PHASE_SWEEP_CC_WRAPPER); + gcstats::AutoPhase apccw(stats, gcstats::PHASE_SWEEP_CC_WRAPPER); for (GCCompartmentGroupIter c(rt); !c.done(); c.next()) { c->sweepCrossCompartmentWrappers(); } } { - gcstats::MaybeAutoPhase apbs(stats, !isHeapCompacting(), - gcstats::PHASE_SWEEP_BASE_SHAPE); + gcstats::AutoPhase apbs(stats, gcstats::PHASE_SWEEP_BASE_SHAPE); for (GCCompartmentGroupIter c(rt); !c.done(); c.next()) { c->sweepBaseShapeTable(); } } { - gcstats::MaybeAutoPhase apis(stats, !isHeapCompacting(), - gcstats::PHASE_SWEEP_INITIAL_SHAPE); + gcstats::AutoPhase apis(stats, gcstats::PHASE_SWEEP_INITIAL_SHAPE); for (GCCompartmentGroupIter c(rt); !c.done(); c.next()) { c->sweepInitialShapeTable(); } } { - gcstats::MaybeAutoPhase apto(stats, !isHeapCompacting(), - gcstats::PHASE_SWEEP_TYPE_OBJECT); + gcstats::AutoPhase apto(stats, gcstats::PHASE_SWEEP_TYPE_OBJECT); for (GCCompartmentGroupIter c(rt); !c.done(); c.next()) { c->sweepTypeObjectTables(); } } { - gcstats::MaybeAutoPhase apre(stats, !isHeapCompacting(), - gcstats::PHASE_SWEEP_REGEXP); + gcstats::AutoPhase apre(stats, gcstats::PHASE_SWEEP_REGEXP); for (GCCompartmentGroupIter c(rt); !c.done(); c.next()) { c->sweepRegExps(); } } { - gcstats::MaybeAutoPhase apmisc(stats, !isHeapCompacting(), - gcstats::PHASE_SWEEP_MISC); + gcstats::AutoPhase apmisc(stats, gcstats::PHASE_SWEEP_MISC); for (GCCompartmentGroupIter c(rt); !c.done(); c.next()) { c->sweepCallsiteClones(); c->sweepSavedStacks(); @@ -4729,16 +4722,14 @@ GCRuntime::beginSweepingZoneGroup() } { - gcstats::MaybeAutoPhase ap(stats, !isHeapCompacting(), - gcstats::PHASE_DISCARD_ANALYSIS); + gcstats::AutoPhase ap(stats, gcstats::PHASE_DISCARD_ANALYSIS); for (GCZoneGroupIter zone(rt); !zone.done(); zone.next()) { zone->sweepAnalysis(&fop, releaseObservedTypes && !zone->isPreservingCode()); } } { - gcstats::MaybeAutoPhase ap(stats, !isHeapCompacting(), - gcstats::PHASE_SWEEP_BREAKPOINT); + gcstats::AutoPhase ap(stats, gcstats::PHASE_SWEEP_BREAKPOINT); for (GCZoneGroupIter zone(rt); !zone.done(); zone.next()) { zone->sweepBreakpoints(&fop); } diff --git a/js/src/jsinfer.cpp b/js/src/jsinfer.cpp index 5ceab613e0c2..f22f44bbb3ea 100644 --- a/js/src/jsinfer.cpp +++ b/js/src/jsinfer.cpp @@ -5021,8 +5021,8 @@ TypeZone::sweep(FreeOp *fop, bool releaseTypes, bool *oom) } { - gcstats::MaybeAutoPhase ap2(rt->gc.stats, !rt->isHeapCompacting(), - gcstats::PHASE_DISCARD_TI); + gcstats::AutoPhase ap2(rt->gc.stats, !rt->isHeapCompacting(), + gcstats::PHASE_DISCARD_TI); for (ZoneCellIterUnderGC i(zone(), FINALIZE_SCRIPT); !i.done(); i.next()) { JSScript *script = i.get(); @@ -5053,8 +5053,8 @@ TypeZone::sweep(FreeOp *fop, bool releaseTypes, bool *oom) } { - gcstats::MaybeAutoPhase ap2(rt->gc.stats, !rt->isHeapCompacting(), - gcstats::PHASE_SWEEP_TYPES); + gcstats::AutoPhase ap2(rt->gc.stats, !rt->isHeapCompacting(), + gcstats::PHASE_SWEEP_TYPES); for (gc::ZoneCellIterUnderGC iter(zone(), gc::FINALIZE_TYPE_OBJECT); !iter.done(); iter.next()) @@ -5082,8 +5082,8 @@ TypeZone::sweep(FreeOp *fop, bool releaseTypes, bool *oom) } { - gcstats::MaybeAutoPhase ap2(rt->gc.stats, !rt->isHeapCompacting(), - gcstats::PHASE_FREE_TI_ARENA); + gcstats::AutoPhase ap2(rt->gc.stats, !rt->isHeapCompacting(), + gcstats::PHASE_FREE_TI_ARENA); rt->freeLifoAlloc.transferFrom(&oldAlloc); } } From 6d3904cd352293c8a7d8840ee17ee797f30983ef Mon Sep 17 00:00:00 2001 From: Ben Turner Date: Mon, 29 Sep 2014 14:36:13 -0700 Subject: [PATCH 32/82] Bug 1074418, Guard against talos using IndexedDB after shutdown has started, r=khuey. --- dom/indexedDB/ActorsParent.cpp | 137 ++++++++++++++++++++++++--------- 1 file changed, 99 insertions(+), 38 deletions(-) diff --git a/dom/indexedDB/ActorsParent.cpp b/dom/indexedDB/ActorsParent.cpp index 262e32a019d1..f0f31e51cd2c 100644 --- a/dom/indexedDB/ActorsParent.cpp +++ b/dom/indexedDB/ActorsParent.cpp @@ -3910,20 +3910,7 @@ protected: FactoryOp(Factory* aFactory, already_AddRefed aContentParent, const CommonFactoryRequestParams& aCommonParams, - bool aDeleting) - : mFactory(aFactory) - , mContentParent(Move(aContentParent)) - , mCommonParams(aCommonParams) - , mState(State_Initial) - , mStoragePrivilege(mozilla::dom::quota::Content) - , mEnforcingQuota(true) - , mDeleting(aDeleting) - , mBlockedQuotaManager(false) - , mChromeWriteAccessAllowed(false) - { - AssertIsOnBackgroundThread(); - MOZ_ASSERT(aFactory); - } + bool aDeleting); virtual ~FactoryOp() @@ -5075,20 +5062,42 @@ public: static QuotaClient* GetInstance() { + MOZ_ASSERT(NS_IsMainThread()); + return sInstance; } - void - NoteBackgroundThread(nsIEventTarget* aBackgroundThread); + static bool + IsShuttingDownOnMainThread() + { + MOZ_ASSERT(NS_IsMainThread()); + + if (sInstance) { + return sInstance->mShutdownRequested; + } + + return QuotaManager::IsShuttingDown(); + } + + static bool + IsShuttingDownOnNonMainThread() + { + MOZ_ASSERT(!NS_IsMainThread()); + + return QuotaManager::IsShuttingDown(); + } bool - HasShutDown() const + IsShuttingDown() const { MOZ_ASSERT(NS_IsMainThread()); return mShutdownRequested; } + void + NoteBackgroundThread(nsIEventTarget* aBackgroundThread); + NS_INLINE_DECL_REFCOUNTING(QuotaClient) virtual mozilla::dom::quota::Client::Type @@ -5557,6 +5566,10 @@ AllocPBackgroundIDBFactoryParent(PBackgroundParent* aManager, } } + if (NS_WARN_IF(QuotaClient::IsShuttingDownOnNonMainThread())) { + return nullptr; + } + nsRefPtr actor = Factory::Create(aOptionalWindowId); return actor.forget().take(); } @@ -5568,6 +5581,7 @@ RecvPBackgroundIDBFactoryConstructor(PBackgroundParent* /* aManager */, { AssertIsOnBackgroundThread(); MOZ_ASSERT(aActor); + MOZ_ASSERT(!QuotaClient::IsShuttingDownOnNonMainThread()); return true; } @@ -5751,6 +5765,7 @@ Factory::Factory(const OptionalWindowId& aOptionalWindowId) , mActorDestroyed(false) { AssertIsOnBackgroundThread(); + MOZ_ASSERT(!QuotaClient::IsShuttingDownOnNonMainThread()); } Factory::~Factory() @@ -5763,6 +5778,7 @@ already_AddRefed Factory::Create(const OptionalWindowId& aOptionalWindowId) { AssertIsOnBackgroundThread(); + MOZ_ASSERT(!QuotaClient::IsShuttingDownOnNonMainThread()); // If this is the first instance then we need to do some initialization. if (!sFactoryInstanceCount) { @@ -5872,6 +5888,10 @@ Factory::AllocPBackgroundIDBFactoryRequestParent( AssertIsOnBackgroundThread(); MOZ_ASSERT(aParams.type() != FactoryRequestParams::T__None); + if (NS_WARN_IF(QuotaClient::IsShuttingDownOnNonMainThread())) { + return nullptr; + } + const CommonFactoryRequestParams* commonParams; switch (aParams.type()) { @@ -5933,6 +5953,7 @@ Factory::RecvPBackgroundIDBFactoryRequestConstructor( AssertIsOnBackgroundThread(); MOZ_ASSERT(aActor); MOZ_ASSERT(aParams.type() != FactoryRequestParams::T__None); + MOZ_ASSERT(!QuotaClient::IsShuttingDownOnNonMainThread()); auto* op = static_cast(aActor); @@ -10304,6 +10325,25 @@ AutoSetProgressHandler::Register( return NS_OK; } +FactoryOp::FactoryOp(Factory* aFactory, + already_AddRefed aContentParent, + const CommonFactoryRequestParams& aCommonParams, + bool aDeleting) + : mFactory(aFactory) + , mContentParent(Move(aContentParent)) + , mCommonParams(aCommonParams) + , mState(State_Initial) + , mStoragePrivilege(mozilla::dom::quota::Content) + , mEnforcingQuota(true) + , mDeleting(aDeleting) + , mBlockedQuotaManager(false) + , mChromeWriteAccessAllowed(false) +{ + AssertIsOnBackgroundThread(); + MOZ_ASSERT(aFactory); + MOZ_ASSERT(!QuotaClient::IsShuttingDownOnNonMainThread()); +} + nsresult FactoryOp::Open() { @@ -10314,7 +10354,8 @@ FactoryOp::Open() nsRefPtr contentParent; mContentParent.swap(contentParent); - if (!OperationMayProceed()) { + if (NS_WARN_IF(QuotaClient::IsShuttingDownOnMainThread()) || + !OperationMayProceed()) { IDB_REPORT_INTERNAL_ERR(); return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; } @@ -10392,7 +10433,8 @@ FactoryOp::RetryCheckPermission() nsRefPtr contentParent; mContentParent.swap(contentParent); - if (!OperationMayProceed()) { + if (NS_WARN_IF(QuotaClient::IsShuttingDownOnMainThread()) || + !OperationMayProceed()) { IDB_REPORT_INTERNAL_ERR(); return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; } @@ -10428,16 +10470,14 @@ FactoryOp::SendToIOThread() MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(mState == State_OpenPending); - if (!OperationMayProceed()) { + if (NS_WARN_IF(QuotaClient::IsShuttingDownOnMainThread()) || + !OperationMayProceed()) { IDB_REPORT_INTERNAL_ERR(); return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; } QuotaManager* quotaManager = QuotaManager::Get(); - if (NS_WARN_IF(!quotaManager)) { - IDB_REPORT_INTERNAL_ERR(); - return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; - } + MOZ_ASSERT(quotaManager); // Must set this before dispatching otherwise we will race with the IO thread. mState = State_DatabaseWorkOpen; @@ -10819,6 +10859,7 @@ FactoryOp::FinishOpen() MOZ_ASSERT(!mDatabaseId.IsEmpty()); MOZ_ASSERT(!mBlockedQuotaManager); MOZ_ASSERT(!mContentParent); + MOZ_ASSERT(!QuotaClient::IsShuttingDownOnMainThread()); PersistenceType persistenceType = mCommonParams.metadata().persistenceType(); @@ -10830,10 +10871,8 @@ FactoryOp::FinishOpen() return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; } - QuotaManager* quotaManager; - - if (QuotaManager::IsShuttingDown() || - !(quotaManager = QuotaManager::GetOrCreate())) { + QuotaManager* quotaManager = QuotaManager::GetOrCreate(); + if (NS_WARN_IF(!quotaManager)) { IDB_REPORT_INTERNAL_ERR(); return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; } @@ -11002,9 +11041,8 @@ OpenDatabaseOp::QuotaManagerOpen() MOZ_ASSERT(!mOfflineStorage); QuotaClient* quotaClient = QuotaClient::GetInstance(); - MOZ_ASSERT(quotaClient); - - if (NS_WARN_IF(quotaClient->HasShutDown())) { + if (NS_WARN_IF(!quotaClient) || + NS_WARN_IF(quotaClient->IsShuttingDown())) { IDB_REPORT_INTERNAL_ERR(); return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; } @@ -11051,7 +11089,8 @@ OpenDatabaseOp::DoDatabaseWork() "OpenDatabaseHelper::DoDatabaseWork", js::ProfileEntry::Category::STORAGE); - if (NS_WARN_IF(QuotaManager::IsShuttingDown()) || !OperationMayProceed()) { + if (NS_WARN_IF(QuotaClient::IsShuttingDownOnNonMainThread()) || + !OperationMayProceed()) { IDB_REPORT_INTERNAL_ERR(); return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; } @@ -11502,7 +11541,9 @@ OpenDatabaseOp::BeginVersionChange() MOZ_ASSERT(!mDatabase); MOZ_ASSERT(!mVersionChangeTransaction); - if (IsActorDestroyed()) { + if (NS_WARN_IF(QuotaClient::IsShuttingDownOnNonMainThread()) || + !OperationMayProceed() || + IsActorDestroyed()) { IDB_REPORT_INTERNAL_ERR(); return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; } @@ -11658,7 +11699,9 @@ OpenDatabaseOp::SendUpgradeNeeded() MOZ_ASSERT(NS_SUCCEEDED(mResultCode)); MOZ_ASSERT_IF(!IsActorDestroyed(), mDatabase); - if (IsActorDestroyed()) { + if (NS_WARN_IF(QuotaClient::IsShuttingDownOnNonMainThread()) || + !OperationMayProceed() || + IsActorDestroyed()) { IDB_REPORT_INTERNAL_ERR(); return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; } @@ -12066,6 +12109,12 @@ VersionChangeOp::DoDatabaseWork(TransactionBase* aTransaction) aTransaction->AssertIsOnTransactionThread(); MOZ_ASSERT(mOpenDatabaseOp->mState == State_DatabaseWorkVersionChange); + if (NS_WARN_IF(QuotaClient::IsShuttingDownOnNonMainThread()) || + !OperationMayProceed()) { + IDB_REPORT_INTERNAL_ERR(); + return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + } + PROFILER_LABEL("IndexedDB", "VersionChangeOp::DoDatabaseWork", js::ProfileEntry::Category::STORAGE); @@ -12267,7 +12316,8 @@ DeleteDatabaseOp::DoDatabaseWork() "DeleteDatabaseOp::DoDatabaseWork", js::ProfileEntry::Category::STORAGE); - if (!OperationMayProceed()) { + if (NS_WARN_IF(QuotaClient::IsShuttingDownOnNonMainThread()) || + !OperationMayProceed()) { IDB_REPORT_INTERNAL_ERR(); return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; } @@ -12343,7 +12393,9 @@ DeleteDatabaseOp::BeginVersionChange() MOZ_ASSERT(mState == State_BeginVersionChange); MOZ_ASSERT(mMaybeBlockedDatabases.IsEmpty()); - if (IsActorDestroyed()) { + if (NS_WARN_IF(QuotaClient::IsShuttingDownOnNonMainThread()) || + !OperationMayProceed() || + IsActorDestroyed()) { IDB_REPORT_INTERNAL_ERR(); return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; } @@ -12381,7 +12433,9 @@ DeleteDatabaseOp::DispatchToWorkThread() MOZ_ASSERT(mState == State_WaitingForTransactionsToComplete); MOZ_ASSERT(mMaybeBlockedDatabases.IsEmpty()); - if (IsActorDestroyed()) { + if (NS_WARN_IF(QuotaClient::IsShuttingDownOnNonMainThread()) || + !OperationMayProceed() || + IsActorDestroyed()) { IDB_REPORT_INTERNAL_ERR(); return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; } @@ -12474,6 +12528,12 @@ VersionChangeOp::RunOnMainThread() MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(mDeleteDatabaseOp->mState == State_DatabaseWorkVersionChange); + if (NS_WARN_IF(QuotaClient::IsShuttingDownOnMainThread()) || + !OperationMayProceed()) { + IDB_REPORT_INTERNAL_ERR(); + return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + } + QuotaManager* quotaManager = QuotaManager::Get(); MOZ_ASSERT(quotaManager); @@ -12497,7 +12557,8 @@ VersionChangeOp::RunOnIOThread() "DeleteDatabaseOp::VersionChangeOp::RunOnIOThread", js::ProfileEntry::Category::STORAGE); - if (!OperationMayProceed()) { + if (NS_WARN_IF(QuotaClient::IsShuttingDownOnNonMainThread()) || + !OperationMayProceed()) { IDB_REPORT_INTERNAL_ERR(); return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; } From 0c802f536ab35198b725e8247d98847c0a2af10b Mon Sep 17 00:00:00 2001 From: Jim Mathies Date: Mon, 29 Sep 2014 16:51:55 -0500 Subject: [PATCH 33/82] Bug 1074299 - Fix navbar shortcut dragging in e10s. r=gavin --- browser/base/content/browser.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/browser/base/content/browser.js b/browser/base/content/browser.js index ed30af87a583..fa600882d507 100644 --- a/browser/base/content/browser.js +++ b/browser/base/content/browser.js @@ -6792,11 +6792,11 @@ var gIdentityHandler = { if (gURLBar.getAttribute("pageproxystate") != "valid") return; - var value = content.location.href; - var urlString = value + "\n" + content.document.title; - var htmlString = "" + value + ""; + let value = gBrowser.currentURI.spec; + let urlString = value + "\n" + gBrowser.contentTitle; + let htmlString = "" + value + ""; - var dt = event.dataTransfer; + let dt = event.dataTransfer; dt.setData("text/x-moz-url", urlString); dt.setData("text/uri-list", value); dt.setData("text/plain", value); From af9be8e2a8971b0d9cd2f81a56226ee98c27524e Mon Sep 17 00:00:00 2001 From: Jim Mathies Date: Mon, 29 Sep 2014 16:11:08 -0500 Subject: [PATCH 34/82] Bug 1072417 - Remove the browser.tabs.remote pref and turn everything it controls on by default. r=felipe --- browser/app/profile/firefox.js | 11 +--- browser/base/content/browser.js | 16 +++--- browser/base/content/tabbrowser.xml | 1 - .../customizableui/CustomizableWidgets.jsm | 54 +++++++++---------- .../components/customizableui/test/head.js | 5 ++ browser/metro/base/content/browser.js | 2 +- browser/metro/profile/metro.js | 2 - content/base/test/chrome/test_cpows.xul | 5 +- dom/ipc/ContentParent.cpp | 3 -- dom/ipc/TabChild.cpp | 19 ++----- layout/forms/nsListControlFrame.cpp | 3 +- layout/tools/reftest/b2g_desktop.py | 1 - layout/tools/reftest/runreftestb2g.py | 2 - mobile/android/app/mobile.js | 2 - testing/mochitest/runtests.py | 1 - testing/testsuite-targets.mk | 4 +- .../components/satchel/nsFormAutoComplete.js | 4 +- toolkit/xre/nsAppRunner.cpp | 20 ------- widget/android/nsWindow.cpp | 4 +- xpcom/system/nsIXULRuntime.idl | 10 +--- 20 files changed, 54 insertions(+), 115 deletions(-) diff --git a/browser/app/profile/firefox.js b/browser/app/profile/firefox.js index bd9625ec606a..9c1b5a006c07 100644 --- a/browser/app/profile/firefox.js +++ b/browser/app/profile/firefox.js @@ -1177,16 +1177,9 @@ pref("dom.ipc.plugins.enabled.x86_64", true); pref("dom.ipc.plugins.enabled", true); #endif -#if defined(NIGHTLY_BUILD) -// browser.tabs.remote is enabled on nightly. However, users won't -// actually get remote tabs unless they enable -// browser.tabs.remote.autostart or they use the "New OOP Window" menu -// option. -pref("browser.tabs.remote", true); -#else -pref("browser.tabs.remote", false); -#endif +// Start the browser in e10s mode pref("browser.tabs.remote.autostart", false); +pref("browser.tabs.remote.desktopbehavior", true); #if defined(MOZ_CONTENT_SANDBOX) && defined(XP_WIN) // This controls whether the content process on Windows is sandboxed. diff --git a/browser/base/content/browser.js b/browser/base/content/browser.js index fa600882d507..22238093c033 100644 --- a/browser/base/content/browser.js +++ b/browser/base/content/browser.js @@ -6952,20 +6952,16 @@ let gRemoteTabsUI = { return; } - let remoteTabs = Services.appinfo.browserTabsRemote; - let autostart = Services.appinfo.browserTabsRemoteAutostart; - let newRemoteWindow = document.getElementById("menu_newRemoteWindow"); let newNonRemoteWindow = document.getElementById("menu_newNonRemoteWindow"); - - if (!remoteTabs) { - newRemoteWindow.hidden = true; - newNonRemoteWindow.hidden = true; - return; - } - +#ifdef E10S_TESTING_ONLY + let autostart = Services.appinfo.browserTabsRemoteAutostart; newRemoteWindow.hidden = autostart; newNonRemoteWindow.hidden = !autostart; +#else + newRemoteWindow.hidden = true; + newNonRemoteWindow.hidden = true; +#endif } }; diff --git a/browser/base/content/tabbrowser.xml b/browser/base/content/tabbrowser.xml index a937bdc860ce..660fcf22aec5 100644 --- a/browser/base/content/tabbrowser.xml +++ b/browser/base/content/tabbrowser.xml @@ -2563,7 +2563,6 @@ ]]> -#endif diff --git a/browser/components/customizableui/CustomizableWidgets.jsm b/browser/components/customizableui/CustomizableWidgets.jsm index 56b60c45445a..5cf80c170c81 100644 --- a/browser/components/customizableui/CustomizableWidgets.jsm +++ b/browser/components/customizableui/CustomizableWidgets.jsm @@ -1043,33 +1043,31 @@ if (Services.prefs.getBoolPref("privacy.panicButton.enabled")) { #ifdef E10S_TESTING_ONLY /** - * The e10s button's purpose is to lower the barrier of entry - * for our Nightly testers to use e10s windows. We'll be removing it - * once remote tabs are enabled. This button should never ever make it - * to production. If it does, that'd be bad, and we should all feel bad. - */ -if (Services.prefs.getBoolPref("browser.tabs.remote")) { - let getCommandFunction = function(aOpenRemote) { - return function(aEvent) { - let win = aEvent.view; - if (win && typeof win.OpenBrowserWindow == "function") { - win.OpenBrowserWindow({remote: aOpenRemote}); - } - }; - } - - let openRemote = !Services.appinfo.browserTabsRemoteAutostart; - // Like the XUL menuitem counterparts, we hard-code these strings in because - // this button should never roll into production. - let buttonLabel = openRemote ? "New e10s Window" - : "New Non-e10s Window"; - - CustomizableWidgets.push({ - id: "e10s-button", - label: buttonLabel, - tooltiptext: buttonLabel, - defaultArea: CustomizableUI.AREA_PANEL, - onCommand: getCommandFunction(openRemote), - }); + * The e10s button's purpose is to lower the barrier of entry + * for our Nightly testers to use e10s windows. We'll be removing it + * once remote tabs are enabled. This button should never ever make it + * to production. If it does, that'd be bad, and we should all feel bad. + */ +let getCommandFunction = function(aOpenRemote) { + return function(aEvent) { + let win = aEvent.view; + if (win && typeof win.OpenBrowserWindow == "function") { + win.OpenBrowserWindow({remote: aOpenRemote}); + } + }; } + +let openRemote = !Services.appinfo.browserTabsRemoteAutostart; +// Like the XUL menuitem counterparts, we hard-code these strings in because +// this button should never roll into production. +let buttonLabel = openRemote ? "New e10s Window" + : "New Non-e10s Window"; + +CustomizableWidgets.push({ + id: "e10s-button", + label: buttonLabel, + tooltiptext: buttonLabel, + defaultArea: CustomizableUI.AREA_PANEL, + onCommand: getCommandFunction(openRemote), +}); #endif diff --git a/browser/components/customizableui/test/head.js b/browser/components/customizableui/test/head.js index 01b7d6cf388a..63e53e03cc5c 100644 --- a/browser/components/customizableui/test/head.js +++ b/browser/components/customizableui/test/head.js @@ -16,6 +16,11 @@ Services.scriptloader.loadSubScript("chrome://mochikit/content/tests/SimpleTest/ Services.prefs.setBoolPref("browser.uiCustomization.skipSourceNodeCheck", true); registerCleanupFunction(() => Services.prefs.clearUserPref("browser.uiCustomization.skipSourceNodeCheck")); +// Remove temporary e10s related new window options in customize ui, +// they break a lot of tests. +CustomizableUI.destroyWidget("e10s-button"); +CustomizableUI.removeWidgetFromArea("e10s-button"); + let {synthesizeDragStart, synthesizeDrop} = ChromeUtils; const kNSXUL = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"; diff --git a/browser/metro/base/content/browser.js b/browser/metro/base/content/browser.js index ad6f9297fab5..0382d4e9707b 100644 --- a/browser/metro/base/content/browser.js +++ b/browser/metro/base/content/browser.js @@ -1451,7 +1451,7 @@ Tab.prototype = { browser.setAttribute("type", "content-targetable"); - let useRemote = Services.appinfo.browserTabsRemote; + let useRemote = Services.appinfo.browserTabsRemoteAutostart; let useLocal = Util.isLocalScheme(aURI); browser.setAttribute("remote", (!useLocal && useRemote) ? "true" : "false"); diff --git a/browser/metro/profile/metro.js b/browser/metro/profile/metro.js index 8a08e48aabc2..836b6496ecef 100644 --- a/browser/metro/profile/metro.js +++ b/browser/metro/profile/metro.js @@ -94,8 +94,6 @@ pref("toolkit.browser.contentViewExpire", 3000); pref("toolkit.defaultChromeURI", "chrome://browser/content/browser.xul"); pref("browser.chromeURL", "chrome://browser/content/"); -pref("browser.tabs.remote", false); - // Telemetry #ifdef MOZ_TELEMETRY_ON_BY_DEFAULT pref("toolkit.telemetry.enabledPreRelease", true); diff --git a/content/base/test/chrome/test_cpows.xul b/content/base/test/chrome/test_cpows.xul index f47f0c57534b..0f10f509439a 100644 --- a/content/base/test/chrome/test_cpows.xul +++ b/content/base/test/chrome/test_cpows.xul @@ -21,10 +21,7 @@ } addLoadEvent(function() { - // We don't want to set browser.tabs.remote to true, but still have CPOWs enabled. - SpecialPowers.pushPrefEnv({"set": [["dom.ipc.cpows.force-enabled", true]]}, function() { - window.open("cpows_parent.xul", "", "chrome"); - }); + window.open("cpows_parent.xul", "", "chrome"); }); ]]> diff --git a/dom/ipc/ContentParent.cpp b/dom/ipc/ContentParent.cpp index d57d3fc8c0bf..4b38a0ba065e 100755 --- a/dom/ipc/ContentParent.cpp +++ b/dom/ipc/ContentParent.cpp @@ -3852,9 +3852,6 @@ ContentParent::RecvKeywordToURI(const nsCString& aKeyword, OptionalInputStreamPa bool ContentParent::ShouldContinueFromReplyTimeout() { - // The only time ContentParent sends blocking messages is for CPOWs, so - // timeouts should only ever occur in electrolysis-enabled sessions. - MOZ_ASSERT(BrowserTabsRemote()); return false; } diff --git a/dom/ipc/TabChild.cpp b/dom/ipc/TabChild.cpp index cb9fa1d98dbd..c5919a32d3cc 100644 --- a/dom/ipc/TabChild.cpp +++ b/dom/ipc/TabChild.cpp @@ -106,7 +106,6 @@ static const CSSSize kDefaultViewportSize(980, 480); static const char BROWSER_ZOOM_TO_RECT[] = "browser-zoom-to-rect"; static const char BEFORE_FIRST_PAINT[] = "before-first-paint"; -static bool sCpowsEnabled = false; static int32_t sActiveDurationMs = 10; static bool sActiveDurationMsSet = false; @@ -2783,12 +2782,6 @@ TabChild::InitRenderingState() BEFORE_FIRST_PAINT, false); } - - // This state can't change during the lifetime of the child. - sCpowsEnabled = BrowserTabsRemote(); - if (Preferences::GetBool("dom.ipc.cpows.force-enabled", false)) - sCpowsEnabled = true; - return true; } @@ -2917,10 +2910,8 @@ TabChild::DoSendBlockingMessage(JSContext* aCx, return false; } InfallibleTArray cpows; - if (sCpowsEnabled) { - if (!Manager()->GetCPOWManager()->Wrap(aCx, aCpows, &cpows)) { - return false; - } + if (!Manager()->GetCPOWManager()->Wrap(aCx, aCpows, &cpows)) { + return false; } if (aIsSync) { return SendSyncMessage(PromiseFlatString(aMessage), data, cpows, @@ -2943,10 +2934,8 @@ TabChild::DoSendAsyncMessage(JSContext* aCx, return false; } InfallibleTArray cpows; - if (sCpowsEnabled) { - if (!Manager()->GetCPOWManager()->Wrap(aCx, aCpows, &cpows)) { - return false; - } + if (!Manager()->GetCPOWManager()->Wrap(aCx, aCpows, &cpows)) { + return false; } return SendAsyncMessage(PromiseFlatString(aMessage), data, cpows, Principal(aPrincipal)); diff --git a/layout/forms/nsListControlFrame.cpp b/layout/forms/nsListControlFrame.cpp index 9da045a36efc..6f5c79a0e6c8 100644 --- a/layout/forms/nsListControlFrame.cpp +++ b/layout/forms/nsListControlFrame.cpp @@ -1803,7 +1803,8 @@ nsListControlFrame::MouseDown(nsIDOMEvent* aMouseEvent) } else { // NOTE: the combo box is responsible for dropping it down if (mComboboxFrame) { - if (XRE_GetProcessType() == GeckoProcessType_Content && BrowserTabsRemote()) { + if (XRE_GetProcessType() == GeckoProcessType_Content && + Preferences::GetBool("browser.tabs.remote.desktopbehavior", false)) { nsContentUtils::DispatchChromeEvent(mContent->OwnerDoc(), mContent, NS_LITERAL_STRING("mozshowdropdown"), true, false); diff --git a/layout/tools/reftest/b2g_desktop.py b/layout/tools/reftest/b2g_desktop.py index 0bda13c500a6..5323c036066a 100644 --- a/layout/tools/reftest/b2g_desktop.py +++ b/layout/tools/reftest/b2g_desktop.py @@ -110,7 +110,6 @@ class B2GDesktopReftest(RefTest): prefs["browser.firstrun.show.localepicker"] = False prefs["b2g.system_startup_url"] = "app://test-container.gaiamobile.org/index.html" prefs["b2g.system_manifest_url"] = "app://test-container.gaiamobile.org/manifest.webapp" - prefs["browser.tabs.remote"] = False prefs["dom.ipc.tabs.disabled"] = False prefs["dom.mozBrowserFramesEnabled"] = True prefs["font.size.inflation.emPerLine"] = 0 diff --git a/layout/tools/reftest/runreftestb2g.py b/layout/tools/reftest/runreftestb2g.py index 23551ad65a9d..05a2678c3617 100644 --- a/layout/tools/reftest/runreftestb2g.py +++ b/layout/tools/reftest/runreftestb2g.py @@ -425,7 +425,6 @@ class B2GRemoteReftest(RefTest): prefs["browser.firstrun.show.localepicker"] = False prefs["b2g.system_startup_url"] = "app://test-container.gaiamobile.org/index.html" prefs["b2g.system_manifest_url"] = "app://test-container.gaiamobile.org/manifest.webapp" - prefs["browser.tabs.remote"] = False prefs["dom.ipc.tabs.disabled"] = False prefs["dom.mozBrowserFramesEnabled"] = True prefs["font.size.inflation.emPerLine"] = 0 @@ -439,7 +438,6 @@ class B2GRemoteReftest(RefTest): prefs["toolkit.telemetry.notifiedOptOut"] = 999 if options.oop: - prefs['browser.tabs.remote'] = True prefs['browser.tabs.remote.autostart'] = True prefs['reftest.browser.iframe.enabled'] = True diff --git a/mobile/android/app/mobile.js b/mobile/android/app/mobile.js index a90df8731d8a..34f09fb48088 100644 --- a/mobile/android/app/mobile.js +++ b/mobile/android/app/mobile.js @@ -26,8 +26,6 @@ pref("toolkit.browser.contentViewExpire", 3000); pref("toolkit.defaultChromeURI", "chrome://browser/content/browser.xul"); pref("browser.chromeURL", "chrome://browser/content/"); -pref("browser.tabs.remote", false); - // If a tab has not been active for this long (seconds), then it may be // turned into a zombie tab to preemptively free up memory. -1 disables time-based // expiration (but low-memory conditions may still require the tab to be zombified). diff --git a/testing/mochitest/runtests.py b/testing/mochitest/runtests.py index b370bc8c33c5..f9bd5fb5c33f 100644 --- a/testing/mochitest/runtests.py +++ b/testing/mochitest/runtests.py @@ -1128,7 +1128,6 @@ class Mochitest(MochitestUtilsMixin): """ create the profile and add optional chrome bits and files if requested """ if options.browserChrome and options.timeout: options.extraPrefs.append("testing.browserTestHarness.timeout=%d" % options.timeout) - options.extraPrefs.append("browser.tabs.remote=%s" % ('true' if options.e10s else 'false')) options.extraPrefs.append("browser.tabs.remote.autostart=%s" % ('true' if options.e10s else 'false')) options.extraPrefs.append("browser.tabs.remote.sandbox=%s" % options.contentSandbox) diff --git a/testing/testsuite-targets.mk b/testing/testsuite-targets.mk index 7c98e9b12a65..af5cfec7d7b1 100644 --- a/testing/testsuite-targets.mk +++ b/testing/testsuite-targets.mk @@ -206,10 +206,10 @@ RUN_REFTEST_B2G = rm -f ./$@.log && $(PYTHON) _tests/reftest/runreftestb2g.py \ ifeq ($(OS_ARCH),WINNT) #{ # GPU-rendered shadow layers are unsupported here -OOP_CONTENT = --setpref=layers.async-pan-zoom.enabled=true --setpref=browser.tabs.remote=true --setpref=browser.tabs.remote.autostart=true --setpref=layers.acceleration.disabled=true +OOP_CONTENT = --setpref=layers.async-pan-zoom.enabled=true --setpref=browser.tabs.remote.autostart=true --setpref=layers.acceleration.disabled=true GPU_RENDERING = else -OOP_CONTENT = --setpref=layers.async-pan-zoom.enabled=true --setpref=browser.tabs.remote=true --setpref=browser.tabs.remote.autostart=true +OOP_CONTENT = --setpref=layers.async-pan-zoom.enabled=true --setpref=browser.tabs.remote.autostart=true GPU_RENDERING = --setpref=layers.acceleration.force-enabled=true endif #} diff --git a/toolkit/components/satchel/nsFormAutoComplete.js b/toolkit/components/satchel/nsFormAutoComplete.js index 8586ed5b7c26..3a23073da853 100644 --- a/toolkit/components/satchel/nsFormAutoComplete.js +++ b/toolkit/components/satchel/nsFormAutoComplete.js @@ -494,8 +494,8 @@ FormAutoCompleteResult.prototype = { }; -let remote = Services.appinfo.browserTabsRemote; -if (Services.appinfo.processType == Services.appinfo.PROCESS_TYPE_CONTENT && remote) { +if (Services.appinfo.processType == Services.appinfo.PROCESS_TYPE_CONTENT && + Services.prefs.getBoolPref("browser.tabs.remote.desktopbehavior", false)) { // Register the stub FormAutoComplete module in the child which will // forward messages to the parent through the process message manager. let component = [FormAutoCompleteChild]; diff --git a/toolkit/xre/nsAppRunner.cpp b/toolkit/xre/nsAppRunner.cpp index 188ec66cce85..6b1d6a042285 100644 --- a/toolkit/xre/nsAppRunner.cpp +++ b/toolkit/xre/nsAppRunner.cpp @@ -841,16 +841,7 @@ nsXULAppInfo::GetProcessID(uint32_t* aResult) return NS_OK; } -static bool gBrowserTabsRemote = false; static bool gBrowserTabsRemoteInitialized = false; - -NS_IMETHODIMP -nsXULAppInfo::GetBrowserTabsRemote(bool* aResult) -{ - *aResult = BrowserTabsRemote(); - return NS_OK; -} - static bool gBrowserTabsRemoteAutostart = false; static bool gBrowserTabsRemoteAutostartInitialized = false; @@ -4565,17 +4556,6 @@ XRE_GetProcessType() return mozilla::startup::sChildProcessType; } -bool -mozilla::BrowserTabsRemote() -{ - if (!gBrowserTabsRemoteInitialized) { - gBrowserTabsRemote = Preferences::GetBool("browser.tabs.remote", false); - gBrowserTabsRemoteInitialized = true; - } - - return gBrowserTabsRemote; -} - bool mozilla::BrowserTabsRemoteAutostart() { diff --git a/widget/android/nsWindow.cpp b/widget/android/nsWindow.cpp index 8b0efb344d13..d112b2df6558 100644 --- a/widget/android/nsWindow.cpp +++ b/widget/android/nsWindow.cpp @@ -809,8 +809,8 @@ nsWindow::OnGlobalAndroidEvent(AndroidGeckoEvent *ae) gAndroidScreenBounds.width = newScreenWidth; gAndroidScreenBounds.height = newScreenHeight; - if (XRE_GetProcessType() != GeckoProcessType_Default || - !BrowserTabsRemote()) { + if (XRE_GetProcessType() != GeckoProcessType_Default && + !Preferences::GetBool("browser.tabs.remote.desktopbehavior", false)) { break; } diff --git a/xpcom/system/nsIXULRuntime.idl b/xpcom/system/nsIXULRuntime.idl index 5afa70e38d76..e37d5c7c4c0d 100644 --- a/xpcom/system/nsIXULRuntime.idl +++ b/xpcom/system/nsIXULRuntime.idl @@ -7,8 +7,6 @@ %{C++ namespace mozilla { -// Simple C++ getter for nsIXULRuntime::browserTabsRemote -bool BrowserTabsRemote(); // Simple C++ getter for nsIXULRuntime::browserTabsRemoteAutostart // This getter is a temporary function that checks for special // conditions in which e10s support is not great yet, and should @@ -25,7 +23,7 @@ bool BrowserTabsRemoteAutostart(); * stable/frozen, please contact Benjamin Smedberg. */ -[scriptable, uuid(789073a0-3f4a-11e4-916c-0800200c9a66)] +[scriptable, uuid(588831D6-7576-4CEA-B368-3AAB76CD9FCC)] interface nsIXULRuntime : nsISupports { /** @@ -87,12 +85,6 @@ interface nsIXULRuntime : nsISupports */ readonly attribute unsigned long processID; - /** - * If true, browser tabs may be opened in a different process from the main - * browser UI. - */ - readonly attribute boolean browserTabsRemote; - /** * If true, browser tabs may be opened by default in a different process * from the main browser UI. From 965a0295ffe58462a81f5bf61815d7f3ce958c0e Mon Sep 17 00:00:00 2001 From: Malini Das Date: Mon, 29 Sep 2014 18:22:44 -0400 Subject: [PATCH 35/82] Bug 1065933 - recover if containing frame is closed during touch, r=jgriffin --- testing/marionette/marionette-server.js | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/testing/marionette/marionette-server.js b/testing/marionette/marionette-server.js index 621ae09b6d68..e61c31d898ba 100644 --- a/testing/marionette/marionette-server.js +++ b/testing/marionette/marionette-server.js @@ -373,6 +373,19 @@ MarionetteServerConnection.prototype = { return Services.wm.getEnumerator(type); }, + /** + */ + addFrameCloseListener: function MDA_addFrameCloseListener(action) { + let curWindow = this.getCurrentWindow(); + let self = this; + this.mozBrowserClose = function() { + curWindow.removeEventListener('mozbrowserclose', self.mozBrowserClose, true); + self.switchToGlobalMessageManager(); + self.sendError("The frame closed during the " + action + ", recovering to allow further communications", 500, null, self.command_id); + }; + curWindow.addEventListener('mozbrowserclose', this.mozBrowserClose, true); + }, + /** * Create a new BrowserObj for window and add to known browsers * @@ -1526,6 +1539,7 @@ MarionetteServerConnection.prototype = { this.sendError("Command 'singleTap' is not available in chrome context", 500, null, this.command_id); } else { + this.addFrameCloseListener("tap"); this.sendAsync("singleTap", { id: serId, @@ -1548,6 +1562,7 @@ MarionetteServerConnection.prototype = { this.sendError("Command 'actionChain' is not available in chrome context", 500, null, this.command_id); } else { + this.addFrameCloseListener("action chain"); this.sendAsync("actionChain", { chain: aRequest.parameters.chain, @@ -1572,6 +1587,7 @@ MarionetteServerConnection.prototype = { this.sendError("Command 'multiAction' is not available in chrome context", 500, null, this.command_id); } else { + this.addFrameCloseListener("multi action chain"); this.sendAsync("multiAction", { value: aRequest.parameters.value, @@ -1734,14 +1750,7 @@ MarionetteServerConnection.prototype = { // This fires the mozbrowserclose event when it closes so we need to // listen for it and then just send an error back. The person making the // call should be aware something isnt right and handle accordingly - let curWindow = this.getCurrentWindow(); - let self = this; - this.mozBrowserClose = function() { - curWindow.removeEventListener('mozbrowserclose', self.mozBrowserClose, true); - self.switchToGlobalMessageManager(); - self.sendError("The frame closed during the click, recovering to allow further communications", 500, null, command_id); - }; - curWindow.addEventListener('mozbrowserclose', this.mozBrowserClose, true); + this.addFrameCloseListener("click"); this.sendAsync("clickElement", { id: aRequest.parameters.id }, command_id); From 1c8f9982028c02e73867be5988b7322943c39d63 Mon Sep 17 00:00:00 2001 From: Eitan Isaacson Date: Mon, 29 Sep 2014 15:35:29 -0700 Subject: [PATCH 36/82] Bug 1061671 - Wait for frame content to load before starting test. r=yzen --- accessible/jsat/content-script.js | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/accessible/jsat/content-script.js b/accessible/jsat/content-script.js index 49272134ccf6..dc3ceda2602d 100644 --- a/accessible/jsat/content-script.js +++ b/accessible/jsat/content-script.js @@ -20,6 +20,8 @@ XPCOMUtils.defineLazyModuleGetter(this, 'ContentControl', 'resource://gre/modules/accessibility/ContentControl.jsm'); XPCOMUtils.defineLazyModuleGetter(this, 'Roles', 'resource://gre/modules/accessibility/Constants.jsm'); +XPCOMUtils.defineLazyModuleGetter(this, 'States', + 'resource://gre/modules/accessibility/Constants.jsm'); Logger.debug('content-script.js'); @@ -142,7 +144,20 @@ addMessageListener( eventManager.inTest = m.json.inTest; eventManager.start(); - sendAsyncMessage('AccessFu:ContentStarted'); + function contentStarted() { + let accDoc = Utils.AccRetrieval.getAccessibleFor(content.document); + if (accDoc && !Utils.getState(accDoc).contains(States.BUSY)) { + sendAsyncMessage('AccessFu:ContentStarted'); + } else { + content.setTimeout(contentStarted, 0); + } + } + + if (m.json.inTest) { + // During a test we want to wait for the document to finish loading for + // consistency. + contentStarted(); + } }); addMessageListener( From 95bdb7948d9f2cdd805e0beafa86ccb3dbad6ad1 Mon Sep 17 00:00:00 2001 From: Chris Pearce Date: Tue, 30 Sep 2014 11:59:15 +1300 Subject: [PATCH 37/82] Bug 1073792 - Refresh WMF output types on discontinuities, to catch cases where WMF doesn't notify us of an output type change. r=kinetik --- content/media/fmp4/wmf/WMFAudioMFTManager.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/content/media/fmp4/wmf/WMFAudioMFTManager.cpp b/content/media/fmp4/wmf/WMFAudioMFTManager.cpp index a22bb8d2d163..ec785894b14e 100644 --- a/content/media/fmp4/wmf/WMFAudioMFTManager.cpp +++ b/content/media/fmp4/wmf/WMFAudioMFTManager.cpp @@ -256,6 +256,13 @@ WMFAudioMFTManager::Output(int64_t aStreamOffset, mAudioFrameOffset = 0; } mMustRecaptureAudioPosition = false; + + // Also update the output type, in case this segment has a different + // rate. This also triggers on the first sample, which can have a + // different rate than is advertised in the container, and sometimes + // we don't get a MF_E_TRANSFORM_STREAM_CHANGE when the rate changes. + hr = UpdateOutputType(); + NS_ENSURE_TRUE(SUCCEEDED(hr), hr); } MOZ_ASSERT(numFramesToStrip >= 0); int32_t offset = std::min(numFramesToStrip, numFrames); From 2174ece36925424fa67dacb9581be1de01d3f6b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bargull?= Date: Tue, 23 Sep 2014 22:47:01 +0200 Subject: [PATCH 38/82] Bug 1068589 - Remove forced extensible check before calling JSObject::preventExtensions. r=jwalden --HG-- extra : rebase_source : 5ec8efb801fd67dd7c9d08ad0259aaee36f225ce --- js/src/builtin/Object.cpp | 6 ----- ...-nonextensible-during-preventExtensions.js | 8 ++++++ js/src/jsapi.cpp | 5 ---- js/src/jsobj.cpp | 5 +--- js/src/tests/ecma_6/Object/freeze-proxy.js | 27 +++++++++++++++++++ .../ecma_6/Object/preventExtensions-proxy.js | 23 ++++++++++++++++ js/src/tests/ecma_6/Object/seal-proxy.js | 27 +++++++++++++++++++ js/src/vm/Debugger.cpp | 7 ----- js/src/vm/Shape.cpp | 12 +++------ 9 files changed, 89 insertions(+), 31 deletions(-) create mode 100644 js/src/jit-test/tests/proxy/target-becomes-nonextensible-during-preventExtensions.js create mode 100644 js/src/tests/ecma_6/Object/freeze-proxy.js create mode 100644 js/src/tests/ecma_6/Object/preventExtensions-proxy.js create mode 100644 js/src/tests/ecma_6/Object/seal-proxy.js diff --git a/js/src/builtin/Object.cpp b/js/src/builtin/Object.cpp index 44aed0e0dd35..66691951d04a 100644 --- a/js/src/builtin/Object.cpp +++ b/js/src/builtin/Object.cpp @@ -1044,12 +1044,6 @@ obj_preventExtensions(JSContext *cx, unsigned argc, Value *vp) args.rval().setObject(*obj); - bool extensible; - if (!JSObject::isExtensible(cx, obj, &extensible)) - return false; - if (!extensible) - return true; - return JSObject::preventExtensions(cx, obj); } diff --git a/js/src/jit-test/tests/proxy/target-becomes-nonextensible-during-preventExtensions.js b/js/src/jit-test/tests/proxy/target-becomes-nonextensible-during-preventExtensions.js new file mode 100644 index 000000000000..3ef475c72887 --- /dev/null +++ b/js/src/jit-test/tests/proxy/target-becomes-nonextensible-during-preventExtensions.js @@ -0,0 +1,8 @@ +// Don't assert +var obj = {}; +var proxy = new Proxy(obj, { + get preventExtensions() { + Object.preventExtensions(obj); + } +}); +Object.preventExtensions(proxy); diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index 5badfe04fd28..4c0876a89919 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -6517,11 +6517,6 @@ JS_DecodeInterpretedFunction(JSContext *cx, const void *data, uint32_t length) JS_PUBLIC_API(bool) JS_PreventExtensions(JSContext *cx, JS::HandleObject obj) { - bool extensible; - if (!JSObject::isExtensible(cx, obj, &extensible)) - return false; - if (!extensible) - return true; return JSObject::preventExtensions(cx, obj); } diff --git a/js/src/jsobj.cpp b/js/src/jsobj.cpp index 9a57319d3a43..7a18b46e477f 100644 --- a/js/src/jsobj.cpp +++ b/js/src/jsobj.cpp @@ -1267,10 +1267,7 @@ JSObject::sealOrFreeze(JSContext *cx, HandleObject obj, ImmutabilityType it) assertSameCompartment(cx, obj); JS_ASSERT(it == SEAL || it == FREEZE); - bool extensible; - if (!JSObject::isExtensible(cx, obj, &extensible)) - return false; - if (extensible && !JSObject::preventExtensions(cx, obj)) + if (!JSObject::preventExtensions(cx, obj)) return false; AutoIdVector props(cx); diff --git a/js/src/tests/ecma_6/Object/freeze-proxy.js b/js/src/tests/ecma_6/Object/freeze-proxy.js new file mode 100644 index 000000000000..3d95a28d4fc8 --- /dev/null +++ b/js/src/tests/ecma_6/Object/freeze-proxy.js @@ -0,0 +1,27 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +function logProxy(object = {}, handler = {}) { + var log = []; + var proxy = new Proxy(object, new Proxy(handler, { + get(target, propertyKey, receiver) { + log.push(propertyKey); + return target[propertyKey]; + } + })); + return {proxy, log}; +} + +// The order of operations is backwards when compared to ES6 draft rev 27 +// (2014 August 24), but see https://bugs.ecmascript.org/show_bug.cgi?id=3215 +// for an explanation on why the spec version is clearly wrong. + +var {proxy, log} = logProxy(); +Object.freeze(proxy); +assertDeepEq(log, ["preventExtensions", "ownKeys"]); + +var {proxy, log} = logProxy(); +Object.freeze(Object.freeze(proxy)); +assertDeepEq(log, ["preventExtensions", "ownKeys", "preventExtensions", "ownKeys"]); + +reportCompare(0, 0); diff --git a/js/src/tests/ecma_6/Object/preventExtensions-proxy.js b/js/src/tests/ecma_6/Object/preventExtensions-proxy.js new file mode 100644 index 000000000000..347a63bc3a9d --- /dev/null +++ b/js/src/tests/ecma_6/Object/preventExtensions-proxy.js @@ -0,0 +1,23 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +function logProxy(object = {}, handler = {}) { + var log = []; + var proxy = new Proxy(object, new Proxy(handler, { + get(target, propertyKey, receiver) { + log.push(propertyKey); + return target[propertyKey]; + } + })); + return {proxy, log}; +} + +var {proxy, log} = logProxy(); +Object.preventExtensions(proxy); +assertDeepEq(log, ["preventExtensions"]); + +var {proxy, log} = logProxy(); +Object.preventExtensions(Object.preventExtensions(proxy)); +assertDeepEq(log, ["preventExtensions", "preventExtensions"]); + +reportCompare(0, 0); diff --git a/js/src/tests/ecma_6/Object/seal-proxy.js b/js/src/tests/ecma_6/Object/seal-proxy.js new file mode 100644 index 000000000000..44bb685c872a --- /dev/null +++ b/js/src/tests/ecma_6/Object/seal-proxy.js @@ -0,0 +1,27 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +function logProxy(object = {}, handler = {}) { + var log = []; + var proxy = new Proxy(object, new Proxy(handler, { + get(target, propertyKey, receiver) { + log.push(propertyKey); + return target[propertyKey]; + } + })); + return {proxy, log}; +} + +// The order of operations is backwards when compared to ES6 draft rev 27 +// (2014 August 24), but see https://bugs.ecmascript.org/show_bug.cgi?id=3215 +// for an explanation on why the spec version is clearly wrong. + +var {proxy, log} = logProxy(); +Object.seal(proxy); +assertDeepEq(log, ["preventExtensions", "ownKeys"]); + +var {proxy, log} = logProxy(); +Object.seal(Object.seal(proxy)); +assertDeepEq(log, ["preventExtensions", "ownKeys", "preventExtensions", "ownKeys"]); + +reportCompare(0, 0); diff --git a/js/src/vm/Debugger.cpp b/js/src/vm/Debugger.cpp index 2a3f8124f7fd..ed59db54a0e2 100644 --- a/js/src/vm/Debugger.cpp +++ b/js/src/vm/Debugger.cpp @@ -5577,13 +5577,6 @@ DebuggerObject_sealHelper(JSContext *cx, unsigned argc, Value *vp, SealHelperOp ok = JSObject::freeze(cx, obj); } else { JS_ASSERT(op == PreventExtensions); - bool extensible; - if (!JSObject::isExtensible(cx, obj, &extensible)) - return false; - if (!extensible) { - args.rval().setUndefined(); - return true; - } ok = JSObject::preventExtensions(cx, obj); } if (!ok) diff --git a/js/src/vm/Shape.cpp b/js/src/vm/Shape.cpp index dc872779eb89..b33d7654cc4a 100644 --- a/js/src/vm/Shape.cpp +++ b/js/src/vm/Shape.cpp @@ -1341,20 +1341,14 @@ Shape::setObjectMetadata(JSContext *cx, JSObject *metadata, TaggedProto proto, S /* static */ bool js::ObjectImpl::preventExtensions(JSContext *cx, Handle obj) { -#ifdef DEBUG - bool extensible; - if (!JSObject::isExtensible(cx, obj, &extensible)) - return false; - MOZ_ASSERT(extensible, - "Callers must ensure |obj| is extensible before calling " - "preventExtensions"); -#endif - if (Downcast(obj)->is()) { RootedObject object(cx, obj->asObjectPtr()); return js::Proxy::preventExtensions(cx, object); } + if (!obj->nonProxyIsExtensible()) + return true; + RootedObject self(cx, obj->asObjectPtr()); /* From f83a8d78dcf8e66bac7b15e8fe2ec4c9d5d52230 Mon Sep 17 00:00:00 2001 From: Jeff Walden Date: Mon, 29 Sep 2014 16:47:52 -0700 Subject: [PATCH 39/82] Remove tabs from js/src/vm. No bug, r=dietabsdie and DONTBUILD because |hg qdi -w| prints nothing for this change --- js/src/vm/ArrayBufferObject-inl.h | 6 +++--- js/src/vm/PosixNSPR.cpp | 12 ++++++------ js/src/vm/SharedTypedArrayObject.cpp | 26 +++++++++++++------------- js/src/vm/TypedArrayCommon.h | 18 +++++++++--------- 4 files changed, 31 insertions(+), 31 deletions(-) diff --git a/js/src/vm/ArrayBufferObject-inl.h b/js/src/vm/ArrayBufferObject-inl.h index b849ebe96857..914500b73652 100644 --- a/js/src/vm/ArrayBufferObject-inl.h +++ b/js/src/vm/ArrayBufferObject-inl.h @@ -21,7 +21,7 @@ inline uint32_t AnyArrayBufferByteLength(const ArrayBufferObjectMaybeShared *buf) { if (buf->is()) - return buf->as().byteLength(); + return buf->as().byteLength(); return buf->as().byteLength(); } @@ -29,7 +29,7 @@ inline uint8_t * AnyArrayBufferDataPointer(const ArrayBufferObjectMaybeShared *buf) { if (buf->is()) - return buf->as().dataPointer(); + return buf->as().dataPointer(); return buf->as().dataPointer(); } @@ -37,7 +37,7 @@ inline ArrayBufferObjectMaybeShared & AsAnyArrayBuffer(HandleValue val) { if (val.toObject().is()) - return val.toObject().as(); + return val.toObject().as(); return val.toObject().as(); } diff --git a/js/src/vm/PosixNSPR.cpp b/js/src/vm/PosixNSPR.cpp index 891e8b9e7273..8d7582b50136 100644 --- a/js/src/vm/PosixNSPR.cpp +++ b/js/src/vm/PosixNSPR.cpp @@ -71,12 +71,12 @@ Initialize() PRThread * PR_CreateThread(PRThreadType type, - void (*start)(void *arg), - void *arg, - PRThreadPriority priority, - PRThreadScope scope, - PRThreadState state, - uint32_t stackSize) + void (*start)(void *arg), + void *arg, + PRThreadPriority priority, + PRThreadScope scope, + PRThreadState state, + uint32_t stackSize) { JS_ASSERT(type == PR_USER_THREAD); JS_ASSERT(priority == PR_PRIORITY_NORMAL); diff --git a/js/src/vm/SharedTypedArrayObject.cpp b/js/src/vm/SharedTypedArrayObject.cpp index 9cdbdc0e4042..e2974e69c704 100644 --- a/js/src/vm/SharedTypedArrayObject.cpp +++ b/js/src/vm/SharedTypedArrayObject.cpp @@ -188,7 +188,7 @@ class SharedTypedArrayObjectTemplate : public SharedTypedArrayObject obj->setSlot(BUFFER_SLOT, ObjectOrNullValue(buffer)); - InitSharedArrayBufferViewDataPointer(obj, buffer, byteOffset); + InitSharedArrayBufferViewDataPointer(obj, buffer, byteOffset); obj->setSlot(LENGTH_SLOT, Int32Value(len)); obj->setSlot(BYTEOFFSET_SLOT, Int32Value(byteOffset)); @@ -273,7 +273,7 @@ class SharedTypedArrayObjectTemplate : public SharedTypedArrayObject if (!UncheckedUnwrap(dataObj)->is()) { JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_SHARED_TYPED_ARRAY_BAD_OBJECT); return nullptr; - } + } uint32_t byteOffset = 0; uint32_t length = LENGTH_NOT_PROVIDED; @@ -373,7 +373,7 @@ class SharedTypedArrayObjectTemplate : public SharedTypedArrayObject static const NativeType getIndex(JSObject *obj, uint32_t index) { - SharedTypedArrayObject &tarray = obj->as(); + SharedTypedArrayObject &tarray = obj->as(); MOZ_ASSERT(index < tarray.length()); return static_cast(tarray.viewData())[index]; } @@ -424,10 +424,10 @@ class SharedTypedArrayObjectTemplate : public SharedTypedArrayObject JS_ASSERT(IsSharedArrayBuffer(bufobj) || bufobj->is()); if (bufobj->is()) { - // Complicated, see TypedArrayObject.cpp for code. For now, punt. - JS_ReportError(cx, "Permission denied to access object"); - return nullptr; - } + // Complicated, see TypedArrayObject.cpp for code. For now, punt. + JS_ReportError(cx, "Permission denied to access object"); + return nullptr; + } Rooted buffer(cx, &AsSharedArrayBuffer(bufobj)); @@ -673,7 +673,7 @@ IMPL_SHARED_TYPED_ARRAY_JSAPI_CONSTRUCTORS(Float64, double) #define IMPL_SHARED_TYPED_ARRAY_COMBINED_UNWRAPPERS(Name, ExternalType, InternalType) \ JS_FRIEND_API(JSObject *) JS_GetObjectAsShared ## Name ## Array(JSObject *obj, \ uint32_t *length, \ - ExternalType **data) \ + ExternalType **data) \ { \ if (!(obj = CheckedUnwrap(obj))) \ return nullptr; \ @@ -683,7 +683,7 @@ IMPL_SHARED_TYPED_ARRAY_JSAPI_CONSTRUCTORS(Float64, double) if (clasp != &SharedTypedArrayObject::classes[id]) \ return nullptr; \ \ - SharedTypedArrayObject *tarr = &obj->as(); \ + SharedTypedArrayObject *tarr = &obj->as(); \ *length = tarr->length(); \ *data = static_cast(tarr->viewData()); \ \ @@ -749,15 +749,15 @@ IMPL_SHARED_TYPED_ARRAY_COMBINED_UNWRAPPERS(Float64, double, double) nullptr, /* call */ \ nullptr, /* hasInstance */ \ nullptr, /* construct */ \ - nullptr, /* trace */ \ + nullptr, /* trace */ \ SHARED_TYPED_ARRAY_CLASS_SPEC(_typedArray) \ } template bool SharedTypedArrayObjectTemplate::FinishClassInit(JSContext *cx, - HandleObject ctor, - HandleObject proto) + HandleObject ctor, + HandleObject proto) { RootedValue bytesValue(cx, Int32Value(BYTES_PER_ELEMENT)); @@ -982,7 +982,7 @@ SharedTypedArrayObject::setElement(SharedTypedArrayObject &obj, uint32_t index, switch (obj.type()) { case Scalar::Int8: - SharedTypedArrayObjectTemplate::setIndexValue(obj, index, d); + SharedTypedArrayObjectTemplate::setIndexValue(obj, index, d); break; case Scalar::Uint8: SharedTypedArrayObjectTemplate::setIndexValue(obj, index, d); diff --git a/js/src/vm/TypedArrayCommon.h b/js/src/vm/TypedArrayCommon.h index 7d264af43ff6..f68e3554e90f 100644 --- a/js/src/vm/TypedArrayCommon.h +++ b/js/src/vm/TypedArrayCommon.h @@ -82,7 +82,7 @@ inline uint32_t AnyTypedArrayLength(HandleObject obj) { if (obj->is()) - return obj->as().length(); + return obj->as().length(); return obj->as().length(); } @@ -90,7 +90,7 @@ inline uint32_t AnyTypedArrayLength(const JSObject *obj) { if (obj->is()) - return obj->as().length(); + return obj->as().length(); return obj->as().length(); } @@ -98,7 +98,7 @@ inline Scalar::Type AnyTypedArrayType(HandleObject obj) { if (obj->is()) - return obj->as().type(); + return obj->as().type(); return obj->as().type(); } @@ -106,7 +106,7 @@ inline Scalar::Type AnyTypedArrayType(const JSObject *obj) { if (obj->is()) - return obj->as().type(); + return obj->as().type(); return obj->as().type(); } @@ -114,7 +114,7 @@ inline Shape* AnyTypedArrayShape(HandleObject obj) { if (obj->is()) - return obj->as().lastProperty(); + return obj->as().lastProperty(); return obj->as().lastProperty(); } @@ -122,7 +122,7 @@ inline Shape* AnyTypedArrayShape(const JSObject *obj) { if (obj->is()) - return obj->as().lastProperty(); + return obj->as().lastProperty(); return obj->as().lastProperty(); } @@ -130,7 +130,7 @@ inline const TypedArrayLayout& AnyTypedArrayLayout(const JSObject *obj) { if (obj->is()) - return obj->as().layout(); + return obj->as().layout(); return obj->as().layout(); } @@ -138,7 +138,7 @@ inline void * AnyTypedArrayViewData(const JSObject *obj) { if (obj->is()) - return obj->as().viewData(); + return obj->as().viewData(); return obj->as().viewData(); } @@ -146,7 +146,7 @@ inline uint32_t AnyTypedArrayByteLength(const JSObject *obj) { if (obj->is()) - return obj->as().byteLength(); + return obj->as().byteLength(); return obj->as().byteLength(); } From 17d41ed59fc3f02a08f8948d600baeeea88ba3fb Mon Sep 17 00:00:00 2001 From: Matt Woodrow Date: Tue, 30 Sep 2014 13:20:35 +1300 Subject: [PATCH 40/82] Bug 1067588 - Don't use source clipping if the draw won't resample. r=Bas --- gfx/thebes/gfxUtils.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/gfx/thebes/gfxUtils.cpp b/gfx/thebes/gfxUtils.cpp index f97d2bc86f0e..a1cb27d0b6ef 100644 --- a/gfx/thebes/gfxUtils.cpp +++ b/gfx/thebes/gfxUtils.cpp @@ -594,6 +594,7 @@ gfxUtils::DrawPixelSnapped(gfxContext* aContext, region.Width(), region.Height()); if (aRegion.IsRestricted() && + aContext->CurrentMatrix().HasNonIntegerTranslation() && drawable->DrawWithSamplingRect(aContext, aRegion.Rect(), aRegion.Restriction(), doTile, aFilter, aOpacity)) { return; From 0fca9e18eb33c8f9d3cf8d1ff6182167ac3605f8 Mon Sep 17 00:00:00 2001 From: Wes Kocher Date: Mon, 29 Sep 2014 17:39:16 -0700 Subject: [PATCH 41/82] Backed out changeset bbf22ebcac51 (bug 1065933) for Gip orange --- testing/marionette/marionette-server.js | 25 ++++++++----------------- 1 file changed, 8 insertions(+), 17 deletions(-) diff --git a/testing/marionette/marionette-server.js b/testing/marionette/marionette-server.js index e61c31d898ba..621ae09b6d68 100644 --- a/testing/marionette/marionette-server.js +++ b/testing/marionette/marionette-server.js @@ -373,19 +373,6 @@ MarionetteServerConnection.prototype = { return Services.wm.getEnumerator(type); }, - /** - */ - addFrameCloseListener: function MDA_addFrameCloseListener(action) { - let curWindow = this.getCurrentWindow(); - let self = this; - this.mozBrowserClose = function() { - curWindow.removeEventListener('mozbrowserclose', self.mozBrowserClose, true); - self.switchToGlobalMessageManager(); - self.sendError("The frame closed during the " + action + ", recovering to allow further communications", 500, null, self.command_id); - }; - curWindow.addEventListener('mozbrowserclose', this.mozBrowserClose, true); - }, - /** * Create a new BrowserObj for window and add to known browsers * @@ -1539,7 +1526,6 @@ MarionetteServerConnection.prototype = { this.sendError("Command 'singleTap' is not available in chrome context", 500, null, this.command_id); } else { - this.addFrameCloseListener("tap"); this.sendAsync("singleTap", { id: serId, @@ -1562,7 +1548,6 @@ MarionetteServerConnection.prototype = { this.sendError("Command 'actionChain' is not available in chrome context", 500, null, this.command_id); } else { - this.addFrameCloseListener("action chain"); this.sendAsync("actionChain", { chain: aRequest.parameters.chain, @@ -1587,7 +1572,6 @@ MarionetteServerConnection.prototype = { this.sendError("Command 'multiAction' is not available in chrome context", 500, null, this.command_id); } else { - this.addFrameCloseListener("multi action chain"); this.sendAsync("multiAction", { value: aRequest.parameters.value, @@ -1750,7 +1734,14 @@ MarionetteServerConnection.prototype = { // This fires the mozbrowserclose event when it closes so we need to // listen for it and then just send an error back. The person making the // call should be aware something isnt right and handle accordingly - this.addFrameCloseListener("click"); + let curWindow = this.getCurrentWindow(); + let self = this; + this.mozBrowserClose = function() { + curWindow.removeEventListener('mozbrowserclose', self.mozBrowserClose, true); + self.switchToGlobalMessageManager(); + self.sendError("The frame closed during the click, recovering to allow further communications", 500, null, command_id); + }; + curWindow.addEventListener('mozbrowserclose', this.mozBrowserClose, true); this.sendAsync("clickElement", { id: aRequest.parameters.id }, command_id); From 8df2e7909323447e36d00f071ee483d755b18bdc Mon Sep 17 00:00:00 2001 From: Matthew Gregan Date: Mon, 29 Sep 2014 13:13:10 +1300 Subject: [PATCH 42/82] Bug 1074004 - Update nestegg from upstream. r=giles --HG-- rename : media/libnestegg/README => media/libnestegg/README.md --- media/libnestegg/{README => README.md} | 2 + media/libnestegg/README_MOZILLA | 2 +- media/libnestegg/include/nestegg.h | 5 +- media/libnestegg/src/halloc.c | 21 ++- media/libnestegg/src/nestegg.c | 241 +++++++++++++++++-------- media/libnestegg/update.sh | 2 +- 6 files changed, 187 insertions(+), 86 deletions(-) rename media/libnestegg/{README => README.md} (63%) diff --git a/media/libnestegg/README b/media/libnestegg/README.md similarity index 63% rename from media/libnestegg/README rename to media/libnestegg/README.md index 47c8237d2f1a..aba7dc2727f6 100644 --- a/media/libnestegg/README +++ b/media/libnestegg/README.md @@ -1,3 +1,5 @@ +[![Build Status](https://travis-ci.org/kinetiknz/nestegg.svg?branch=master)](https://travis-ci.org/kinetiknz/nestegg) + See INSTALL for build instructions. Licensed under an ISC-style license. See LICENSE for details. diff --git a/media/libnestegg/README_MOZILLA b/media/libnestegg/README_MOZILLA index c7857bbdbe88..40f66f037347 100644 --- a/media/libnestegg/README_MOZILLA +++ b/media/libnestegg/README_MOZILLA @@ -5,4 +5,4 @@ Makefile.in build files for the Mozilla build system. The nestegg git repository is: git://github.com/kinetiknz/nestegg.git -The git commit ID used was 46ab96bcc8b099704cc8a15993f80fe0269a5284. +The git commit ID used was 2f596487949b192fb15866c6f8ecc6b04df92cb8. diff --git a/media/libnestegg/include/nestegg.h b/media/libnestegg/include/nestegg.h index 06f9c83b8772..04fc3e9c0555 100644 --- a/media/libnestegg/include/nestegg.h +++ b/media/libnestegg/include/nestegg.h @@ -381,8 +381,11 @@ int nestegg_sniff(unsigned char const * buffer, size_t length); * Set the underlying allocation function for library allocations. * * @param realloc_func The desired function. + * @retval 0 realloc_func(p, 0) does not act as free() + * @retval 1 realloc_func(p, 0) acts as free() + * @retval -1 malloc failed during realloc_func test */ -void nestegg_set_halloc_func(void * (* realloc_func)(void *, size_t)); +int nestegg_set_halloc_func(void * (* realloc_func)(void *, size_t)); #if defined(__cplusplus) } diff --git a/media/libnestegg/src/halloc.c b/media/libnestegg/src/halloc.c index 5758fc07b9e7..441107d83bc3 100644 --- a/media/libnestegg/src/halloc.c +++ b/media/libnestegg/src/halloc.c @@ -40,13 +40,15 @@ typedef struct hblock * */ realloc_t halloc_allocator = NULL; +realloc_t halloc_wrapped_allocator = NULL; #define allocator halloc_allocator +#define wrapped_allocator halloc_wrapped_allocator /* * static methods */ -static void _set_allocator(void); +int halloc_set_allocator(realloc_t realloc_func); static void * _realloc(void * ptr, size_t n); static int _relate(hblock_t * b, hblock_t * p); @@ -62,7 +64,7 @@ void * halloc(void * ptr, size_t len) /* set up default allocator */ if (! allocator) { - _set_allocator(); + halloc_set_allocator(realloc); assert(allocator); } @@ -172,7 +174,7 @@ char * h_strdup(const char * str) /* * static stuff */ -static void _set_allocator(void) +int halloc_set_allocator(realloc_t realloc_func) { void * p; assert(! allocator); @@ -187,17 +189,20 @@ static void _set_allocator(void) * * Thanks to Stan Tobias for pointing this tricky part out. */ - allocator = realloc; + allocator = realloc_func; if (! (p = malloc(1))) /* hmm */ - return; + return -1; - if ((p = realloc(p, 0))) + if ((p = realloc_func(p, 0))) { - /* realloc cannot be used as free() */ + /* realloc_func cannot be used as free() */ allocator = _realloc; + wrapped_allocator = realloc_func; free(p); + return 0; } + return 1; } static void * _realloc(void * ptr, size_t n) @@ -206,7 +211,7 @@ static void * _realloc(void * ptr, size_t n) * free'ing realloc() */ if (n) - return realloc(ptr, n); + return wrapped_allocator(ptr, n); free(ptr); return NULL; } diff --git a/media/libnestegg/src/nestegg.c b/media/libnestegg/src/nestegg.c index f74ba04bc870..2f52888db39b 100644 --- a/media/libnestegg/src/nestegg.c +++ b/media/libnestegg/src/nestegg.c @@ -321,9 +321,18 @@ struct block_additional { struct block_additional * next; }; +#define NE_IO_BUFSZ 16384 + +struct nestegg_io_buf { + nestegg_io io; + unsigned char buffer[NE_IO_BUFSZ]; + size_t bufsz; + int offset; +}; + /* Public (opaque) Structures */ struct nestegg { - nestegg_io * io; + struct nestegg_io_buf * io; nestegg_log log; struct pool_ctx * alloc_pool; uint64_t last_id; @@ -546,19 +555,99 @@ ne_alloc(size_t size) } static int -ne_io_read(nestegg_io * io, void * buffer, size_t length) +ne_io_read(struct nestegg_io_buf * io, void * buffer, size_t length) { - return io->read(buffer, length, io->userdata); + int64_t off; + int r; + size_t avail; + + assert(io->offset == -1 || (io->offset >= 0 && (unsigned int) io->offset < io->bufsz)); + + /* Too big to buffer, invalidate buffer and read through */ + if (length > io->bufsz) { + if (io->offset != -1) { + r = io->io.seek(-(io->bufsz - io->offset), NESTEGG_SEEK_CUR, io->io.userdata); + if (r != 0) { + return -1; + } + } + io->offset = -1; + return io->io.read(buffer, length, io->io.userdata); + } + + /* Buffer invalid */ + if (io->offset == -1) { + off = io->io.tell(io->io.userdata); + if (off == -1) { + return -1; + } + /* Refill buffer */ + r = io->io.read(io->buffer, io->bufsz, io->io.userdata); + if (r != 1) { + /* Read truncated due to being within io->bufsz of EOS, reset read + position and switch to read through mode */ + io->offset = -1; + io->bufsz = 0; + if (r == 0) { + r = io->io.seek(off, NESTEGG_SEEK_SET, io->io.userdata); + } + if (r == 0) { + return io->io.read(buffer, length, io->io.userdata); + } + return -1; + } + if (r == 1) { + io->offset = 0; + } + } + + /* Service request with what we have */ + avail = length; + if (io->bufsz - io->offset < length) { + avail = io->bufsz - io->offset; + } + memcpy(buffer, io->buffer + io->offset, avail); + io->offset += avail; + + if ((unsigned int) io->offset == io->bufsz) { + io->offset = -1; + } + + /* Still more to read, invalidate buffer and read more */ + if (length - avail > 0) { + return ne_io_read(io, (char *) buffer + avail, length - avail); + } + + return 1; } static int -ne_io_seek(nestegg_io * io, int64_t offset, int whence) +ne_io_seek(struct nestegg_io_buf * io, int64_t offset, int whence) { - return io->seek(offset, whence, io->userdata); + /* Invalidate buffer */ + io->offset = -1; + + return io->io.seek(offset, whence, io->io.userdata); +} + +static int64_t +ne_io_tell(struct nestegg_io_buf * io) +{ + int64_t off; + + off = io->io.tell(io->io.userdata); + if (off == -1) { + return -1; + } + if (io->offset == -1) { + return off; + } + assert(off >= (int64_t) io->bufsz - io->offset); + return off - io->bufsz + (unsigned int) io->offset; } static int -ne_io_read_skip(nestegg_io * io, size_t length) +ne_io_read_skip(struct nestegg_io_buf * io, size_t length) { size_t get; unsigned char buf[8192]; @@ -575,14 +664,8 @@ ne_io_read_skip(nestegg_io * io, size_t length) return r; } -static int64_t -ne_io_tell(nestegg_io * io) -{ - return io->tell(io->userdata); -} - static int -ne_bare_read_vint(nestegg_io * io, uint64_t * value, uint64_t * length, enum vint_mask maskflag) +ne_bare_read_vint(struct nestegg_io_buf * io, uint64_t * value, uint64_t * length, enum vint_mask maskflag) { int r; unsigned char b; @@ -619,19 +702,19 @@ ne_bare_read_vint(nestegg_io * io, uint64_t * value, uint64_t * length, enum vin } static int -ne_read_id(nestegg_io * io, uint64_t * value, uint64_t * length) +ne_read_id(struct nestegg_io_buf * io, uint64_t * value, uint64_t * length) { return ne_bare_read_vint(io, value, length, MASK_NONE); } static int -ne_read_vint(nestegg_io * io, uint64_t * value, uint64_t * length) +ne_read_vint(struct nestegg_io_buf * io, uint64_t * value, uint64_t * length) { return ne_bare_read_vint(io, value, length, MASK_FIRST_BIT); } static int -ne_read_svint(nestegg_io * io, int64_t * value, uint64_t * length) +ne_read_svint(struct nestegg_io_buf * io, int64_t * value, uint64_t * length) { int r; uint64_t uvalue; @@ -653,7 +736,7 @@ ne_read_svint(nestegg_io * io, int64_t * value, uint64_t * length) } static int -ne_read_uint(nestegg_io * io, uint64_t * val, uint64_t length) +ne_read_uint(struct nestegg_io_buf * io, uint64_t * val, uint64_t length) { unsigned char b; int r; @@ -675,7 +758,7 @@ ne_read_uint(nestegg_io * io, uint64_t * val, uint64_t length) } static int -ne_read_int(nestegg_io * io, int64_t * val, uint64_t length) +ne_read_int(struct nestegg_io_buf * io, int64_t * val, uint64_t length) { int r; uint64_t uval, base; @@ -688,8 +771,8 @@ ne_read_int(nestegg_io * io, int64_t * val, uint64_t length) base = 1; base <<= length * 8 - 1; if (uval >= base) { - base = 1; - base <<= length * 8; + base = 1; + base <<= length * 8; } else { base = 0; } @@ -702,7 +785,7 @@ ne_read_int(nestegg_io * io, int64_t * val, uint64_t length) } static int -ne_read_float(nestegg_io * io, double * val, uint64_t length) +ne_read_float(struct nestegg_io_buf * io, double * val, uint64_t length) { union { uint64_t u; @@ -736,9 +819,9 @@ ne_read_string(nestegg * ctx, char ** val, uint64_t length) if (!str) return -1; if (length) { - r = ne_io_read(ctx->io, (unsigned char *) str, length); - if (r != 1) - return r; + r = ne_io_read(ctx->io, (unsigned char *) str, length); + if (r != 1) + return r; } str[length] = '\0'; *val = str; @@ -1018,8 +1101,9 @@ ne_read_simple(nestegg * ctx, struct ebml_element_desc * desc, size_t length) break; case TYPE_MASTER: case TYPE_UNKNOWN: - assert(0); + default: r = 0; + assert(0); break; } @@ -1136,7 +1220,7 @@ ne_xiph_lace_value(unsigned char ** np) } static int -ne_read_xiph_lace_value(nestegg_io * io, uint64_t * value, size_t * consumed) +ne_read_xiph_lace_value(struct nestegg_io_buf * io, uint64_t * value, size_t * consumed) { int r; uint64_t lace; @@ -1159,7 +1243,7 @@ ne_read_xiph_lace_value(nestegg_io * io, uint64_t * value, size_t * consumed) } static int -ne_read_xiph_lacing(nestegg_io * io, size_t block, size_t * read, uint64_t n, uint64_t * sizes) +ne_read_xiph_lacing(struct nestegg_io_buf * io, size_t block, size_t * read, uint64_t n, uint64_t * sizes) { int r; size_t i = 0; @@ -1182,7 +1266,7 @@ ne_read_xiph_lacing(nestegg_io * io, size_t block, size_t * read, uint64_t n, ui } static int -ne_read_ebml_lacing(nestegg_io * io, size_t block, size_t * read, uint64_t n, uint64_t * sizes) +ne_read_ebml_lacing(struct nestegg_io_buf * io, size_t block, size_t * read, uint64_t n, uint64_t * sizes) { int r; uint64_t lace, sum, length; @@ -1792,9 +1876,9 @@ struct sniff_buffer { }; static int -ne_buffer_read(void * buffer, size_t length, void * user_data) +ne_buffer_read(void * buffer, size_t length, void * userdata) { - struct sniff_buffer * sb = user_data; + struct sniff_buffer * sb = userdata; int rv = 1; size_t available = sb->length - sb->offset; @@ -1809,21 +1893,21 @@ ne_buffer_read(void * buffer, size_t length, void * user_data) } static int -ne_buffer_seek(int64_t offset, int whence, void * user_data) +ne_buffer_seek(int64_t offset, int whence, void * userdata) { - struct sniff_buffer * sb = user_data; + struct sniff_buffer * sb = userdata; int64_t o = sb->offset; switch(whence) { - case NESTEGG_SEEK_SET: - o = offset; - break; - case NESTEGG_SEEK_CUR: - o += offset; - break; - case NESTEGG_SEEK_END: - o = sb->length + offset; - break; + case NESTEGG_SEEK_SET: + o = offset; + break; + case NESTEGG_SEEK_CUR: + o += offset; + break; + case NESTEGG_SEEK_END: + o = sb->length + offset; + break; } if (o < 0 || o > (int64_t) sb->length) @@ -1834,9 +1918,9 @@ ne_buffer_seek(int64_t offset, int whence, void * user_data) } static int64_t -ne_buffer_tell(void * user_data) +ne_buffer_tell(void * userdata) { - struct sniff_buffer * sb = user_data; + struct sniff_buffer * sb = userdata; return sb->offset; } @@ -1860,7 +1944,9 @@ ne_match_webm(nestegg_io io, int64_t max_offset) nestegg_destroy(ctx); return -1; } - *ctx->io = io; + ctx->io->io = io; + ctx->io->bufsz = NE_IO_BUFSZ; + ctx->io->offset = -1; ctx->alloc_pool = ne_pool_init(); if (!ctx->alloc_pool) { nestegg_destroy(ctx); @@ -1918,7 +2004,9 @@ nestegg_init(nestegg ** context, nestegg_io io, nestegg_log callback, int64_t ma nestegg_destroy(ctx); return -1; } - *ctx->io = io; + ctx->io->io = io; + ctx->io->bufsz = NE_IO_BUFSZ; + ctx->io->offset = -1; ctx->log = callback; ctx->alloc_pool = ne_pool_init(); if (!ctx->alloc_pool) { @@ -2264,35 +2352,35 @@ nestegg_track_codec_data(nestegg * ctx, unsigned int track, unsigned int item, return -1; if (nestegg_track_codec_id(ctx, track) != NESTEGG_CODEC_VORBIS - && nestegg_track_codec_id(ctx, track) != NESTEGG_CODEC_OPUS) + && nestegg_track_codec_id(ctx, track) != NESTEGG_CODEC_OPUS) return -1; if (ne_get_binary(entry->codec_private, &codec_private) != 0) return -1; if (nestegg_track_codec_id(ctx, track) == NESTEGG_CODEC_VORBIS) { - p = codec_private.data; - count = *p++ + 1; + p = codec_private.data; + count = *p++ + 1; - if (count > 3) + if (count > 3) + return -1; + + i = 0; + total = 0; + while (--count) { + sizes[i] = ne_xiph_lace_value(&p); + total += sizes[i]; + i += 1; + } + sizes[i] = codec_private.length - total - (p - codec_private.data); + + for (i = 0; i < item; ++i) { + if (sizes[i] > LIMIT_FRAME) return -1; - - i = 0; - total = 0; - while (--count) { - sizes[i] = ne_xiph_lace_value(&p); - total += sizes[i]; - i += 1; - } - sizes[i] = codec_private.length - total - (p - codec_private.data); - - for (i = 0; i < item; ++i) { - if (sizes[i] > LIMIT_FRAME) - return -1; - p += sizes[i]; - } - *data = p; - *length = sizes[item]; + p += sizes[i]; + } + *data = p; + *length = sizes[item]; } else { *data = codec_private.data; *length = codec_private.length; @@ -2494,7 +2582,7 @@ nestegg_free_packet(nestegg_packet * pkt) free(block_additional); } - free(pkt); + free(pkt); } int @@ -2588,28 +2676,31 @@ int nestegg_has_cues(nestegg * ctx) { return ctx->segment.cues.cue_point.head || - ne_find_seek_for_id(ctx->segment.seek_head.head, ID_CUES); + ne_find_seek_for_id(ctx->segment.seek_head.head, ID_CUES); } int nestegg_sniff(unsigned char const * buffer, size_t length) { nestegg_io io; - struct sniff_buffer user_data; + struct sniff_buffer userdata; - user_data.buffer = buffer; - user_data.length = length; - user_data.offset = 0; + userdata.buffer = buffer; + userdata.length = length; + userdata.offset = 0; io.read = ne_buffer_read; io.seek = ne_buffer_seek; io.tell = ne_buffer_tell; - io.userdata = &user_data; + io.userdata = &userdata; return ne_match_webm(io, length); } -void +/* From halloc.c */ +int halloc_set_allocator(realloc_t realloc_func); + +int nestegg_set_halloc_func(void * (* realloc_func)(void *, size_t)) { - halloc_allocator = realloc_func; + return halloc_set_allocator(realloc_func); } diff --git a/media/libnestegg/update.sh b/media/libnestegg/update.sh index 27d4ec58b4a6..2d97f3eaec48 100755 --- a/media/libnestegg/update.sh +++ b/media/libnestegg/update.sh @@ -7,7 +7,7 @@ cp $1/halloc/src/halloc.c src cp $1/halloc/src/hlist.h src cp $1/halloc/src/macros.h src cp $1/LICENSE . -cp $1/README . +cp $1/README.md . cp $1/AUTHORS . if [ -d $1/.git ]; then rev=$(cd $1 && git rev-parse --verify HEAD) From a0648a0cdf6ac2f60c7dfede354f0c26a41832cf Mon Sep 17 00:00:00 2001 From: Matthew Gregan Date: Mon, 29 Sep 2014 13:13:21 +1300 Subject: [PATCH 43/82] Bug 1074004 - Fix WebMioData implementation in TestWebMWriter. r=giles --- content/media/gtest/TestWebMWriter.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/content/media/gtest/TestWebMWriter.cpp b/content/media/gtest/TestWebMWriter.cpp index 99339c0f4536..fd58ed071fa8 100644 --- a/content/media/gtest/TestWebMWriter.cpp +++ b/content/media/gtest/TestWebMWriter.cpp @@ -240,8 +240,7 @@ static int webm_read(void* aBuffer, size_t aLength, void* aUserData) // Check the read length. if (aLength > ioData->data.Length()) { - NS_ERROR("Invalid read length"); - return -1; + return 0; } // Check eos. @@ -292,7 +291,7 @@ static int webm_seek(int64_t aOffset, int aWhence, void* aUserData) return -1; } - return 1; + return 0; } static int64_t webm_tell(void* aUserData) From 672372df86ed193c17fe5f79be8b8485d780a6dc Mon Sep 17 00:00:00 2001 From: Matthew Gregan Date: Tue, 30 Sep 2014 14:14:40 +1300 Subject: [PATCH 44/82] Bug 1069660 - Factor Vorbis and Opus decoding out. --- content/media/webm/WebMReader.cpp | 385 ++++++++++++++++-------------- content/media/webm/WebMReader.h | 6 + 2 files changed, 208 insertions(+), 183 deletions(-) diff --git a/content/media/webm/WebMReader.cpp b/content/media/webm/WebMReader.cpp index 0ae1402d038f..a8baac7006b9 100644 --- a/content/media/webm/WebMReader.cpp +++ b/content/media/webm/WebMReader.cpp @@ -531,7 +531,6 @@ bool WebMReader::DecodeAudioPacket(nestegg_packet* aPacket, int64_t aOffset) return false; } - const uint32_t rate = mInfo.mAudio.mRate; uint64_t tstamp_usecs = tstamp / NS_PER_USEC; if (mAudioStartUsec == -1) { // This is the first audio chunk. Assume the start time of our decode @@ -542,8 +541,8 @@ bool WebMReader::DecodeAudioPacket(nestegg_packet* aPacket, int64_t aOffset) // the previous audio chunk, we need to increment the packet count so that // the vorbis decode doesn't use data from before the gap to help decode // from after the gap. - CheckedInt64 tstamp_frames = UsecsToFrames(tstamp_usecs, rate); - CheckedInt64 decoded_frames = UsecsToFrames(mAudioStartUsec, rate); + CheckedInt64 tstamp_frames = UsecsToFrames(tstamp_usecs, mInfo.mAudio.mRate); + CheckedInt64 decoded_frames = UsecsToFrames(mAudioStartUsec, mInfo.mAudio.mRate); if (!tstamp_frames.isValid() || !decoded_frames.isValid()) { NS_WARNING("Int overflow converting WebM times to frames"); return false; @@ -555,7 +554,7 @@ bool WebMReader::DecodeAudioPacket(nestegg_packet* aPacket, int64_t aOffset) } if (tstamp_frames.value() > decoded_frames.value()) { #ifdef DEBUG - CheckedInt64 usecs = FramesToUsecs(tstamp_frames.value() - decoded_frames.value(), rate); + CheckedInt64 usecs = FramesToUsecs(tstamp_frames.value() - decoded_frames.value(), mInfo.mAudio.mRate); LOG(PR_LOG_DEBUG, ("WebMReader detected gap of %lld, %lld frames, in audio stream\n", usecs.isValid() ? usecs.value() : -1, tstamp_frames.value() - decoded_frames.value())); @@ -574,198 +573,218 @@ bool WebMReader::DecodeAudioPacket(nestegg_packet* aPacket, int64_t aOffset) return false; } if (mAudioCodec == NESTEGG_CODEC_VORBIS) { - ogg_packet opacket = InitOggPacket(data, length, false, false, -1); - - if (vorbis_synthesis(&mVorbisBlock, &opacket) != 0) { + if (!DecodeVorbis(data, length, aOffset, tstamp_usecs, &total_frames)) { return false; } - - if (vorbis_synthesis_blockin(&mVorbisDsp, - &mVorbisBlock) != 0) { - return false; - } - - VorbisPCMValue** pcm = 0; - int32_t frames = vorbis_synthesis_pcmout(&mVorbisDsp, &pcm); - // If the first packet of audio in the media produces no data, we - // still need to produce an AudioData for it so that the correct media - // start time is calculated. Otherwise we'd end up with a media start - // time derived from the timecode of the first packet that produced - // data. - if (frames == 0 && mAudioFrames == 0) { - AudioQueue().Push(new AudioData(aOffset, tstamp_usecs, 0, 0, nullptr, mChannels, rate)); - } - while (frames > 0) { - nsAutoArrayPtr buffer(new AudioDataValue[frames * mChannels]); - for (uint32_t j = 0; j < mChannels; ++j) { - VorbisPCMValue* channel = pcm[j]; - for (uint32_t i = 0; i < uint32_t(frames); ++i) { - buffer[i*mChannels + j] = MOZ_CONVERT_VORBIS_SAMPLE(channel[i]); - } - } - - CheckedInt64 duration = FramesToUsecs(frames, rate); - if (!duration.isValid()) { - NS_WARNING("Int overflow converting WebM audio duration"); - return false; - } - CheckedInt64 total_duration = FramesToUsecs(total_frames, rate); - if (!total_duration.isValid()) { - NS_WARNING("Int overflow converting WebM audio total_duration"); - return false; - } - - CheckedInt64 time = total_duration + tstamp_usecs; - if (!time.isValid()) { - NS_WARNING("Int overflow adding total_duration and tstamp_usecs"); - return false; - }; - - total_frames += frames; - AudioQueue().Push(new AudioData(aOffset, - time.value(), - duration.value(), - frames, - buffer.forget(), - mChannels, - rate)); - mAudioFrames += frames; - if (vorbis_synthesis_read(&mVorbisDsp, frames) != 0) { - return false; - } - - frames = vorbis_synthesis_pcmout(&mVorbisDsp, &pcm); - } } else if (mAudioCodec == NESTEGG_CODEC_OPUS) { #ifdef MOZ_OPUS - uint32_t channels = mOpusParser->mChannels; - - // Maximum value is 63*2880, so there's no chance of overflow. - int32_t frames_number = opus_packet_get_nb_frames(data, length); - - if (frames_number <= 0) - return false; // Invalid packet header. - int32_t samples = opus_packet_get_samples_per_frame(data, - (opus_int32) rate); - int32_t frames = frames_number*samples; - - // A valid Opus packet must be between 2.5 and 120 ms long. - if (frames < 120 || frames > 5760) + if (!DecodeOpus(data, length, aOffset, tstamp_usecs, aPacket)) { return false; - nsAutoArrayPtr buffer(new AudioDataValue[frames * channels]); - - // Decode to the appropriate sample type. -#ifdef MOZ_SAMPLE_TYPE_FLOAT32 - int ret = opus_multistream_decode_float(mOpusDecoder, - data, length, - buffer, frames, false); -#else - int ret = opus_multistream_decode(mOpusDecoder, - data, length, - buffer, frames, false); -#endif - if (ret < 0) - return false; - NS_ASSERTION(ret == frames, "Opus decoded too few audio samples"); - CheckedInt64 startTime = tstamp_usecs; - - // Trim the initial frames while the decoder is settling. - if (mSkip > 0) { - int32_t skipFrames = std::min(mSkip, frames); - if (skipFrames == frames) { - // discard the whole packet - mSkip -= frames; - LOG(PR_LOG_DEBUG, ("Opus decoder skipping %d frames" - " (whole packet)", frames)); - return true; - } - int32_t keepFrames = frames - skipFrames; - int samples = keepFrames * channels; - nsAutoArrayPtr trimBuffer(new AudioDataValue[samples]); - PodCopy(trimBuffer.get(), buffer.get() + skipFrames*channels, samples); - startTime = startTime + FramesToUsecs(skipFrames, rate); - frames = keepFrames; - buffer = trimBuffer; - - mSkip -= skipFrames; - LOG(PR_LOG_DEBUG, ("Opus decoder skipping %d frames", skipFrames)); - } - - int64_t discardPadding = 0; - r = nestegg_packet_discard_padding(aPacket, &discardPadding); - if (discardPadding > 0) { - CheckedInt64 discardFrames = UsecsToFrames(discardPadding / NS_PER_USEC, rate); - if (!discardFrames.isValid()) { - NS_WARNING("Int overflow in DiscardPadding"); - return false; - } - if (discardFrames.value() >= frames) { - LOG(PR_LOG_DEBUG, ("Opus decoder discarding whole packet" - " (%d frames) as padding (%lld discarded)", - frames, discardFrames.value())); - return true; - } - int32_t keepFrames = frames - discardFrames.value(); - int32_t samples = keepFrames * channels; - nsAutoArrayPtr trimBuffer(new AudioDataValue[samples]); - PodCopy(trimBuffer.get(), buffer.get(), samples); - frames = keepFrames; - buffer = trimBuffer; - } - - // Apply the header gain if one was specified. -#ifdef MOZ_SAMPLE_TYPE_FLOAT32 - if (mOpusParser->mGain != 1.0f) { - float gain = mOpusParser->mGain; - int samples = frames * channels; - for (int i = 0; i < samples; i++) { - buffer[i] *= gain; - } - } -#else - if (mOpusParser->mGain_Q16 != 65536) { - int64_t gain_Q16 = mOpusParser->mGain_Q16; - int samples = frames * channels; - for (int i = 0; i < samples; i++) { - int32_t val = static_cast((gain_Q16*buffer[i] + 32768)>>16); - buffer[i] = static_cast(MOZ_CLIP_TO_15(val)); - } } #endif - - // No channel mapping for more than 8 channels. - if (channels > 8) { - return false; - } - - CheckedInt64 duration = FramesToUsecs(frames, rate); - if (!duration.isValid()) { - NS_WARNING("Int overflow converting WebM audio duration"); - return false; - } - CheckedInt64 time = startTime - mCodecDelay; - if (!time.isValid()) { - NS_WARNING("Int overflow shifting tstamp by codec delay"); - return false; - }; - AudioQueue().Push(new AudioData(mDecoder->GetResource()->Tell(), - time.value(), - duration.value(), - frames, - buffer.forget(), - mChannels, - rate)); - - mAudioFrames += frames; -#else - return false; -#endif /* MOZ_OPUS */ } } return true; } +bool WebMReader::DecodeVorbis(unsigned char* aData, size_t aLength, + int64_t aOffset, uint64_t aTstampUsecs, int32_t* aTotalFrames) +{ + ogg_packet opacket = InitOggPacket(aData, aLength, false, false, -1); + + if (vorbis_synthesis(&mVorbisBlock, &opacket) != 0) { + return false; + } + + if (vorbis_synthesis_blockin(&mVorbisDsp, + &mVorbisBlock) != 0) { + return false; + } + + VorbisPCMValue** pcm = 0; + int32_t frames = vorbis_synthesis_pcmout(&mVorbisDsp, &pcm); + // If the first packet of audio in the media produces no data, we + // still need to produce an AudioData for it so that the correct media + // start time is calculated. Otherwise we'd end up with a media start + // time derived from the timecode of the first packet that produced + // data. + if (frames == 0 && mAudioFrames == 0) { + AudioQueue().Push(new AudioData(aOffset, aTstampUsecs, 0, 0, nullptr, mChannels, mInfo.mAudio.mRate)); + } + while (frames > 0) { + nsAutoArrayPtr buffer(new AudioDataValue[frames * mChannels]); + for (uint32_t j = 0; j < mChannels; ++j) { + VorbisPCMValue* channel = pcm[j]; + for (uint32_t i = 0; i < uint32_t(frames); ++i) { + buffer[i*mChannels + j] = MOZ_CONVERT_VORBIS_SAMPLE(channel[i]); + } + } + + CheckedInt64 duration = FramesToUsecs(frames, mInfo.mAudio.mRate); + if (!duration.isValid()) { + NS_WARNING("Int overflow converting WebM audio duration"); + return false; + } + CheckedInt64 total_duration = FramesToUsecs(*aTotalFrames, mInfo.mAudio.mRate); + if (!total_duration.isValid()) { + NS_WARNING("Int overflow converting WebM audio total_duration"); + return false; + } + + CheckedInt64 time = total_duration + aTstampUsecs; + if (!time.isValid()) { + NS_WARNING("Int overflow adding total_duration and aTstampUsecs"); + return false; + }; + + *aTotalFrames += frames; + AudioQueue().Push(new AudioData(aOffset, + time.value(), + duration.value(), + frames, + buffer.forget(), + mChannels, + mInfo.mAudio.mRate)); + mAudioFrames += frames; + if (vorbis_synthesis_read(&mVorbisDsp, frames) != 0) { + return false; + } + + frames = vorbis_synthesis_pcmout(&mVorbisDsp, &pcm); + } + + return true; +} + +#ifdef MOZ_OPUS +bool WebMReader::DecodeOpus(unsigned char* aData, size_t aLength, + int64_t aOffset, uint64_t aTstampUsecs, nestegg_packet* aPacket) +{ + uint32_t channels = mOpusParser->mChannels; + + // Maximum value is 63*2880, so there's no chance of overflow. + int32_t frames_number = opus_packet_get_nb_frames(aData, aLength); + + if (frames_number <= 0) + return false; // Invalid packet header. + int32_t samples = opus_packet_get_samples_per_frame(aData, + (opus_int32) mInfo.mAudio.mRate); + int32_t frames = frames_number*samples; + + // A valid Opus packet must be between 2.5 and 120 ms long. + if (frames < 120 || frames > 5760) + return false; + nsAutoArrayPtr buffer(new AudioDataValue[frames * channels]); + + // Decode to the appropriate sample type. +#ifdef MOZ_SAMPLE_TYPE_FLOAT32 + int ret = opus_multistream_decode_float(mOpusDecoder, + aData, aLength, + buffer, frames, false); +#else + int ret = opus_multistream_decode(mOpusDecoder, + aData, aLength, + buffer, frames, false); +#endif + if (ret < 0) + return false; + NS_ASSERTION(ret == frames, "Opus decoded too few audio samples"); + CheckedInt64 startTime = aTstampUsecs; + + // Trim the initial frames while the decoder is settling. + if (mSkip > 0) { + int32_t skipFrames = std::min(mSkip, frames); + if (skipFrames == frames) { + // discard the whole packet + mSkip -= frames; + LOG(PR_LOG_DEBUG, ("Opus decoder skipping %d frames" + " (whole packet)", frames)); + return true; + } + int32_t keepFrames = frames - skipFrames; + int samples = keepFrames * channels; + nsAutoArrayPtr trimBuffer(new AudioDataValue[samples]); + PodCopy(trimBuffer.get(), buffer.get() + skipFrames*channels, samples); + startTime = startTime + FramesToUsecs(skipFrames, mInfo.mAudio.mRate); + frames = keepFrames; + buffer = trimBuffer; + + mSkip -= skipFrames; + LOG(PR_LOG_DEBUG, ("Opus decoder skipping %d frames", skipFrames)); + } + + int64_t discardPadding = 0; + (void) nestegg_packet_discard_padding(aPacket, &discardPadding); + if (discardPadding > 0) { + CheckedInt64 discardFrames = UsecsToFrames(discardPadding / NS_PER_USEC, mInfo.mAudio.mRate); + if (!discardFrames.isValid()) { + NS_WARNING("Int overflow in DiscardPadding"); + return false; + } + if (discardFrames.value() >= frames) { + LOG(PR_LOG_DEBUG, ("Opus decoder discarding whole packet" + " (%d frames) as padding (%lld discarded)", + frames, discardFrames.value())); + return true; + } + int32_t keepFrames = frames - discardFrames.value(); + int32_t samples = keepFrames * channels; + nsAutoArrayPtr trimBuffer(new AudioDataValue[samples]); + PodCopy(trimBuffer.get(), buffer.get(), samples); + frames = keepFrames; + buffer = trimBuffer; + } + + // Apply the header gain if one was specified. +#ifdef MOZ_SAMPLE_TYPE_FLOAT32 + if (mOpusParser->mGain != 1.0f) { + float gain = mOpusParser->mGain; + int samples = frames * channels; + for (int i = 0; i < samples; i++) { + buffer[i] *= gain; + } + } +#else + if (mOpusParser->mGain_Q16 != 65536) { + int64_t gain_Q16 = mOpusParser->mGain_Q16; + int samples = frames * channels; + for (int i = 0; i < samples; i++) { + int32_t val = static_cast((gain_Q16*buffer[i] + 32768)>>16); + buffer[i] = static_cast(MOZ_CLIP_TO_15(val)); + } + } +#endif + + // No channel mapping for more than 8 channels. + if (channels > 8) { + return false; + } + + CheckedInt64 duration = FramesToUsecs(frames, mInfo.mAudio.mRate); + if (!duration.isValid()) { + NS_WARNING("Int overflow converting WebM audio duration"); + return false; + } + CheckedInt64 time = startTime - mCodecDelay; + if (!time.isValid()) { + NS_WARNING("Int overflow shifting tstamp by codec delay"); + return false; + }; + AudioQueue().Push(new AudioData(aOffset, + time.value(), + duration.value(), + frames, + buffer.forget(), + mChannels, + mInfo.mAudio.mRate)); + + mAudioFrames += frames; + + return true; +} +#endif /* MOZ_OPUS */ + nsReturnRef WebMReader::NextPacket(TrackType aTrackType) { // The packet queue that packets will be pushed on if they diff --git a/content/media/webm/WebMReader.h b/content/media/webm/WebMReader.h index 3eece17f9ca5..6078e3b77965 100644 --- a/content/media/webm/WebMReader.h +++ b/content/media/webm/WebMReader.h @@ -176,6 +176,12 @@ protected: // must be held during this call. The caller is responsible for freeing // aPacket. bool DecodeAudioPacket(nestegg_packet* aPacket, int64_t aOffset); + bool DecodeVorbis(unsigned char* aData, size_t aLength, + int64_t aOffset, uint64_t aTstampUsecs, int32_t* aTotalFrames); +#ifdef MOZ_OPUS + bool DecodeOpus(unsigned char* aData, size_t aLength, + int64_t aOffset, uint64_t aTstampUsecs, nestegg_packet* aPacket); +#endif // Release context and set to null. Called when an error occurs during // reading metadata or destruction of the reader itself. From fe0dfa0ede5d8d5d0a1b0cb47f804d2966fceeea Mon Sep 17 00:00:00 2001 From: Matthew Gregan Date: Tue, 30 Sep 2014 14:14:40 +1300 Subject: [PATCH 45/82] Bug 1069660 - WebMReader cleanups. --- content/media/webm/WebMReader.cpp | 88 +++++++++++++------------------ content/media/webm/WebMReader.h | 15 ++---- 2 files changed, 41 insertions(+), 62 deletions(-) diff --git a/content/media/webm/WebMReader.cpp b/content/media/webm/WebMReader.cpp index a8baac7006b9..bd98a8b99989 100644 --- a/content/media/webm/WebMReader.cpp +++ b/content/media/webm/WebMReader.cpp @@ -143,11 +143,24 @@ static void webm_log(nestegg * context, #endif } +ogg_packet +InitOggPacket(const unsigned char* aData, size_t aLength, bool aBOS, bool aEOS, + int64_t aGranulepos, int64_t aPacketNo) +{ + ogg_packet packet; + packet.packet = const_cast(aData); + packet.bytes = aLength; + packet.b_o_s = aBOS; + packet.e_o_s = aEOS; + packet.granulepos = aGranulepos; + packet.packetno = aPacketNo; + return packet; +} + WebMReader::WebMReader(AbstractMediaDecoder* aDecoder) : MediaDecoderReader(aDecoder), mContext(nullptr), mPacketCount(0), - mChannels(0), #ifdef MOZ_OPUS mOpusParser(nullptr), mOpusDecoder(nullptr), @@ -399,7 +412,7 @@ nsresult WebMReader::ReadMetadata(MediaInfo* aInfo, Cleanup(); return NS_ERROR_FAILURE; } - ogg_packet opacket = InitOggPacket(data, length, header == 0, false, 0); + ogg_packet opacket = InitOggPacket(data, length, header == 0, false, 0, mPacketCount++); r = vorbis_synthesis_headerin(&mVorbisInfo, &mVorbisComment, @@ -424,7 +437,6 @@ nsresult WebMReader::ReadMetadata(MediaInfo* aInfo, mInfo.mAudio.mRate = mVorbisDsp.vi->rate; mInfo.mAudio.mChannels = mVorbisDsp.vi->channels; - mChannels = mInfo.mAudio.mChannels; #ifdef MOZ_OPUS } else if (mAudioCodec == NESTEGG_CODEC_OPUS) { unsigned char* data = 0; @@ -456,7 +468,6 @@ nsresult WebMReader::ReadMetadata(MediaInfo* aInfo, mInfo.mAudio.mRate = mOpusParser->mRate; mInfo.mAudio.mChannels = mOpusParser->mChannels; - mChannels = mInfo.mAudio.mChannels; mSeekPreroll = params.seek_preroll; #endif } else { @@ -487,33 +498,17 @@ bool WebMReader::InitOpusDecoder() NS_ASSERTION(mOpusDecoder == nullptr, "leaking OpusDecoder"); mOpusDecoder = opus_multistream_decoder_create(mOpusParser->mRate, - mOpusParser->mChannels, - mOpusParser->mStreams, - mOpusParser->mCoupledStreams, - mOpusParser->mMappingTable, - &r); + mOpusParser->mChannels, + mOpusParser->mStreams, + mOpusParser->mCoupledStreams, + mOpusParser->mMappingTable, + &r); mSkip = mOpusParser->mPreSkip; return r == OPUS_OK; } #endif -ogg_packet WebMReader::InitOggPacket(unsigned char* aData, - size_t aLength, - bool aBOS, - bool aEOS, - int64_t aGranulepos) -{ - ogg_packet packet; - packet.packet = aData; - packet.bytes = aLength; - packet.b_o_s = aBOS; - packet.e_o_s = aEOS; - packet.granulepos = aGranulepos; - packet.packetno = mPacketCount++; - return packet; -} - bool WebMReader::DecodeAudioPacket(nestegg_packet* aPacket, int64_t aOffset) { NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread."); @@ -588,10 +583,10 @@ bool WebMReader::DecodeAudioPacket(nestegg_packet* aPacket, int64_t aOffset) return true; } -bool WebMReader::DecodeVorbis(unsigned char* aData, size_t aLength, +bool WebMReader::DecodeVorbis(const unsigned char* aData, size_t aLength, int64_t aOffset, uint64_t aTstampUsecs, int32_t* aTotalFrames) { - ogg_packet opacket = InitOggPacket(aData, aLength, false, false, -1); + ogg_packet opacket = InitOggPacket(aData, aLength, false, false, -1, mPacketCount++); if (vorbis_synthesis(&mVorbisBlock, &opacket) != 0) { return false; @@ -610,14 +605,14 @@ bool WebMReader::DecodeVorbis(unsigned char* aData, size_t aLength, // time derived from the timecode of the first packet that produced // data. if (frames == 0 && mAudioFrames == 0) { - AudioQueue().Push(new AudioData(aOffset, aTstampUsecs, 0, 0, nullptr, mChannels, mInfo.mAudio.mRate)); + AudioQueue().Push(new AudioData(aOffset, aTstampUsecs, 0, 0, nullptr, mInfo.mAudio.mChannels, mInfo.mAudio.mRate)); } while (frames > 0) { - nsAutoArrayPtr buffer(new AudioDataValue[frames * mChannels]); - for (uint32_t j = 0; j < mChannels; ++j) { + nsAutoArrayPtr buffer(new AudioDataValue[frames * mInfo.mAudio.mChannels]); + for (uint32_t j = 0; j < mInfo.mAudio.mChannels; ++j) { VorbisPCMValue* channel = pcm[j]; for (uint32_t i = 0; i < uint32_t(frames); ++i) { - buffer[i*mChannels + j] = MOZ_CONVERT_VORBIS_SAMPLE(channel[i]); + buffer[i*mInfo.mAudio.mChannels + j] = MOZ_CONVERT_VORBIS_SAMPLE(channel[i]); } } @@ -644,7 +639,7 @@ bool WebMReader::DecodeVorbis(unsigned char* aData, size_t aLength, duration.value(), frames, buffer.forget(), - mChannels, + mInfo.mAudio.mChannels, mInfo.mAudio.mRate)); mAudioFrames += frames; if (vorbis_synthesis_read(&mVorbisDsp, frames) != 0) { @@ -658,23 +653,28 @@ bool WebMReader::DecodeVorbis(unsigned char* aData, size_t aLength, } #ifdef MOZ_OPUS -bool WebMReader::DecodeOpus(unsigned char* aData, size_t aLength, +bool WebMReader::DecodeOpus(const unsigned char* aData, size_t aLength, int64_t aOffset, uint64_t aTstampUsecs, nestegg_packet* aPacket) { uint32_t channels = mOpusParser->mChannels; + // No channel mapping for more than 8 channels. + if (channels > 8) { + return false; + } // Maximum value is 63*2880, so there's no chance of overflow. int32_t frames_number = opus_packet_get_nb_frames(aData, aLength); - if (frames_number <= 0) return false; // Invalid packet header. + int32_t samples = opus_packet_get_samples_per_frame(aData, (opus_int32) mInfo.mAudio.mRate); - int32_t frames = frames_number*samples; - // A valid Opus packet must be between 2.5 and 120 ms long. + // A valid Opus packet must be between 2.5 and 120 ms long (48kHz). + int32_t frames = frames_number*samples; if (frames < 120 || frames > 5760) return false; + nsAutoArrayPtr buffer(new AudioDataValue[frames * channels]); // Decode to the appropriate sample type. @@ -703,12 +703,9 @@ bool WebMReader::DecodeOpus(unsigned char* aData, size_t aLength, return true; } int32_t keepFrames = frames - skipFrames; - int samples = keepFrames * channels; - nsAutoArrayPtr trimBuffer(new AudioDataValue[samples]); - PodCopy(trimBuffer.get(), buffer.get() + skipFrames*channels, samples); + PodMove(buffer.get(), buffer.get() + skipFrames * channels, keepFrames * channels); startTime = startTime + FramesToUsecs(skipFrames, mInfo.mAudio.mRate); frames = keepFrames; - buffer = trimBuffer; mSkip -= skipFrames; LOG(PR_LOG_DEBUG, ("Opus decoder skipping %d frames", skipFrames)); @@ -729,11 +726,7 @@ bool WebMReader::DecodeOpus(unsigned char* aData, size_t aLength, return true; } int32_t keepFrames = frames - discardFrames.value(); - int32_t samples = keepFrames * channels; - nsAutoArrayPtr trimBuffer(new AudioDataValue[samples]); - PodCopy(trimBuffer.get(), buffer.get(), samples); frames = keepFrames; - buffer = trimBuffer; } // Apply the header gain if one was specified. @@ -756,11 +749,6 @@ bool WebMReader::DecodeOpus(unsigned char* aData, size_t aLength, } #endif - // No channel mapping for more than 8 channels. - if (channels > 8) { - return false; - } - CheckedInt64 duration = FramesToUsecs(frames, mInfo.mAudio.mRate); if (!duration.isValid()) { NS_WARNING("Int overflow converting WebM audio duration"); @@ -776,7 +764,7 @@ bool WebMReader::DecodeOpus(unsigned char* aData, size_t aLength, duration.value(), frames, buffer.forget(), - mChannels, + mInfo.mAudio.mChannels, mInfo.mAudio.mRate)); mAudioFrames += frames; diff --git a/content/media/webm/WebMReader.h b/content/media/webm/WebMReader.h index 6078e3b77965..0d46a53ef199 100644 --- a/content/media/webm/WebMReader.h +++ b/content/media/webm/WebMReader.h @@ -157,13 +157,6 @@ protected: // Pushes a packet to the front of the video packet queue. virtual void PushVideoPacket(NesteggPacketHolder* aItem); - // Returns an initialized ogg packet with data obtained from the WebM container. - ogg_packet InitOggPacket(unsigned char* aData, - size_t aLength, - bool aBOS, - bool aEOS, - int64_t aGranulepos); - #ifdef MOZ_OPUS // Setup opus decoder bool InitOpusDecoder(); @@ -176,10 +169,10 @@ protected: // must be held during this call. The caller is responsible for freeing // aPacket. bool DecodeAudioPacket(nestegg_packet* aPacket, int64_t aOffset); - bool DecodeVorbis(unsigned char* aData, size_t aLength, + bool DecodeVorbis(const unsigned char* aData, size_t aLength, int64_t aOffset, uint64_t aTstampUsecs, int32_t* aTotalFrames); #ifdef MOZ_OPUS - bool DecodeOpus(unsigned char* aData, size_t aLength, + bool DecodeOpus(const unsigned char* aData, size_t aLength, int64_t aOffset, uint64_t aTstampUsecs, nestegg_packet* aPacket); #endif @@ -200,9 +193,7 @@ private: vorbis_comment mVorbisComment; vorbis_dsp_state mVorbisDsp; vorbis_block mVorbisBlock; - uint32_t mPacketCount; - uint32_t mChannels; - + int64_t mPacketCount; #ifdef MOZ_OPUS // Opus decoder state From b6efffa7210b2959547f1fc14b87ac7c68fc5cbd Mon Sep 17 00:00:00 2001 From: Matthew Gregan Date: Tue, 30 Sep 2014 14:14:40 +1300 Subject: [PATCH 46/82] Bug 1069660 - Enforce DiscardPadding validity. --- content/media/webm/WebMReader.cpp | 80 ++++++++++++++++++------------- content/media/webm/WebMReader.h | 9 +++- 2 files changed, 56 insertions(+), 33 deletions(-) diff --git a/content/media/webm/WebMReader.cpp b/content/media/webm/WebMReader.cpp index bd98a8b99989..152e93604f01 100644 --- a/content/media/webm/WebMReader.cpp +++ b/content/media/webm/WebMReader.cpp @@ -158,24 +158,26 @@ InitOggPacket(const unsigned char* aData, size_t aLength, bool aBOS, bool aEOS, } WebMReader::WebMReader(AbstractMediaDecoder* aDecoder) - : MediaDecoderReader(aDecoder), - mContext(nullptr), - mPacketCount(0), + : MediaDecoderReader(aDecoder) + , mContext(nullptr) + , mPacketCount(0) #ifdef MOZ_OPUS - mOpusParser(nullptr), - mOpusDecoder(nullptr), - mSkip(0), - mSeekPreroll(0), + , mOpusDecoder(nullptr) + , mSkip(0) + , mSeekPreroll(0) +#endif + , mVideoTrack(0) + , mAudioTrack(0) + , mAudioStartUsec(-1) + , mAudioFrames(0) + , mLastVideoFrameTime(0) + , mAudioCodec(-1) + , mVideoCodec(-1) + , mHasVideo(false) + , mHasAudio(false) +#ifdef MOZ_OPUS + , mPaddingDiscarded(false) #endif - mVideoTrack(0), - mAudioTrack(0), - mAudioStartUsec(-1), - mAudioFrames(0), - mLastVideoFrameTime(0), - mAudioCodec(-1), - mVideoCodec(-1), - mHasVideo(false), - mHasAudio(false) { MOZ_COUNT_CTOR(WebMReader); #ifdef PR_LOGGING @@ -251,6 +253,7 @@ nsresult WebMReader::ResetDecode() // Reset the decoder. opus_multistream_decoder_ctl(mOpusDecoder, OPUS_RESET_STATE); mSkip = mOpusParser->mPreSkip; + mPaddingDiscarded = false; } #endif } @@ -504,6 +507,7 @@ bool WebMReader::InitOpusDecoder() mOpusParser->mMappingTable, &r); mSkip = mOpusParser->mPreSkip; + mPaddingDiscarded = false; return r == OPUS_OK; } @@ -662,10 +666,19 @@ bool WebMReader::DecodeOpus(const unsigned char* aData, size_t aLength, return false; } + if (mPaddingDiscarded) { + // Discard padding should be used only on the final packet, so + // decoding after a padding discard is invalid. + LOG(PR_LOG_DEBUG, ("Opus decoder error, discard padding on interstitial packet")); + GetCallback()->OnDecodeError(); + return false; + } + // Maximum value is 63*2880, so there's no chance of overflow. int32_t frames_number = opus_packet_get_nb_frames(aData, aLength); - if (frames_number <= 0) + if (frames_number <= 0) { return false; // Invalid packet header. + } int32_t samples = opus_packet_get_samples_per_frame(aData, (opus_int32) mInfo.mAudio.mRate); @@ -694,37 +707,40 @@ bool WebMReader::DecodeOpus(const unsigned char* aData, size_t aLength, // Trim the initial frames while the decoder is settling. if (mSkip > 0) { - int32_t skipFrames = std::min(mSkip, frames); - if (skipFrames == frames) { - // discard the whole packet - mSkip -= frames; - LOG(PR_LOG_DEBUG, ("Opus decoder skipping %d frames" - " (whole packet)", frames)); - return true; - } + int32_t skipFrames = std::min(mSkip, frames); int32_t keepFrames = frames - skipFrames; + LOG(PR_LOG_DEBUG, ("Opus decoder skipping %d of %d frames", skipFrames, frames)); PodMove(buffer.get(), buffer.get() + skipFrames * channels, keepFrames * channels); startTime = startTime + FramesToUsecs(skipFrames, mInfo.mAudio.mRate); frames = keepFrames; - mSkip -= skipFrames; - LOG(PR_LOG_DEBUG, ("Opus decoder skipping %d frames", skipFrames)); } int64_t discardPadding = 0; (void) nestegg_packet_discard_padding(aPacket, &discardPadding); + if (discardPadding < 0) { + // Negative discard padding is invalid. + LOG(PR_LOG_DEBUG, ("Opus decoder error, negative discard padding")); + GetCallback()->OnDecodeError(); + return false; + } if (discardPadding > 0) { CheckedInt64 discardFrames = UsecsToFrames(discardPadding / NS_PER_USEC, mInfo.mAudio.mRate); if (!discardFrames.isValid()) { NS_WARNING("Int overflow in DiscardPadding"); return false; } - if (discardFrames.value() >= frames) { - LOG(PR_LOG_DEBUG, ("Opus decoder discarding whole packet" - " (%d frames) as padding (%lld discarded)", - frames, discardFrames.value())); - return true; + if (discardFrames.value() > frames) { + // Discarding more than the entire packet is invalid. + LOG(PR_LOG_DEBUG, ("Opus decoder error, discard padding larger than packet")); + GetCallback()->OnDecodeError(); + return false; } + LOG(PR_LOG_DEBUG, ("Opus decoder discarding %d of %d frames", int32_t(discardFrames.value()), frames)); + // Padding discard is only supposed to happen on the final packet. + // Record the discard so we can return an error if another packet is + // decoded. + mPaddingDiscarded = true; int32_t keepFrames = frames - discardFrames.value(); frames = keepFrames; } diff --git a/content/media/webm/WebMReader.h b/content/media/webm/WebMReader.h index 0d46a53ef199..55fd4fe767b5 100644 --- a/content/media/webm/WebMReader.h +++ b/content/media/webm/WebMReader.h @@ -199,7 +199,7 @@ private: // Opus decoder state nsAutoPtr mOpusParser; OpusMSDecoder *mOpusDecoder; - int mSkip; // Number of samples left to trim before playback. + uint16_t mSkip; // Number of samples left to trim before playback. uint64_t mSeekPreroll; // Number of nanoseconds that must be discarded after seeking. #endif @@ -244,6 +244,13 @@ private: // Booleans to indicate if we have audio and/or video data bool mHasVideo; bool mHasAudio; + +#ifdef MOZ_OPUS + // Opus padding should only be discarded on the final packet. Once this + // is set to true, if the reader attempts to decode any further packets it + // will raise an error so we can indicate that the file is invalid. + bool mPaddingDiscarded; +#endif }; } // namespace mozilla From a6330ee024f9095a9d9eca5aa7dbca6830d5b3f8 Mon Sep 17 00:00:00 2001 From: Matthew Gregan Date: Tue, 30 Sep 2014 14:14:41 +1300 Subject: [PATCH 47/82] Bug 1069660 - Add mochitests for WebM/Opus files with invalid DiscardPadding. --- .../test/invalid-discard_on_multi_blocks.webm | Bin 0 -> 19636 bytes ...alid-discard_on_multi_blocks.webm^headers^ | 1 + .../media/test/invalid-excess_discard.webm | Bin 0 -> 18442 bytes .../test/invalid-excess_discard.webm^headers^ | 1 + .../test/invalid-excess_neg_discard.webm | Bin 0 -> 18442 bytes .../invalid-excess_neg_discard.webm^headers^ | 1 + content/media/test/invalid-neg_discard.webm | Bin 0 -> 18442 bytes .../test/invalid-neg_discard.webm^headers^ | 1 + content/media/test/manifest.js | 7 +++ content/media/test/mochitest.ini | 9 ++++ content/media/test/test_invalid_reject.html | 4 +- .../media/test/test_invalid_reject_play.html | 44 ++++++++++++++++++ content/media/test/test_media_selection.html | 4 +- 13 files changed, 68 insertions(+), 4 deletions(-) create mode 100644 content/media/test/invalid-discard_on_multi_blocks.webm create mode 100644 content/media/test/invalid-discard_on_multi_blocks.webm^headers^ create mode 100644 content/media/test/invalid-excess_discard.webm create mode 100644 content/media/test/invalid-excess_discard.webm^headers^ create mode 100644 content/media/test/invalid-excess_neg_discard.webm create mode 100644 content/media/test/invalid-excess_neg_discard.webm^headers^ create mode 100644 content/media/test/invalid-neg_discard.webm create mode 100644 content/media/test/invalid-neg_discard.webm^headers^ create mode 100644 content/media/test/test_invalid_reject_play.html diff --git a/content/media/test/invalid-discard_on_multi_blocks.webm b/content/media/test/invalid-discard_on_multi_blocks.webm new file mode 100644 index 0000000000000000000000000000000000000000..f39ab5bcb8c86009c7d77d52360a5c3c340add18 GIT binary patch literal 19636 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 literal 0 HcmV?d00001 diff --git a/content/media/test/invalid-discard_on_multi_blocks.webm^headers^ b/content/media/test/invalid-discard_on_multi_blocks.webm^headers^ new file mode 100644 index 000000000000..4030ea1d3ddb --- /dev/null +++ b/content/media/test/invalid-discard_on_multi_blocks.webm^headers^ @@ -0,0 +1 @@ +Cache-Control: no-store diff --git a/content/media/test/invalid-excess_discard.webm b/content/media/test/invalid-excess_discard.webm new file mode 100644 index 0000000000000000000000000000000000000000..5b34aca1a7d64f36afc12cc125816121bb30211e GIT binary patch literal 18442 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 literal 0 HcmV?d00001 diff --git a/content/media/test/invalid-excess_discard.webm^headers^ b/content/media/test/invalid-excess_discard.webm^headers^ new file mode 100644 index 000000000000..4030ea1d3ddb --- /dev/null +++ b/content/media/test/invalid-excess_discard.webm^headers^ @@ -0,0 +1 @@ +Cache-Control: no-store diff --git a/content/media/test/invalid-excess_neg_discard.webm b/content/media/test/invalid-excess_neg_discard.webm new file mode 100644 index 0000000000000000000000000000000000000000..2bfad6ed21c611d6bfebd9a31fdd0f2da70dffc4 GIT binary patch literal 18442 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 literal 0 HcmV?d00001 diff --git a/content/media/test/invalid-excess_neg_discard.webm^headers^ b/content/media/test/invalid-excess_neg_discard.webm^headers^ new file mode 100644 index 000000000000..4030ea1d3ddb --- /dev/null +++ b/content/media/test/invalid-excess_neg_discard.webm^headers^ @@ -0,0 +1 @@ +Cache-Control: no-store diff --git a/content/media/test/invalid-neg_discard.webm b/content/media/test/invalid-neg_discard.webm new file mode 100644 index 0000000000000000000000000000000000000000..3f665c0b5966314ff01613dd7516dfbd40c3d9b6 GIT binary patch literal 18442 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 literal 0 HcmV?d00001 diff --git a/content/media/test/invalid-neg_discard.webm^headers^ b/content/media/test/invalid-neg_discard.webm^headers^ new file mode 100644 index 000000000000..4030ea1d3ddb --- /dev/null +++ b/content/media/test/invalid-neg_discard.webm^headers^ @@ -0,0 +1 @@ +Cache-Control: no-store diff --git a/content/media/test/manifest.js b/content/media/test/manifest.js index 10182c424274..10d5c968cb7f 100644 --- a/content/media/test/manifest.js +++ b/content/media/test/manifest.js @@ -251,6 +251,13 @@ var gInvalidTests = [ { name:"invalid-preskip.webm", type:"audio/webm; codecs=opus"}, ]; +var gInvalidPlayTests = [ + { name:"invalid-excess_discard.webm", type:"audio/webm; codecs=opus"}, + { name:"invalid-excess_neg_discard.webm", type:"audio/webm; codecs=opus"}, + { name:"invalid-neg_discard.webm", type:"audio/webm; codecs=opus"}, + { name:"invalid-discard_on_multi_blocks.webm", type:"audio/webm; codecs=opus"}, +]; + // Files to check different cases of ogg skeleton information. // sample-fisbone-skeleton4.ogv // - Skeleton v4, w/ Content-Type,Role,Name,Language,Title for both theora/vorbis diff --git a/content/media/test/mochitest.ini b/content/media/test/mochitest.ini index efbed9fc724a..371551641287 100644 --- a/content/media/test/mochitest.ini +++ b/content/media/test/mochitest.ini @@ -157,6 +157,12 @@ support-files = invalid-cmap-s1c2.opus^headers^ invalid-cmap-short.opus invalid-cmap-short.opus^headers^ + invalid-discard_on_multi_blocks.webm + invalid-discard_on_multi_blocks.webm^headers^ + invalid-excess_discard.webm + invalid-excess_discard.webm^headers^ + invalid-excess_neg_discard.webm + invalid-excess_neg_discard.webm^headers^ invalid-m0c0.opus invalid-m0c0.opus^headers^ invalid-m0c3.opus @@ -169,6 +175,8 @@ support-files = invalid-m2c0.opus^headers^ invalid-m2c1.opus invalid-m2c1.opus^headers^ + invalid-neg_discard.webm + invalid-neg_discard.webm^headers^ invalid-preskip.webm invalid-preskip.webm^headers^ long.vtt @@ -350,6 +358,7 @@ skip-if = toolkit == 'android' # bug 608634 [test_imagecapture.html] [test_info_leak.html] [test_invalid_reject.html] +[test_invalid_reject_play.html] [test_invalid_seek.html] [test_load.html] [test_load_candidates.html] diff --git a/content/media/test/test_invalid_reject.html b/content/media/test/test_invalid_reject.html index 55635d51df39..8ef614aad5ed 100644 --- a/content/media/test/test_invalid_reject.html +++ b/content/media/test/test_invalid_reject.html @@ -31,10 +31,10 @@ function startTest(test, token) { }); // Seeing a decoder error is a success. - v.addEventListener("error", function(e) { + v.addEventListener("error", function onerror(e) { is(v.error.code, v.error.MEDIA_ERR_DECODE, "decoder should reject " + test.name); - v.removeEventListener('error', arguments.callee, false); + v.removeEventListener('error', onerror, false); manager.finished(token); }); diff --git a/content/media/test/test_invalid_reject_play.html b/content/media/test/test_invalid_reject_play.html new file mode 100644 index 000000000000..5d9252d53006 --- /dev/null +++ b/content/media/test/test_invalid_reject_play.html @@ -0,0 +1,44 @@ + + + + + Test rejection of invalid files during playback + + + + + +

+
+
+ + diff --git a/content/media/test/test_media_selection.html b/content/media/test/test_media_selection.html index 6cb0e1d26a99..a2eac8cb81bc 100644 --- a/content/media/test/test_media_selection.html +++ b/content/media/test/test_media_selection.html @@ -30,12 +30,12 @@ function maketest(attach_media, name, type, check_metadata) { manager.finished(token); }, false); } else { - e.addEventListener('error', function(event) { + e.addEventListener('error', function onerror(event) { is(errorRun, false, "error handler should run once only!"); errorRun = true; is(e.readyState, HTMLMediaElement.HAVE_NOTHING, 'test ' + token + ' readyState should be HAVE_NOTHING when load fails.'); - e.removeEventListener('error', arguments.callee, true); + e.removeEventListener('error', onerror, true); removeNodeAndSource(e); manager.finished(token); }, true); From 3b336bf2cf78e08530294450762310c106adaddb Mon Sep 17 00:00:00 2001 From: Matthew Gregan Date: Tue, 30 Sep 2014 14:14:41 +1300 Subject: [PATCH 48/82] Bug 1069660 - Wrap long lines at 80 cols. --- content/media/webm/WebMReader.cpp | 121 ++++++++++++++++++------------ content/media/webm/WebMReader.h | 16 ++-- 2 files changed, 85 insertions(+), 52 deletions(-) diff --git a/content/media/webm/WebMReader.cpp b/content/media/webm/WebMReader.cpp index 152e93604f01..44edd8aed3f5 100644 --- a/content/media/webm/WebMReader.cpp +++ b/content/media/webm/WebMReader.cpp @@ -24,7 +24,8 @@ using mozilla::NesteggPacketHolder; template <> -class nsAutoRefTraits : public nsPointerRefTraits +class nsAutoRefTraits : + public nsPointerRefTraits { public: static void Release(NesteggPacketHolder* aHolder) { delete aHolder; } @@ -61,8 +62,9 @@ static const double NS_PER_S = 1e9; // decoder from which the media resource is obtained. static int webm_read(void *aBuffer, size_t aLength, void *aUserData) { - NS_ASSERTION(aUserData, "aUserData must point to a valid AbstractMediaDecoder"); - AbstractMediaDecoder* decoder = reinterpret_cast(aUserData); + MOZ_ASSERT(aUserData); + AbstractMediaDecoder* decoder = + reinterpret_cast(aUserData); MediaResource* resource = decoder->GetResource(); NS_ASSERTION(resource, "Decoder has no media resource"); @@ -86,8 +88,9 @@ static int webm_read(void *aBuffer, size_t aLength, void *aUserData) static int webm_seek(int64_t aOffset, int aWhence, void *aUserData) { - NS_ASSERTION(aUserData, "aUserData must point to a valid AbstractMediaDecoder"); - AbstractMediaDecoder* decoder = reinterpret_cast(aUserData); + MOZ_ASSERT(aUserData); + AbstractMediaDecoder* decoder = + reinterpret_cast(aUserData); MediaResource* resource = decoder->GetResource(); NS_ASSERTION(resource, "Decoder has no media resource"); nsresult rv = resource->Seek(aWhence, aOffset); @@ -96,8 +99,9 @@ static int webm_seek(int64_t aOffset, int aWhence, void *aUserData) static int64_t webm_tell(void *aUserData) { - NS_ASSERTION(aUserData, "aUserData must point to a valid AbstractMediaDecoder"); - AbstractMediaDecoder* decoder = reinterpret_cast(aUserData); + MOZ_ASSERT(aUserData); + AbstractMediaDecoder* decoder = + reinterpret_cast(aUserData); MediaResource* resource = decoder->GetResource(); NS_ASSERTION(resource, "Decoder has no media resource"); return resource->Tell(); @@ -331,10 +335,12 @@ nsresult WebMReader::ReadMetadata(MediaInfo* aInfo, // Picture region, taking into account cropping, before scaling // to the display size. + unsigned int cropH = params.crop_right + params.crop_left; + unsigned int cropV = params.crop_bottom + params.crop_top; nsIntRect pictureRect(params.crop_left, params.crop_top, - params.width - (params.crop_right + params.crop_left), - params.height - (params.crop_bottom + params.crop_top)); + params.width - cropH, + params.height - cropV); // If the cropping data appears invalid then use the frame data if (pictureRect.width <= 0 || @@ -348,8 +354,8 @@ nsresult WebMReader::ReadMetadata(MediaInfo* aInfo, pictureRect.height = params.height; } - // Validate the container-reported frame and pictureRect sizes. This ensures - // that our video frame creation code doesn't overflow. + // Validate the container-reported frame and pictureRect sizes. This + // ensures that our video frame creation code doesn't overflow. nsIntSize displaySize(params.display_width, params.display_height); nsIntSize frameSize(params.width, params.height); if (!IsValidVideoRegion(frameSize, pictureRect, displaySize)) { @@ -415,7 +421,8 @@ nsresult WebMReader::ReadMetadata(MediaInfo* aInfo, Cleanup(); return NS_ERROR_FAILURE; } - ogg_packet opacket = InitOggPacket(data, length, header == 0, false, 0, mPacketCount++); + ogg_packet opacket = InitOggPacket(data, length, header == 0, false, + 0, mPacketCount++); r = vorbis_synthesis_headerin(&mVorbisInfo, &mVorbisComment, @@ -461,9 +468,10 @@ nsresult WebMReader::ReadMetadata(MediaInfo* aInfo, return NS_ERROR_FAILURE; } - if (static_cast(mCodecDelay) != FramesToUsecs(mOpusParser->mPreSkip, mOpusParser->mRate).value()) { + if (int64_t(mCodecDelay) != FramesToUsecs(mOpusParser->mPreSkip, + mOpusParser->mRate).value()) { LOG(PR_LOG_WARNING, - ("Invalid Opus header: CodecDelay and pre-skip do not match!\n")); + ("Invalid Opus header: CodecDelay and pre-skip do not match!")); Cleanup(); return NS_ERROR_FAILURE; } @@ -541,7 +549,8 @@ bool WebMReader::DecodeAudioPacket(nestegg_packet* aPacket, int64_t aOffset) // the vorbis decode doesn't use data from before the gap to help decode // from after the gap. CheckedInt64 tstamp_frames = UsecsToFrames(tstamp_usecs, mInfo.mAudio.mRate); - CheckedInt64 decoded_frames = UsecsToFrames(mAudioStartUsec, mInfo.mAudio.mRate); + CheckedInt64 decoded_frames = UsecsToFrames(mAudioStartUsec, + mInfo.mAudio.mRate); if (!tstamp_frames.isValid() || !decoded_frames.isValid()) { NS_WARNING("Int overflow converting WebM times to frames"); return false; @@ -553,10 +562,11 @@ bool WebMReader::DecodeAudioPacket(nestegg_packet* aPacket, int64_t aOffset) } if (tstamp_frames.value() > decoded_frames.value()) { #ifdef DEBUG - CheckedInt64 usecs = FramesToUsecs(tstamp_frames.value() - decoded_frames.value(), mInfo.mAudio.mRate); - LOG(PR_LOG_DEBUG, ("WebMReader detected gap of %lld, %lld frames, in audio stream\n", + int64_t gap_frames = tstamp_frames.value() - decoded_frames.value(); + CheckedInt64 usecs = FramesToUsecs(gap_frames, mInfo.mAudio.mRate); + LOG(PR_LOG_DEBUG, ("WebMReader detected gap of %lld, %lld frames, in audio", usecs.isValid() ? usecs.value() : -1, - tstamp_frames.value() - decoded_frames.value())); + gap_frames)); #endif mPacketCount++; mAudioStartUsec = tstamp_usecs; @@ -588,9 +598,11 @@ bool WebMReader::DecodeAudioPacket(nestegg_packet* aPacket, int64_t aOffset) } bool WebMReader::DecodeVorbis(const unsigned char* aData, size_t aLength, - int64_t aOffset, uint64_t aTstampUsecs, int32_t* aTotalFrames) + int64_t aOffset, uint64_t aTstampUsecs, + int32_t* aTotalFrames) { - ogg_packet opacket = InitOggPacket(aData, aLength, false, false, -1, mPacketCount++); + ogg_packet opacket = InitOggPacket(aData, aLength, false, false, -1, + mPacketCount++); if (vorbis_synthesis(&mVorbisBlock, &opacket) != 0) { return false; @@ -609,14 +621,17 @@ bool WebMReader::DecodeVorbis(const unsigned char* aData, size_t aLength, // time derived from the timecode of the first packet that produced // data. if (frames == 0 && mAudioFrames == 0) { - AudioQueue().Push(new AudioData(aOffset, aTstampUsecs, 0, 0, nullptr, mInfo.mAudio.mChannels, mInfo.mAudio.mRate)); + AudioQueue().Push(new AudioData(aOffset, aTstampUsecs, 0, 0, nullptr, + mInfo.mAudio.mChannels, + mInfo.mAudio.mRate)); } while (frames > 0) { - nsAutoArrayPtr buffer(new AudioDataValue[frames * mInfo.mAudio.mChannels]); - for (uint32_t j = 0; j < mInfo.mAudio.mChannels; ++j) { + uint32_t channels = mInfo.mAudio.mChannels; + nsAutoArrayPtr buffer(new AudioDataValue[frames*channels]); + for (uint32_t j = 0; j < channels; ++j) { VorbisPCMValue* channel = pcm[j]; for (uint32_t i = 0; i < uint32_t(frames); ++i) { - buffer[i*mInfo.mAudio.mChannels + j] = MOZ_CONVERT_VORBIS_SAMPLE(channel[i]); + buffer[i*channels + j] = MOZ_CONVERT_VORBIS_SAMPLE(channel[i]); } } @@ -625,7 +640,8 @@ bool WebMReader::DecodeVorbis(const unsigned char* aData, size_t aLength, NS_WARNING("Int overflow converting WebM audio duration"); return false; } - CheckedInt64 total_duration = FramesToUsecs(*aTotalFrames, mInfo.mAudio.mRate); + CheckedInt64 total_duration = FramesToUsecs(*aTotalFrames, + mInfo.mAudio.mRate); if (!total_duration.isValid()) { NS_WARNING("Int overflow converting WebM audio total_duration"); return false; @@ -658,7 +674,8 @@ bool WebMReader::DecodeVorbis(const unsigned char* aData, size_t aLength, #ifdef MOZ_OPUS bool WebMReader::DecodeOpus(const unsigned char* aData, size_t aLength, - int64_t aOffset, uint64_t aTstampUsecs, nestegg_packet* aPacket) + int64_t aOffset, uint64_t aTstampUsecs, + nestegg_packet* aPacket) { uint32_t channels = mOpusParser->mChannels; // No channel mapping for more than 8 channels. @@ -669,7 +686,7 @@ bool WebMReader::DecodeOpus(const unsigned char* aData, size_t aLength, if (mPaddingDiscarded) { // Discard padding should be used only on the final packet, so // decoding after a padding discard is invalid. - LOG(PR_LOG_DEBUG, ("Opus decoder error, discard padding on interstitial packet")); + LOG(PR_LOG_DEBUG, ("Opus error, discard padding on interstitial packet")); GetCallback()->OnDecodeError(); return false; } @@ -680,8 +697,8 @@ bool WebMReader::DecodeOpus(const unsigned char* aData, size_t aLength, return false; // Invalid packet header. } - int32_t samples = opus_packet_get_samples_per_frame(aData, - (opus_int32) mInfo.mAudio.mRate); + int32_t samples = + opus_packet_get_samples_per_frame(aData, opus_int32(mInfo.mAudio.mRate)); // A valid Opus packet must be between 2.5 and 120 ms long (48kHz). int32_t frames = frames_number*samples; @@ -709,8 +726,11 @@ bool WebMReader::DecodeOpus(const unsigned char* aData, size_t aLength, if (mSkip > 0) { int32_t skipFrames = std::min(mSkip, frames); int32_t keepFrames = frames - skipFrames; - LOG(PR_LOG_DEBUG, ("Opus decoder skipping %d of %d frames", skipFrames, frames)); - PodMove(buffer.get(), buffer.get() + skipFrames * channels, keepFrames * channels); + LOG(PR_LOG_DEBUG, ("Opus decoder skipping %d of %d frames", + skipFrames, frames)); + PodMove(buffer.get(), + buffer.get() + skipFrames * channels, + keepFrames * channels); startTime = startTime + FramesToUsecs(skipFrames, mInfo.mAudio.mRate); frames = keepFrames; mSkip -= skipFrames; @@ -720,23 +740,25 @@ bool WebMReader::DecodeOpus(const unsigned char* aData, size_t aLength, (void) nestegg_packet_discard_padding(aPacket, &discardPadding); if (discardPadding < 0) { // Negative discard padding is invalid. - LOG(PR_LOG_DEBUG, ("Opus decoder error, negative discard padding")); + LOG(PR_LOG_DEBUG, ("Opus error, negative discard padding")); GetCallback()->OnDecodeError(); return false; } if (discardPadding > 0) { - CheckedInt64 discardFrames = UsecsToFrames(discardPadding / NS_PER_USEC, mInfo.mAudio.mRate); + CheckedInt64 discardFrames = UsecsToFrames(discardPadding / NS_PER_USEC, + mInfo.mAudio.mRate); if (!discardFrames.isValid()) { NS_WARNING("Int overflow in DiscardPadding"); return false; } if (discardFrames.value() > frames) { // Discarding more than the entire packet is invalid. - LOG(PR_LOG_DEBUG, ("Opus decoder error, discard padding larger than packet")); + LOG(PR_LOG_DEBUG, ("Opus error, discard padding larger than packet")); GetCallback()->OnDecodeError(); return false; } - LOG(PR_LOG_DEBUG, ("Opus decoder discarding %d of %d frames", int32_t(discardFrames.value()), frames)); + LOG(PR_LOG_DEBUG, ("Opus decoder discarding %d of %d frames", + int32_t(discardFrames.value()), frames)); // Padding discard is only supposed to happen on the final packet. // Record the discard so we can return an error if another packet is // decoded. @@ -959,9 +981,9 @@ bool WebMReader::DecodeVideoFrame(bool &aKeyframeSkip, vpx_image_t *img; while ((img = vpx_codec_get_frame(&mVPX, &iter))) { - NS_ASSERTION(img->fmt == VPX_IMG_FMT_I420, "WebM image format is not I420"); + NS_ASSERTION(img->fmt == VPX_IMG_FMT_I420, "WebM image format not I420"); - // Chroma shifts are rounded down as per the decoding examples in the VP8 SDK + // Chroma shifts are rounded down as per the decoding examples in the SDK VideoData::YCbCrBuffer b; b.mPlanes[0].mData = img->planes[0]; b.mPlanes[0].mStride = img->stride[0]; @@ -984,9 +1006,10 @@ bool WebMReader::DecodeVideoFrame(bool &aKeyframeSkip, IntRect picture = ToIntRect(mPicture); if (img->d_w != static_cast(mInitialFrame.width) || img->d_h != static_cast(mInitialFrame.height)) { - // Frame size is different from what the container reports. This is legal - // in WebM, and we will preserve the ratio of the crop rectangle as it - // was reported relative to the picture size reported by the container. + // Frame size is different from what the container reports. This is + // legal in WebM, and we will preserve the ratio of the crop rectangle + // as it was reported relative to the picture size reported by the + // container. picture.x = (mPicture.x * img->d_w) / mInitialFrame.width; picture.y = (mPicture.y * img->d_h) / mInitialFrame.height; picture.width = (img->d_w * mPicture.width) / mInitialFrame.width; @@ -997,7 +1020,7 @@ bool WebMReader::DecodeVideoFrame(bool &aKeyframeSkip, mDecoder->GetImageContainer(), holder->mOffset, tstamp_usecs, - (next_tstamp / NS_PER_USEC) - tstamp_usecs, + (next_tstamp / NS_PER_USEC)-tstamp_usecs, b, si.is_kf, -1, @@ -1036,10 +1059,14 @@ nsresult WebMReader::Seek(int64_t aTarget, int64_t aStartTime, int64_t aEndTime, uint64_t target = aTarget * NS_PER_USEC; if (mSeekPreroll) { - target = std::max(static_cast(aStartTime * NS_PER_USEC), target - mSeekPreroll); + target = std::max(uint64_t(aStartTime * NS_PER_USEC), + target - mSeekPreroll); } int r = nestegg_track_seek(mContext, trackToSeek, target); if (r != 0) { + LOG(PR_LOG_DEBUG, ("Reader [%p]: track_seek for track %u failed, r=%d", + this, trackToSeek, r)); + // Try seeking directly based on cluster information in memory. int64_t offset = 0; bool rv = mBufferedState->GetOffsetForTime(target, &offset); @@ -1048,8 +1075,8 @@ nsresult WebMReader::Seek(int64_t aTarget, int64_t aStartTime, int64_t aEndTime, } r = nestegg_offset_seek(mContext, offset); - LOG(PR_LOG_DEBUG, ("Reader [%p]: track_seek for %u failed, offset_seek to %lld r=%d", - this, trackToSeek, offset, r)); + LOG(PR_LOG_DEBUG, ("Reader [%p]: attempted offset_seek to %lld r=%d", + this, offset, r)); if (r != 0) { return NS_ERROR_FAILURE; } @@ -1096,7 +1123,8 @@ nsresult WebMReader::GetBuffered(dom::TimeRanges* aBuffered, int64_t aStartTime) double endTime = (end - startOffset) / NS_PER_S; // If this range extends to the end of the file, the true end time // is the file's duration. - if (mContext && resource->IsDataCachedToEndOfResource(ranges[index].mStart)) { + if (mContext && + resource->IsDataCachedToEndOfResource(ranges[index].mStart)) { uint64_t duration = 0; if (nestegg_duration(mContext, &duration) == 0) { endTime = duration / NS_PER_S; @@ -1110,7 +1138,8 @@ nsresult WebMReader::GetBuffered(dom::TimeRanges* aBuffered, int64_t aStartTime) return NS_OK; } -void WebMReader::NotifyDataArrived(const char* aBuffer, uint32_t aLength, int64_t aOffset) +void WebMReader::NotifyDataArrived(const char* aBuffer, uint32_t aLength, + int64_t aOffset) { mBufferedState->NotifyDataArrived(aBuffer, aLength, aOffset); } diff --git a/content/media/webm/WebMReader.h b/content/media/webm/WebMReader.h index 55fd4fe767b5..f350c5b81e1f 100644 --- a/content/media/webm/WebMReader.h +++ b/content/media/webm/WebMReader.h @@ -134,9 +134,11 @@ public: virtual nsresult ReadMetadata(MediaInfo* aInfo, MetadataTags** aTags); - virtual nsresult Seek(int64_t aTime, int64_t aStartTime, int64_t aEndTime, int64_t aCurrentTime); + virtual nsresult Seek(int64_t aTime, int64_t aStartTime, int64_t aEndTime, + int64_t aCurrentTime); virtual nsresult GetBuffered(dom::TimeRanges* aBuffered, int64_t aStartTime); - virtual void NotifyDataArrived(const char* aBuffer, uint32_t aLength, int64_t aOffset); + virtual void NotifyDataArrived(const char* aBuffer, uint32_t aLength, + int64_t aOffset); virtual int64_t GetEvictionOffset(double aTime); virtual bool IsMediaSeekable() MOZ_OVERRIDE; @@ -170,10 +172,12 @@ protected: // aPacket. bool DecodeAudioPacket(nestegg_packet* aPacket, int64_t aOffset); bool DecodeVorbis(const unsigned char* aData, size_t aLength, - int64_t aOffset, uint64_t aTstampUsecs, int32_t* aTotalFrames); + int64_t aOffset, uint64_t aTstampUsecs, + int32_t* aTotalFrames); #ifdef MOZ_OPUS bool DecodeOpus(const unsigned char* aData, size_t aLength, - int64_t aOffset, uint64_t aTstampUsecs, nestegg_packet* aPacket); + int64_t aOffset, uint64_t aTstampUsecs, + nestegg_packet* aPacket); #endif // Release context and set to null. Called when an error occurs during @@ -199,8 +203,8 @@ private: // Opus decoder state nsAutoPtr mOpusParser; OpusMSDecoder *mOpusDecoder; - uint16_t mSkip; // Number of samples left to trim before playback. - uint64_t mSeekPreroll; // Number of nanoseconds that must be discarded after seeking. + uint16_t mSkip; // Samples left to trim before playback. + uint64_t mSeekPreroll; // Nanoseconds to discard after seeking. #endif // Queue of video and audio packets that have been read but not decoded. These From bbbe163c3bcea232de27fb8b0c9c37a073796e4a Mon Sep 17 00:00:00 2001 From: Felipe Gomes Date: Mon, 29 Sep 2014 22:22:21 -0300 Subject: [PATCH 49/82] Bug 1071203 - Log a message to the console when we override browser.tabs.remote.autostart. r=billm --- toolkit/xre/nsAppRunner.cpp | 51 +++++++++++++++++++++++++++++-------- 1 file changed, 40 insertions(+), 11 deletions(-) diff --git a/toolkit/xre/nsAppRunner.cpp b/toolkit/xre/nsAppRunner.cpp index 6b1d6a042285..6987328c2e2f 100644 --- a/toolkit/xre/nsAppRunner.cpp +++ b/toolkit/xre/nsAppRunner.cpp @@ -4556,6 +4556,17 @@ XRE_GetProcessType() return mozilla::startup::sChildProcessType; } +static void +LogE10sBlockedReason(const char *reason) { + nsAutoString msg(NS_LITERAL_STRING("==================\nE10s has been blocked from running because:\n")); + msg.Append(NS_ConvertASCIItoUTF16(reason)); + msg.AppendLiteral("\n==================\n"); + nsCOMPtr console(do_GetService("@mozilla.org/consoleservice;1")); + if (console) { + console->LogStringMessage(msg.get()); + } +} + bool mozilla::BrowserTabsRemoteAutostart() { @@ -4563,11 +4574,26 @@ mozilla::BrowserTabsRemoteAutostart() return false; #endif if (!gBrowserTabsRemoteAutostartInitialized) { - bool hasIME = KeyboardMayHaveIME(); - bool prefEnabled = Preferences::GetBool("browser.tabs.remote.autostart", false) || - (Preferences::GetBool("browser.tabs.remote.autostart.1", false) && !hasIME); + bool optInPref = Preferences::GetBool("browser.tabs.remote.autostart", false); + bool trialPref = Preferences::GetBool("browser.tabs.remote.autostart.1", false); + bool prefEnabled = optInPref || trialPref; + bool disabledForA11y = Preferences::GetBool("browser.tabs.remote.autostart.disabled-because-using-a11y", false); - gBrowserTabsRemoteAutostart = !gSafeMode && !disabledForA11y && prefEnabled; + // Only disable for IME for the automatic pref, not the opt-in one. + bool disabledForIME = trialPref && KeyboardMayHaveIME(); + + if (prefEnabled) { + if (gSafeMode) { + LogE10sBlockedReason("Firefox is in safe mode."); + } else if (disabledForA11y) { + LogE10sBlockedReason("An accessibility tool is active."); + } else if (disabledForIME) { + LogE10sBlockedReason("The keyboard being used has activated IME."); + } else { + gBrowserTabsRemoteAutostart = true; + } + } + #if defined(XP_WIN) || defined(XP_MACOSX) // If for any reason we suspect acceleration will be disabled, disabled @@ -4576,13 +4602,6 @@ mozilla::BrowserTabsRemoteAutostart() // Check prefs bool accelDisabled = Preferences::GetBool("layers.acceleration.disabled", false) && !Preferences::GetBool("layers.acceleration.force-enabled", false); - // Check env flags - if (!accelDisabled) { - const char *acceleratedEnv = PR_GetEnv("MOZ_ACCELERATED"); - if (acceleratedEnv && (*acceleratedEnv != '0')) { - accelDisabled = false; - } - } #if defined(XP_MACOSX) accelDisabled = !nsCocoaFeatures::AccelerateByDefault(); @@ -4615,8 +4634,18 @@ mozilla::BrowserTabsRemoteAutostart() } } } + + // Check env flags + if (accelDisabled) { + const char *acceleratedEnv = PR_GetEnv("MOZ_ACCELERATED"); + if (acceleratedEnv && (*acceleratedEnv != '0')) { + accelDisabled = false; + } + } + if (accelDisabled) { gBrowserTabsRemoteAutostart = false; + LogE10sBlockedReason("Hardware acceleration is disabled."); } } #endif From fcd996d937ad01099b4da396b8f8006e845ba76a Mon Sep 17 00:00:00 2001 From: Matt Woodrow Date: Fri, 26 Sep 2014 15:05:14 -0400 Subject: [PATCH 50/82] Bug 1070722 - Use the imagelib high quality downscaler on OSX instead of the quartz one. r=jrmuizel --- content/html/document/reftests/reftests.list | 2 +- gfx/2d/DrawTargetCG.cpp | 2 +- image/test/reftest/downscaling/reftest.list | 10 +--------- modules/libpref/init/all.js | 6 ------ 4 files changed, 3 insertions(+), 17 deletions(-) diff --git a/content/html/document/reftests/reftests.list b/content/html/document/reftests/reftests.list index 9684dfedc8d3..13dfc18e1a1c 100644 --- a/content/html/document/reftests/reftests.list +++ b/content/html/document/reftests/reftests.list @@ -9,4 +9,4 @@ # (Fuzzy necessary due to pixel-wise comparison of different JPEGs. # The vast majority of the fuzziness comes from Linux and WinXP.) fuzzy(1,149) == bug917595-iframe-1.html bug917595-1-ref.html -fuzzy(2,640) == bug917595-exif-rotated.jpg bug917595-pixel-rotated.jpg +fuzzy(3,640) == bug917595-exif-rotated.jpg bug917595-pixel-rotated.jpg diff --git a/gfx/2d/DrawTargetCG.cpp b/gfx/2d/DrawTargetCG.cpp index c3c6d30a862a..0c10a5a84412 100644 --- a/gfx/2d/DrawTargetCG.cpp +++ b/gfx/2d/DrawTargetCG.cpp @@ -136,7 +136,7 @@ InterpolationQualityFromFilter(Filter aFilter) case Filter::POINT: return kCGInterpolationNone; case Filter::GOOD: - return kCGInterpolationDefault; + return kCGInterpolationLow; } } diff --git a/image/test/reftest/downscaling/reftest.list b/image/test/reftest/downscaling/reftest.list index a01b4e50ad7a..9eef69cd54d8 100644 --- a/image/test/reftest/downscaling/reftest.list +++ b/image/test/reftest/downscaling/reftest.list @@ -44,56 +44,48 @@ fuzzy(20,999) != downscale-2b.html?203,52,left about:blank fuzzy(20,999) != downscale-2c.html?203,52,left about:blank fuzzy(20,999) != downscale-2d.html?203,52,left about:blank fuzzy(20,999) != downscale-2e.html?203,52,left about:blank -fuzzy(20,999) fails-if(!cocoaWidget) != downscale-2f.html?203,52,left about:blank fuzzy(20,999) != downscale-2a.html?205,53,left about:blank fuzzy(20,999) != downscale-2b.html?205,53,left about:blank fuzzy(20,999) != downscale-2c.html?205,53,left about:blank fuzzy(20,999) != downscale-2d.html?205,53,left about:blank fuzzy(20,999) != downscale-2e.html?205,53,left about:blank -fuzzy(20,999) fails-if(!cocoaWidget) != downscale-2f.html?205,53,left about:blank fuzzy(20,999) != downscale-2a.html?203,52,right about:blank fuzzy(20,999) != downscale-2b.html?203,52,right about:blank fuzzy(20,999) != downscale-2c.html?203,52,right about:blank fuzzy(20,999) != downscale-2d.html?203,52,right about:blank fuzzy(20,999) != downscale-2e.html?203,52,right about:blank -fuzzy(20,999) random-if(!cocoaWidget) != downscale-2f.html?203,52,right about:blank fuzzy(20,999) != downscale-2a.html?205,53,right about:blank fuzzy(20,999) != downscale-2b.html?205,53,right about:blank fuzzy(20,999) != downscale-2c.html?205,53,right about:blank fuzzy(20,999) != downscale-2d.html?205,53,right about:blank fuzzy(20,999) != downscale-2e.html?205,53,right about:blank -fuzzy(20,999) random-if(!cocoaWidget) != downscale-2f.html?205,53,right about:blank fuzzy(20,999) != downscale-2a.html?203,52,top about:blank fuzzy(20,999) != downscale-2b.html?203,52,top about:blank fuzzy(20,999) != downscale-2c.html?203,52,top about:blank fuzzy(20,999) != downscale-2d.html?203,52,top about:blank fuzzy(20,999) != downscale-2e.html?203,52,top about:blank -fuzzy(20,999) fails-if(!cocoaWidget) != downscale-2f.html?203,52,top about:blank fuzzy(20,999) != downscale-2a.html?205,53,top about:blank fuzzy(20,999) != downscale-2b.html?205,53,top about:blank fuzzy(20,999) != downscale-2c.html?205,53,top about:blank fuzzy(20,999) != downscale-2d.html?205,53,top about:blank fuzzy(20,999) != downscale-2e.html?205,53,top about:blank -fuzzy(20,999) fails-if(!cocoaWidget) != downscale-2f.html?205,53,top about:blank fuzzy(20,999) != downscale-2a.html?203,52,bottom about:blank fuzzy(20,999) != downscale-2b.html?203,52,bottom about:blank fuzzy(20,999) != downscale-2c.html?203,52,bottom about:blank fuzzy(20,999) != downscale-2d.html?203,52,bottom about:blank fuzzy(20,999) != downscale-2e.html?203,52,bottom about:blank -fuzzy(20,999) fails-if(!cocoaWidget) != downscale-2f.html?203,52,bottom about:blank fuzzy(20,999) != downscale-2a.html?205,53,bottom about:blank fuzzy(20,999) != downscale-2b.html?205,53,bottom about:blank fuzzy(20,999) != downscale-2c.html?205,53,bottom about:blank fuzzy(20,999) != downscale-2d.html?205,53,bottom about:blank -fuzzy(20,999) != downscale-2e.html?205,53,bottom about:blank -fuzzy(20,999) fails-if(!cocoaWidget) != downscale-2f.html?205,53,bottom about:blank +fuzzy(20,999) fails-if(cocoaWidget) != downscale-2e.html?205,53,bottom about:blank # RUN TESTS WITH HIGH QUALITY DOWNSCALING ENABLED: # ================================================ diff --git a/modules/libpref/init/all.js b/modules/libpref/init/all.js index e29b8aa53141..81e1a680b4bd 100644 --- a/modules/libpref/init/all.js +++ b/modules/libpref/init/all.js @@ -3672,13 +3672,7 @@ pref("image.cache.timeweight", 500); // The default Accept header sent for images loaded over HTTP(S) pref("image.http.accept", "image/png,image/*;q=0.8,*/*;q=0.5"); -// Whether we do high-quality image downscaling. OS X natively supports -// high-quality image scaling. -#ifdef XP_MACOSX -pref("image.high_quality_downscaling.enabled", false); -#else pref("image.high_quality_downscaling.enabled", true); -#endif // The minimum percent downscaling we'll use high-quality downscaling on, // interpreted as a floating-point number / 1000. From 492e380aa74c85fb5d9dd9e8895d23b6a6f4979b Mon Sep 17 00:00:00 2001 From: Matt Woodrow Date: Tue, 30 Sep 2014 15:26:40 +1300 Subject: [PATCH 51/82] Bug 1070722 - Fix reftest annotation for OSX 10.6 --- gfx/thebes/gfxPlatformMac.cpp | 7 ++++--- image/test/reftest/downscaling/reftest.list | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/gfx/thebes/gfxPlatformMac.cpp b/gfx/thebes/gfxPlatformMac.cpp index b1211adc5df8..fa6b58825a06 100644 --- a/gfx/thebes/gfxPlatformMac.cpp +++ b/gfx/thebes/gfxPlatformMac.cpp @@ -7,6 +7,7 @@ #include "gfxQuartzSurface.h" #include "gfxQuartzImageSurface.h" +#include "gfxImageSurface.h" #include "mozilla/gfx/2D.h" #include "gfxMacPlatformFontList.h" @@ -75,9 +76,9 @@ gfxPlatformMac::gfxPlatformMac() uint32_t canvasMask = BackendTypeBit(BackendType::CAIRO) | BackendTypeBit(BackendType::SKIA) | BackendTypeBit(BackendType::COREGRAPHICS); - uint32_t contentMask = BackendTypeBit(BackendType::COREGRAPHICS); + uint32_t contentMask = BackendTypeBit(BackendType::CAIRO); InitBackendPrefs(canvasMask, BackendType::COREGRAPHICS, - contentMask, BackendType::COREGRAPHICS); + contentMask, BackendType::CAIRO); } gfxPlatformMac::~gfxPlatformMac() @@ -101,7 +102,7 @@ gfxPlatformMac::CreateOffscreenSurface(const IntSize& size, gfxContentType contentType) { nsRefPtr newSurface = - new gfxQuartzSurface(ThebesIntSize(size), + new gfxImageSurface(ThebesIntSize(size), OptimalFormatForContent(contentType)); return newSurface.forget(); } diff --git a/image/test/reftest/downscaling/reftest.list b/image/test/reftest/downscaling/reftest.list index 9eef69cd54d8..ff8701ba6797 100644 --- a/image/test/reftest/downscaling/reftest.list +++ b/image/test/reftest/downscaling/reftest.list @@ -85,7 +85,7 @@ fuzzy(20,999) != downscale-2a.html?205,53,bottom about:blank fuzzy(20,999) != downscale-2b.html?205,53,bottom about:blank fuzzy(20,999) != downscale-2c.html?205,53,bottom about:blank fuzzy(20,999) != downscale-2d.html?205,53,bottom about:blank -fuzzy(20,999) fails-if(cocoaWidget) != downscale-2e.html?205,53,bottom about:blank +fuzzy(20,999) fails-if(OSX==10.8) != downscale-2e.html?205,53,bottom about:blank # RUN TESTS WITH HIGH QUALITY DOWNSCALING ENABLED: # ================================================ From 05fe9a0d0c773edaf8a42991b9cff6450c2b026b Mon Sep 17 00:00:00 2001 From: Matt Woodrow Date: Tue, 30 Sep 2014 16:12:28 +1300 Subject: [PATCH 52/82] Bug 1070722 - Backout accidental inclusions in a4ae53da4fe6 --- gfx/thebes/gfxPlatformMac.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/gfx/thebes/gfxPlatformMac.cpp b/gfx/thebes/gfxPlatformMac.cpp index fa6b58825a06..b1211adc5df8 100644 --- a/gfx/thebes/gfxPlatformMac.cpp +++ b/gfx/thebes/gfxPlatformMac.cpp @@ -7,7 +7,6 @@ #include "gfxQuartzSurface.h" #include "gfxQuartzImageSurface.h" -#include "gfxImageSurface.h" #include "mozilla/gfx/2D.h" #include "gfxMacPlatformFontList.h" @@ -76,9 +75,9 @@ gfxPlatformMac::gfxPlatformMac() uint32_t canvasMask = BackendTypeBit(BackendType::CAIRO) | BackendTypeBit(BackendType::SKIA) | BackendTypeBit(BackendType::COREGRAPHICS); - uint32_t contentMask = BackendTypeBit(BackendType::CAIRO); + uint32_t contentMask = BackendTypeBit(BackendType::COREGRAPHICS); InitBackendPrefs(canvasMask, BackendType::COREGRAPHICS, - contentMask, BackendType::CAIRO); + contentMask, BackendType::COREGRAPHICS); } gfxPlatformMac::~gfxPlatformMac() @@ -102,7 +101,7 @@ gfxPlatformMac::CreateOffscreenSurface(const IntSize& size, gfxContentType contentType) { nsRefPtr newSurface = - new gfxImageSurface(ThebesIntSize(size), + new gfxQuartzSurface(ThebesIntSize(size), OptimalFormatForContent(contentType)); return newSurface.forget(); } From f306f96f76ad93772a937ab04bc55416b8701586 Mon Sep 17 00:00:00 2001 From: Cervantes Yu Date: Tue, 30 Sep 2014 00:00:00 +0800 Subject: [PATCH 53/82] Bug 1051633: Make sure magic file descriptors in the content process will not be taken for other uses. r=khuey --HG-- extra : rebase_source : 5e2dedb855dd5e0e6637d6f42c80c69df8081971 --- b2g/app/B2GLoader.cpp | 57 ++++++++++++++++++++++---- ipc/glue/ProcessUtils_linux.cpp | 72 +++++++++++++++++++++++++++------ memory/replace/dmd/DMD.cpp | 7 +++- xpcom/base/nsDebugImpl.cpp | 10 ----- xpcom/build/nsXULAppAPI.h | 7 +++- 5 files changed, 120 insertions(+), 33 deletions(-) diff --git a/b2g/app/B2GLoader.cpp b/b2g/app/B2GLoader.cpp index 7e0aa9627840..8846ac794ef9 100644 --- a/b2g/app/B2GLoader.cpp +++ b/b2g/app/B2GLoader.cpp @@ -26,7 +26,6 @@ #define ASSERT(x) if (!(x)) { MOZ_CRASH(); } - // Functions being loaded by XPCOMGlue XRE_ProcLoaderServiceRunType XRE_ProcLoaderServiceRun; XRE_ProcLoaderClientInitType XRE_ProcLoaderClientInit; @@ -43,6 +42,10 @@ static const nsDynamicFunctionLoad kXULFuncs[] = { { nullptr, nullptr } }; +typedef mozilla::Vector FdArray; +static const int kReservedFileDescriptors = 5; +static const int kBeginReserveFileDescriptor = STDERR_FILENO + 1; + static int GetDirnameSlash(const char *aPath, char *aOutDir, int aMaxLen) { @@ -69,7 +72,7 @@ GetXPCOMPath(const char *aProgram, char *aOutPath, int aMaxLen) int len = GetDirnameSlash(progBuf, aOutPath, aMaxLen); NS_ENSURE_TRUE(!!len, false); - NS_ENSURE_TRUE((len + sizeof(XPCOM_DLL)) < aMaxLen, false); + NS_ENSURE_TRUE((len + sizeof(XPCOM_DLL)) < (unsigned)aMaxLen, false); char *afterSlash = aOutPath + len; strcpy(afterSlash, XPCOM_DLL); return true; @@ -181,7 +184,7 @@ LoadStaticData(int argc, const char *argv[]) * The parent is the b2g process and child for Nuwa. */ static int -RunProcesses(int argc, const char *argv[]) +RunProcesses(int argc, const char *argv[], FdArray& aReservedFds) { /* * The original main() of the b2g process. It is renamed to @@ -212,15 +215,50 @@ RunProcesses(int argc, const char *argv[]) * The b2g process would send a IPC message of loading Nuwa * as the replacement of forking and executing plugin-container. */ - return XRE_ProcLoaderServiceRun(getppid(), childSock, argc, argv); + return XRE_ProcLoaderServiceRun(getppid(), childSock, argc, argv, + aReservedFds); } // The b2g process int childPid = pid; - XRE_ProcLoaderClientInit(childPid, parentSock); + XRE_ProcLoaderClientInit(childPid, parentSock, aReservedFds); return b2g_main(argc, argv); } +/** + * Reserve the file descriptors that shouldn't be taken for other use for the + * child process. + */ +static void +ReserveFileDescriptors(FdArray& aReservedFds) +{ + for (int i = 0; i < kReservedFileDescriptors; i++) { + struct stat fileState; + int target = kBeginReserveFileDescriptor + i; + if (fstat(target, &fileState) == 0) { + MOZ_CRASH("ProcLoader error: a magic file descriptor is occupied."); + } + + int fd = open("/dev/null", O_RDWR); + if (fd == -1) { + MOZ_CRASH("ProcLoader error: failed to reserve a magic file descriptor."); + } + + aReservedFds.append(target); + + if (fd == target) { + // No need to call dup2(). We already occupy the desired file descriptor. + continue; + } + + if (dup2(fd, target)) { + MOZ_CRASH("ProcLoader error: failed to reserve a magic file descriptor."); + } + + close(fd); + } +} + /** * B2G Loader is responsible for loading the b2g process and the * Nuwa process. It forks into the parent process, for the b2g @@ -234,7 +272,12 @@ RunProcesses(int argc, const char *argv[]) int main(int argc, const char* argv[]) { - const char *program = argv[0]; + /** + * Reserve file descriptors before loading static data. + */ + FdArray reservedFds; + ReserveFileDescriptors(reservedFds); + /* * Before fork(), libxul and static data of Gecko are loaded for * sharing. @@ -244,5 +287,5 @@ main(int argc, const char* argv[]) return 255; } - return RunProcesses(argc, argv); + return RunProcesses(argc, argv, reservedFds); } diff --git a/ipc/glue/ProcessUtils_linux.cpp b/ipc/glue/ProcessUtils_linux.cpp index 42998991bd79..ca401a57e558 100644 --- a/ipc/glue/ProcessUtils_linux.cpp +++ b/ipc/glue/ProcessUtils_linux.cpp @@ -27,7 +27,10 @@ #include "base/file_descriptor_shuffle.h" #include "mozilla/BackgroundHangMonitor.h" #include "mozilla/DebugOnly.h" +#include "mozilla/UniquePtr.h" +#include "mozilla/unused.h" #include "base/process_util.h" +#include "base/eintr_wrapper.h" #include "prenv.h" @@ -37,11 +40,12 @@ int content_process_main(int argc, char *argv[]); -extern bool gDisableAndroidLog; +typedef mozilla::Vector FdArray; #endif /* MOZ_B2G_LOADER */ namespace mozilla { + namespace ipc { void SetThisProcessName(const char *aName) @@ -98,9 +102,18 @@ static pid_t sProcLoaderPid = 0; static int sProcLoaderChannelFd = -1; static PProcLoaderParent *sProcLoaderParent = nullptr; static MessageLoop *sProcLoaderLoop = nullptr; +static mozilla::UniquePtr sReservedFds; static void ProcLoaderClientDeinit(); +/** + * Some file descriptors, like the child IPC channel FD, must be opened at + * specific numbers. To ensure this, we pre-reserve kReservedFileDescriptors FDs + * starting from kBeginReserveFileDescriptor so that operations like + * __android_log_print() won't take these magic FDs. + */ +static const int kReservedFileDescriptors = 5; +static const int kBeginReserveFileDescriptor = STDERR_FILENO + 1; class ProcLoaderParent : public PProcLoaderParent { @@ -139,6 +152,14 @@ ProcLoaderParent::RecvLoadComplete(const int32_t &aPid, return true; } +static void +CloseFileDescriptors(FdArray& aFds) +{ + for (size_t i = 0; i < aFds.length(); i++) { + unused << HANDLE_EINTR(close(aFds[i])); + } +} + void ProcLoaderParent::OnChannelError() { @@ -292,6 +313,7 @@ class ProcLoaderRunnerBase { public: virtual int DoWork() = 0; + virtual ~ProcLoaderRunnerBase() {} }; @@ -305,7 +327,6 @@ ProcLoaderNoopRunner::DoWork() { return 0; } - /** * The runner to load Nuwa at the current process. */ @@ -336,9 +357,12 @@ ProcLoaderLoadRunner::ShuffleFds() { unsigned int i; + MOZ_ASSERT(mFdsRemap.Length() <= kReservedFileDescriptors); + InjectiveMultimap fd_shuffle1, fd_shuffle2; fd_shuffle1.reserve(mFdsRemap.Length()); fd_shuffle2.reserve(mFdsRemap.Length()); + for (i = 0; i < mFdsRemap.Length(); i++) { const FDRemap *map = &mFdsRemap[i]; int fd = map->fd().PlatformHandle(); @@ -346,12 +370,28 @@ ProcLoaderLoadRunner::ShuffleFds() fd_shuffle1.push_back(InjectionArc(fd, tofd, false)); fd_shuffle2.push_back(InjectionArc(fd, tofd, false)); + + // Erase from sReservedFds we will use. + for (int* toErase = sReservedFds->begin(); + toErase < sReservedFds->end(); + toErase++) { + if (tofd == *toErase) { + sReservedFds->erase(toErase); + break; + } + } } DebugOnly ok = ShuffleFileDescriptors(&fd_shuffle1); - MOZ_ASSERT(ok, "ShuffleFileDescriptors failed"); - CloseSuperfluousFds(fd_shuffle2); + // Close the FDs that are reserved but not used after + // ShuffleFileDescriptors(). + MOZ_ASSERT(sReservedFds); + CloseFileDescriptors(*sReservedFds); + sReservedFds = nullptr; + + // Note that we don'e call ::base::CloseSuperfluousFds() here, assuming that + // The file descriptor inherited from the parent are also necessary for us. } int @@ -482,8 +522,13 @@ public: */ static int ProcLoaderServiceRun(pid_t aPeerPid, int aFd, - int aArgc, const char *aArgv[]) + int aArgc, const char *aArgv[], + FdArray& aReservedFds) { + // Make a copy of aReservedFds. It will be used when we dup() the magic file + // descriptors when ProcLoaderChild::RecvLoad() runs. + sReservedFds = MakeUnique(mozilla::Move(aReservedFds)); + ScopedLogging logging; char **_argv; @@ -498,11 +543,8 @@ ProcLoaderServiceRun(pid_t aPeerPid, int aFd, gArgc = aArgc; { - gDisableAndroidLog = true; - nsresult rv = XRE_InitCommandLine(aArgc, _argv); if (NS_FAILED(rv)) { - gDisableAndroidLog = false; MOZ_CRASH(); } @@ -530,8 +572,6 @@ ProcLoaderServiceRun(pid_t aPeerPid, int aFd, BackgroundHangMonitor::Allow(); XRE_DeinitCommandLine(); - - gDisableAndroidLog = false; } MOZ_ASSERT(sProcLoaderDispatchedTask != nullptr); @@ -555,16 +595,22 @@ ProcLoaderServiceRun(pid_t aPeerPid, int aFd, #ifdef MOZ_B2G_LOADER void -XRE_ProcLoaderClientInit(pid_t aPeerPid, int aChannelFd) +XRE_ProcLoaderClientInit(pid_t aPeerPid, int aChannelFd, FdArray& aReservedFds) { + // We already performed fork(). It's safe to free the "danger zone" of file + // descriptors . + mozilla::ipc::CloseFileDescriptors(aReservedFds); + mozilla::ipc::ProcLoaderClientInit(aPeerPid, aChannelFd); } int XRE_ProcLoaderServiceRun(pid_t aPeerPid, int aFd, - int aArgc, const char *aArgv[]) + int aArgc, const char *aArgv[], + FdArray& aReservedFds) { return mozilla::ipc::ProcLoaderServiceRun(aPeerPid, aFd, - aArgc, aArgv); + aArgc, aArgv, + aReservedFds); } #endif /* MOZ_B2G_LOADER */ diff --git a/memory/replace/dmd/DMD.cpp b/memory/replace/dmd/DMD.cpp index 909e5baaa124..01c4378c7814 100644 --- a/memory/replace/dmd/DMD.cpp +++ b/memory/replace/dmd/DMD.cpp @@ -201,7 +201,12 @@ StatusMsg(const char* aFmt, ...) va_list ap; va_start(ap, aFmt); #ifdef ANDROID - __android_log_vprint(ANDROID_LOG_INFO, "DMD", aFmt, ap); +#ifdef MOZ_B2G_LOADER + // Don't call __android_log_vprint() during initialization, or the magic file + // descriptors will be occupied by android logcat. + if (gIsDMDRunning) +#endif + __android_log_vprint(ANDROID_LOG_INFO, "DMD", aFmt, ap); #else // The +64 is easily enough for the "DMD[] " prefix and the NUL. char* fmt = (char*) InfallibleAllocPolicy::malloc_(strlen(aFmt) + 64); diff --git a/xpcom/base/nsDebugImpl.cpp b/xpcom/base/nsDebugImpl.cpp index dbcf58f67d93..f61277a8d7eb 100644 --- a/xpcom/base/nsDebugImpl.cpp +++ b/xpcom/base/nsDebugImpl.cpp @@ -102,13 +102,6 @@ Break(const char* aMsg); #include #endif -#ifdef MOZ_B2G_LOADER -/* Avoid calling Android logger/logd temporarily while running - * B2GLoader to start the child process. - */ -bool gDisableAndroidLog = false; -#endif - using namespace mozilla; static const char* sMultiprocessDescription = nullptr; @@ -391,9 +384,6 @@ NS_DebugBreak(uint32_t aSeverity, const char* aStr, const char* aExpr, #endif #ifdef ANDROID -#ifdef MOZ_B2G_LOADER - if (!gDisableAndroidLog) -#endif __android_log_print(ANDROID_LOG_INFO, "Gecko", "%s", buf.buffer); #endif diff --git a/xpcom/build/nsXULAppAPI.h b/xpcom/build/nsXULAppAPI.h index eb60f2a8d768..a08e99418e3c 100644 --- a/xpcom/build/nsXULAppAPI.h +++ b/xpcom/build/nsXULAppAPI.h @@ -16,6 +16,7 @@ #include "mozilla/ArrayUtils.h" #include "mozilla/Assertions.h" +#include "mozilla/Vector.h" /** * A directory service key which provides the platform-correct "application @@ -470,9 +471,11 @@ XRE_API(WindowsEnvironmentType, #ifdef MOZ_B2G_LOADER XRE_API(int, - XRE_ProcLoaderServiceRun, (pid_t, int, int argc, const char* argv[])); + XRE_ProcLoaderServiceRun, (pid_t, int, int argc, const char* argv[], + mozilla::Vector& aReservedFds)); XRE_API(void, - XRE_ProcLoaderClientInit, (pid_t, int)); + XRE_ProcLoaderClientInit, (pid_t, int, + mozilla::Vector& aReservedFds)); XRE_API(void, XRE_ProcLoaderPreload, (const char* aProgramDir, const nsXREAppData* aAppData)); From 0b6d02d00acec04d3063ea1f404e147ae018bd3c Mon Sep 17 00:00:00 2001 From: Matthew Gregan Date: Tue, 30 Sep 2014 16:20:56 +1300 Subject: [PATCH 54/82] Bug 1074004 - Bustage fix for nestegg_set_halloc_func. --HG-- extra : rebase_source : 7e24e953dc287e80112e0a0d4263b6ac5b1fbd8d --- media/libnestegg/README_MOZILLA | 2 +- media/libnestegg/src/halloc.c | 13 ++++++------- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/media/libnestegg/README_MOZILLA b/media/libnestegg/README_MOZILLA index 40f66f037347..11ce46ea96ec 100644 --- a/media/libnestegg/README_MOZILLA +++ b/media/libnestegg/README_MOZILLA @@ -5,4 +5,4 @@ Makefile.in build files for the Mozilla build system. The nestegg git repository is: git://github.com/kinetiknz/nestegg.git -The git commit ID used was 2f596487949b192fb15866c6f8ecc6b04df92cb8. +The git commit ID used was ec23378e70030bacf3b4905c444c38ef4a37bceb. diff --git a/media/libnestegg/src/halloc.c b/media/libnestegg/src/halloc.c index 441107d83bc3..3d0e75cb588e 100644 --- a/media/libnestegg/src/halloc.c +++ b/media/libnestegg/src/halloc.c @@ -40,10 +40,8 @@ typedef struct hblock * */ realloc_t halloc_allocator = NULL; -realloc_t halloc_wrapped_allocator = NULL; #define allocator halloc_allocator -#define wrapped_allocator halloc_wrapped_allocator /* * static methods @@ -64,7 +62,10 @@ void * halloc(void * ptr, size_t len) /* set up default allocator */ if (! allocator) { - halloc_set_allocator(realloc); + if (halloc_set_allocator(realloc) == 0) + { + halloc_set_allocator(_realloc); + } assert(allocator); } @@ -189,7 +190,6 @@ int halloc_set_allocator(realloc_t realloc_func) * * Thanks to Stan Tobias for pointing this tricky part out. */ - allocator = realloc_func; if (! (p = malloc(1))) /* hmm */ return -1; @@ -197,11 +197,10 @@ int halloc_set_allocator(realloc_t realloc_func) if ((p = realloc_func(p, 0))) { /* realloc_func cannot be used as free() */ - allocator = _realloc; - wrapped_allocator = realloc_func; free(p); return 0; } + allocator = realloc_func; return 1; } @@ -211,7 +210,7 @@ static void * _realloc(void * ptr, size_t n) * free'ing realloc() */ if (n) - return wrapped_allocator(ptr, n); + return realloc(ptr, n); free(ptr); return NULL; } From 5e5056d05ae52112197c6ea3a5f61b65d039f83f Mon Sep 17 00:00:00 2001 From: Kai-Zhen Li Date: Fri, 26 Sep 2014 17:41:19 +0800 Subject: [PATCH 55/82] Bug 1068468 - Truncate ssid if its length of encoded to utf8 is longer than 32. r=chulee --HG-- extra : amend_source : 7f41d7bf31c3152e75934731d36aead919371baa --- dom/wifi/WifiWorker.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/dom/wifi/WifiWorker.js b/dom/wifi/WifiWorker.js index 4cb873564744..83c32d606402 100644 --- a/dom/wifi/WifiWorker.js +++ b/dom/wifi/WifiWorker.js @@ -3077,6 +3077,12 @@ WifiWorker.prototype = { debug("Invalid SSID value."); return null; } + // Truncate ssid if its length of encoded to utf8 is longer than 32. + while (unescape(encodeURIComponent(ssid)).length > 32) + { + ssid = ssid.substring(0, ssid.length-1); + } + if (securityType != WIFI_SECURITY_TYPE_NONE && securityType != WIFI_SECURITY_TYPE_WPA_PSK && securityType != WIFI_SECURITY_TYPE_WPA2_PSK) { From 7ba932d1963faf9d1e900a6f79ca8cb8bf0bbbef Mon Sep 17 00:00:00 2001 From: JW Wang Date: Wed, 24 Sep 2014 19:25:00 +1200 Subject: [PATCH 56/82] Bug 883731 - Remove ResourceLoaded notifications since they don't make much sense with a media cache. r=cpearce Based on roc's patch in https://bugzilla.mozilla.org/page.cgi?id=splinter.html&bug=883731&attachment=767505. --- .../html/content/public/HTMLMediaElement.h | 30 ++++----- content/html/content/src/HTMLMediaElement.cpp | 64 ++++++------------- content/media/MediaCache.cpp | 7 +- content/media/MediaDecoder.cpp | 57 ++--------------- content/media/MediaDecoder.h | 8 --- content/media/MediaDecoderOwner.h | 12 +--- content/media/MediaResource.cpp | 19 +++++- 7 files changed, 58 insertions(+), 139 deletions(-) diff --git a/content/html/content/public/HTMLMediaElement.h b/content/html/content/public/HTMLMediaElement.h index 923dde489e93..0fe536c1f436 100755 --- a/content/html/content/public/HTMLMediaElement.h +++ b/content/html/content/public/HTMLMediaElement.h @@ -162,15 +162,9 @@ public: virtual void MetadataLoaded(const MediaInfo* aInfo, const MetadataTags* aTags) MOZ_FINAL MOZ_OVERRIDE; - // Called by the video decoder object, on the main thread, - // when it has read the first frame of the video - // aResourceFullyLoaded should be true if the resource has been - // fully loaded and the caller will call ResourceLoaded next. - virtual void FirstFrameLoaded(bool aResourceFullyLoaded) MOZ_FINAL MOZ_OVERRIDE; - - // Called by the video decoder object, on the main thread, - // when the resource has completed downloading. - virtual void ResourceLoaded() MOZ_FINAL MOZ_OVERRIDE; + // Called by the decoder object, on the main thread, + // when it has read the first frame of the video or audio. + virtual void FirstFrameLoaded() MOZ_FINAL MOZ_OVERRIDE; // Called by the video decoder object, on the main thread, // when the resource has a network error during loading. @@ -238,10 +232,6 @@ public: // HAVE_FUTURE_DATA or HAVE_ENOUGH_DATA. virtual void UpdateReadyStateForData(MediaDecoderOwner::NextFrameStatus aNextFrame) MOZ_FINAL MOZ_OVERRIDE; - // Use this method to change the mReadyState member, so required - // events can be fired. - void ChangeReadyState(nsMediaReadyState aState); - // Return true if we can activate autoplay assuming enough data has arrived. bool CanActivateAutoplay(); @@ -540,7 +530,7 @@ public: already_AddRefed SetMediaKeys(MediaKeys* mediaKeys, ErrorResult& aRv); - + MediaWaitingFor WaitingFor() const; mozilla::dom::EventHandlerNonNull* GetOnencrypted(); @@ -648,6 +638,11 @@ protected: nsCOMPtr mTimer; }; + /** Use this method to change the mReadyState member, so required + * events can be fired. + */ + void ChangeReadyState(nsMediaReadyState aState); + /** * These two methods are called by the WakeLockBoolWrapper when the wakelock * has to be created or released. @@ -933,7 +928,7 @@ protected: // desired, and we'll seek to the sync point (keyframe and/or start of the // next block of audio samples) preceeding seek target. void Seek(double aTime, SeekTarget::Type aSeekType, ErrorResult& aRv); - + // Update the audio channel playing state void UpdateAudioChannelPlayingState(); @@ -1104,9 +1099,8 @@ protected: // Set to false when completed, or not yet started. bool mBegun; - // True when the decoder has loaded enough data to display the - // first frame of the content. - bool mLoadedFirstFrame; + // True if loadeddata has been fired. + bool mLoadedDataFired; // Indicates whether current playback is a result of user action // (ie. calling of the Play method), or automatic playback due to diff --git a/content/html/content/src/HTMLMediaElement.cpp b/content/html/content/src/HTMLMediaElement.cpp index ed72a4045e0b..734c7898bb92 100755 --- a/content/html/content/src/HTMLMediaElement.cpp +++ b/content/html/content/src/HTMLMediaElement.cpp @@ -661,7 +661,8 @@ void HTMLMediaElement::AbortExistingLoads() } mError = nullptr; - mLoadedFirstFrame = false; + mBegun = false; + mLoadedDataFired = false; mAutoplaying = true; mIsLoadingFromSourceChildren = false; mSuspendedAfterFirstFrame = false; @@ -2027,7 +2028,7 @@ HTMLMediaElement::HTMLMediaElement(already_AddRefed& aNo mPlayed(new TimeRanges), mCurrentPlayRangeStart(-1.0), mBegun(false), - mLoadedFirstFrame(false), + mLoadedDataFired(false), mAutoplaying(true), mAutoplayEnabled(true), mPaused(true), @@ -2776,7 +2777,7 @@ public: mHaveCurrentData = true; if (mElement) { nsRefPtr deathGrip = mElement; - mElement->FirstFrameLoaded(false); + mElement->FirstFrameLoaded(); } UpdateReadyStateForData(); DoNotifyOutput(); @@ -2870,8 +2871,7 @@ void HTMLMediaElement::SetupSrcMediaStreamPlayback(DOMMediaStream* aStream) DispatchAsyncEvent(NS_LITERAL_STRING("suspend")); mNetworkState = nsIDOMHTMLMediaElement::NETWORK_IDLE; AddRemoveSelfReference(); - // FirstFrameLoaded(false) will be called when the stream has current data, - // to complete the setup by entering the HAVE_CURRENT_DATA state. + // FirstFrameLoaded() will be called when the stream has current data. } void HTMLMediaElement::EndSrcMediaStreamPlayback() @@ -2921,6 +2921,7 @@ void HTMLMediaElement::MetadataLoaded(const MediaInfo* aInfo, { mHasAudio = aInfo->HasAudio(); mTags = aTags; + mLoadedDataFired = false; ChangeReadyState(nsIDOMHTMLMediaElement::HAVE_METADATA); DispatchAsyncEvent(NS_LITERAL_STRING("durationchange")); DispatchAsyncEvent(NS_LITERAL_STRING("loadedmetadata")); @@ -2940,25 +2941,19 @@ void HTMLMediaElement::MetadataLoaded(const MediaInfo* aInfo, } } -void HTMLMediaElement::FirstFrameLoaded(bool aResourceFullyLoaded) +void HTMLMediaElement::FirstFrameLoaded() { - ChangeReadyState(aResourceFullyLoaded ? - nsIDOMHTMLMediaElement::HAVE_ENOUGH_DATA : - nsIDOMHTMLMediaElement::HAVE_CURRENT_DATA); - ChangeDelayLoadStatus(false); - NS_ASSERTION(!mSuspendedAfterFirstFrame, "Should not have already suspended"); + ChangeDelayLoadStatus(false); + if (mDecoder && mAllowSuspendAfterFirstFrame && mPaused && - !aResourceFullyLoaded && !HasAttr(kNameSpaceID_None, nsGkAtoms::autoplay) && mPreloadAction == HTMLMediaElement::PRELOAD_METADATA) { mSuspendedAfterFirstFrame = true; mDecoder->Suspend(); - } else if (mLoadedFirstFrame && - mDownloadSuspendedByCache && - mDecoder && - !mDecoder->IsEnded()) { + } else if (mDownloadSuspendedByCache && + mDecoder && !mDecoder->IsEnded()) { // We've already loaded the first frame, and the decoder has signalled // that the download has been suspended by the media cache. So move // readyState into HAVE_ENOUGH_DATA, in case there's script waiting @@ -2972,26 +2967,6 @@ void HTMLMediaElement::FirstFrameLoaded(bool aResourceFullyLoaded) } } -void HTMLMediaElement::ResourceLoaded() -{ - NS_ASSERTION(!mSrcStream, "Don't call this for streams"); - - mBegun = false; - mNetworkState = nsIDOMHTMLMediaElement::NETWORK_IDLE; - AddRemoveSelfReference(); - if (mReadyState >= nsIDOMHTMLMediaElement::HAVE_METADATA) { - // MediaStream sources are put into HAVE_CURRENT_DATA state here on setup. If the - // stream is not blocked, we will receive a notification that will put it - // into HAVE_ENOUGH_DATA state. - ChangeReadyState(mSrcStream ? nsIDOMHTMLMediaElement::HAVE_CURRENT_DATA - : nsIDOMHTMLMediaElement::HAVE_ENOUGH_DATA); - } - // Ensure a progress event is dispatched at the end of download. - DispatchAsyncEvent(NS_LITERAL_STRING("progress")); - // The download has stopped. - DispatchAsyncEvent(NS_LITERAL_STRING("suspend")); -} - void HTMLMediaElement::NetworkError() { Error(nsIDOMMediaError::MEDIA_ERR_NETWORK); @@ -3146,8 +3121,7 @@ void HTMLMediaElement::UpdateReadyStateForData(MediaDecoderOwner::NextFrameStatu { if (mReadyState < nsIDOMHTMLMediaElement::HAVE_METADATA) { // aNextFrame might have a next frame because the decoder can advance - // on its own thread before ResourceLoaded or MetadataLoaded gets - // a chance to run. + // on its own thread before MetadataLoaded gets a chance to run. // The arrival of more data can't change us out of this readyState. return; } @@ -3160,17 +3134,16 @@ void HTMLMediaElement::UpdateReadyStateForData(MediaDecoderOwner::NextFrameStatu return; } - if (mReadyState > nsIDOMHTMLMediaElement::HAVE_METADATA && - mDownloadSuspendedByCache && - mDecoder && - !mDecoder->IsEnded()) { - // The decoder has signalled that the download has been suspended by the + if (mDownloadSuspendedByCache && mDecoder && !mDecoder->IsEnded()) { + // The decoder has signaled that the download has been suspended by the // media cache. So move readyState into HAVE_ENOUGH_DATA, in case there's // script waiting for a "canplaythrough" event; without this forced // transition, we will never fire the "canplaythrough" event if the // media cache is too small, and scripts are bound to fail. Don't force // this transition if the decoder is in ended state; the readyState // should remain at HAVE_CURRENT_DATA in this case. + // Note that this state transition includes the case where we finished + // downloaded the whole data stream. ChangeReadyState(nsIDOMHTMLMediaElement::HAVE_ENOUGH_DATA); return; } @@ -3241,10 +3214,9 @@ void HTMLMediaElement::ChangeReadyState(nsMediaReadyState aState) if (oldState < nsIDOMHTMLMediaElement::HAVE_CURRENT_DATA && mReadyState >= nsIDOMHTMLMediaElement::HAVE_CURRENT_DATA && - !mLoadedFirstFrame) - { + !mLoadedDataFired) { DispatchAsyncEvent(NS_LITERAL_STRING("loadeddata")); - mLoadedFirstFrame = true; + mLoadedDataFired = true; } if (mReadyState == nsIDOMHTMLMediaElement::HAVE_CURRENT_DATA) { diff --git a/content/media/MediaCache.cpp b/content/media/MediaCache.cpp index 4d23627da28a..8b47b0e21d57 100644 --- a/content/media/MediaCache.cpp +++ b/content/media/MediaCache.cpp @@ -1857,7 +1857,11 @@ MediaCacheStream::NotifyDataEnded(nsresult aStatus) mResourceID = gMediaCache->AllocateResourceID(); } + // It is prudent to update channel/cache status before calling + // CacheClientNotifyDataEnded() which will read |mChannelEnded|. FlushPartialBlockInternal(true); + mChannelEnded = true; + gMediaCache->QueueUpdate(); MediaCache::ResourceStreamIterator iter(mResourceID); while (MediaCacheStream* stream = iter.Next()) { @@ -1871,9 +1875,6 @@ MediaCacheStream::NotifyDataEnded(nsresult aStatus) stream->mClient->CacheClientNotifyDataEnded(aStatus); } } - - mChannelEnded = true; - gMediaCache->QueueUpdate(); } void diff --git a/content/media/MediaDecoder.cpp b/content/media/MediaDecoder.cpp index c903baaf2e75..27f4fad57bfb 100644 --- a/content/media/MediaDecoder.cpp +++ b/content/media/MediaDecoder.cpp @@ -440,7 +440,6 @@ MediaDecoder::MediaDecoder() : mIsExitingDormant(false), mPlayState(PLAY_STATE_PAUSED), mNextState(PLAY_STATE_PAUSED), - mCalledResourceLoaded(false), mIgnoreProgressData(false), mInfiniteStream(false), mOwner(nullptr), @@ -724,20 +723,8 @@ void MediaDecoder::MetadataLoaded(MediaInfo* aInfo, MetadataTags* aTags) mOwner->MetadataLoaded(aInfo, aTags); } - if (!mCalledResourceLoaded) { - StartProgress(); - } else if (mOwner) { - // Resource was loaded during metadata loading, when progress - // events are being ignored. Fire the final progress event. - mOwner->DispatchAsyncEvent(NS_LITERAL_STRING("progress")); - } - - // Only inform the element of FirstFrameLoaded if not doing a load() in order - // to fulfill a seek, otherwise we'll get multiple loadedfirstframe events. - bool notifyResourceIsLoaded = !mCalledResourceLoaded && - IsDataCachedToEndOfResource(); if (mOwner) { - mOwner->FirstFrameLoaded(notifyResourceIsLoaded); + mOwner->FirstFrameLoaded(); } // This can run cache callbacks. @@ -756,45 +743,11 @@ void MediaDecoder::MetadataLoaded(MediaInfo* aInfo, MetadataTags* aTags) } } - if (notifyResourceIsLoaded) { - ResourceLoaded(); - } - // Run NotifySuspendedStatusChanged now to give us a chance to notice // that autoplay should run. NotifySuspendedStatusChanged(); } -void MediaDecoder::ResourceLoaded() -{ - MOZ_ASSERT(NS_IsMainThread()); - - // Don't handle ResourceLoaded if we are shutting down, or if - // we need to ignore progress data due to seeking (in the case - // that the seek results in reaching end of file, we get a bogus call - // to ResourceLoaded). - if (mShuttingDown) - return; - - { - // If we are seeking or loading then the resource loaded notification we get - // should be ignored, since it represents the end of the seek request. - ReentrantMonitorAutoEnter mon(GetReentrantMonitor()); - if (mIgnoreProgressData || mCalledResourceLoaded || mPlayState == PLAY_STATE_LOADING) - return; - - Progress(false); - - mCalledResourceLoaded = true; - StopProgress(); - } - - // Ensure the final progress event gets fired - if (mOwner) { - mOwner->ResourceLoaded(); - } -} - void MediaDecoder::ResetConnectionState() { MOZ_ASSERT(NS_IsMainThread()); @@ -1029,12 +982,12 @@ void MediaDecoder::NotifyDownloadEnded(nsresult aStatus) } if (NS_SUCCEEDED(aStatus)) { - ResourceLoaded(); - } - else if (aStatus != NS_BASE_STREAM_CLOSED) { + UpdateReadyStateForData(); + // A final progress event will be fired by the MediaResource calling + // DownloadSuspended on the element. + } else if (aStatus != NS_BASE_STREAM_CLOSED) { NetworkError(); } - UpdateReadyStateForData(); } void MediaDecoder::NotifyPrincipalChanged() diff --git a/content/media/MediaDecoder.h b/content/media/MediaDecoder.h index c5ccf734e587..497e685df80c 100644 --- a/content/media/MediaDecoder.h +++ b/content/media/MediaDecoder.h @@ -308,9 +308,6 @@ public: // Called in |Load| to open mResource. nsresult OpenResource(nsIStreamListener** aStreamListener); - // Called when the video file has completed downloading. - virtual void ResourceLoaded(); - // Called if the media file encounters a network error. virtual void NetworkError(); @@ -1159,11 +1156,6 @@ protected: // been requested. When a seek is started this is reset to invalid. SeekTarget mRequestedSeekTarget; - // True when we have fully loaded the resource and reported that - // to the element (i.e. reached NETWORK_LOADED state). - // Accessed on the main thread only. - bool mCalledResourceLoaded; - // True when seeking or otherwise moving the play position around in // such a manner that progress event data is inaccurate. This is set // during seek and duration operations to prevent the progress indicator diff --git a/content/media/MediaDecoderOwner.h b/content/media/MediaDecoderOwner.h index c31ffcdf4f57..7cd2b1e0da8d 100644 --- a/content/media/MediaDecoderOwner.h +++ b/content/media/MediaDecoderOwner.h @@ -52,15 +52,9 @@ public: virtual void MetadataLoaded(const MediaInfo* aInfo, const MetadataTags* aTags) = 0; - // Called by the video decoder object, on the main thread, - // when it has read the first frame of the video - // aResourceFullyLoaded should be true if the resource has been - // fully loaded and the caller will call ResourceLoaded next. - virtual void FirstFrameLoaded(bool aResourceFullyLoaded) = 0; - - // Called by the video decoder object, on the main thread, - // when the resource has completed downloading. - virtual void ResourceLoaded() = 0; + // Called by the decoder object, on the main thread, + // when it has read the first frame of the video or audio. + virtual void FirstFrameLoaded() = 0; // Called by the video decoder object, on the main thread, // when the resource has a network error during loading. diff --git a/content/media/MediaResource.cpp b/content/media/MediaResource.cpp index 7f391861d8c3..89befcf8c214 100644 --- a/content/media/MediaResource.cpp +++ b/content/media/MediaResource.cpp @@ -984,11 +984,24 @@ public: mDecoder(aDecoder), mStatus(aStatus) {} NS_IMETHOD Run() { mDecoder->NotifyDownloadEnded(mStatus); + if (NS_SUCCEEDED(mStatus)) { + MediaDecoderOwner* owner = mDecoder->GetMediaOwner(); + if (owner) { + dom::HTMLMediaElement* element = owner->GetMediaElement(); + if (element) { + element->DownloadSuspended(); + } + } + // NotifySuspendedStatusChanged will tell the element that download + // has been suspended "by the cache", which is true since we never download + // anything. The element can then transition to HAVE_ENOUGH_DATA. + mDecoder->NotifySuspendedStatusChanged(); + } return NS_OK; } private: nsRefPtr mDecoder; - nsresult mStatus; + nsresult mStatus; }; void @@ -1248,8 +1261,8 @@ public: return std::max(aOffset, mSize); } virtual bool IsDataCachedToEndOfResource(int64_t aOffset) { return true; } - virtual bool IsSuspendedByCache() { return false; } - virtual bool IsSuspended() { return false; } + virtual bool IsSuspendedByCache() { return true; } + virtual bool IsSuspended() { return true; } virtual bool IsTransportSeekable() MOZ_OVERRIDE { return true; } nsresult GetCachedRanges(nsTArray& aRanges); From 3714d8b91eb89c72d68b4d0516fe47f233ccb57c Mon Sep 17 00:00:00 2001 From: JW Wang Date: Wed, 24 Sep 2014 19:25:00 +1200 Subject: [PATCH 57/82] Bug 883731 - Remove unnecessary update of readyState and introduce NEXT_FRAME_UNAVAILABLE_SEEKING so that we return HAVE_METADATA while seeking. r=cpearce --- content/html/content/src/HTMLMediaElement.cpp | 20 ++++++------------- content/media/MediaDecoder.cpp | 10 ++++------ content/media/MediaDecoderOwner.h | 2 ++ content/media/MediaDecoderStateMachine.cpp | 8 +++++++- content/media/omx/AudioOffloadPlayer.cpp | 2 +- 5 files changed, 20 insertions(+), 22 deletions(-) diff --git a/content/html/content/src/HTMLMediaElement.cpp b/content/html/content/src/HTMLMediaElement.cpp index 734c7898bb92..4cc8be686921 100755 --- a/content/html/content/src/HTMLMediaElement.cpp +++ b/content/html/content/src/HTMLMediaElement.cpp @@ -2952,18 +2952,6 @@ void HTMLMediaElement::FirstFrameLoaded() mPreloadAction == HTMLMediaElement::PRELOAD_METADATA) { mSuspendedAfterFirstFrame = true; mDecoder->Suspend(); - } else if (mDownloadSuspendedByCache && - mDecoder && !mDecoder->IsEnded()) { - // We've already loaded the first frame, and the decoder has signalled - // that the download has been suspended by the media cache. So move - // readyState into HAVE_ENOUGH_DATA, in case there's script waiting - // for a "canplaythrough" event; without this forced transition, we will - // never fire the "canplaythrough" event if the media cache is so small - // that the download was suspended before the first frame was loaded. - // Don't force this transition if the decoder is in ended state; the - // readyState should remain at HAVE_CURRENT_DATA in this case. - ChangeReadyState(nsIDOMHTMLMediaElement::HAVE_ENOUGH_DATA); - return; } } @@ -3058,7 +3046,6 @@ void HTMLMediaElement::SeekStarted() if(mPlayingThroughTheAudioChannel) { mPlayingThroughTheAudioChannelBeforeSeek = true; } - ChangeReadyState(nsIDOMHTMLMediaElement::HAVE_METADATA); FireTimeUpdate(false); } @@ -3134,6 +3121,11 @@ void HTMLMediaElement::UpdateReadyStateForData(MediaDecoderOwner::NextFrameStatu return; } + if (aNextFrame == MediaDecoderOwner::NEXT_FRAME_UNAVAILABLE_SEEKING) { + ChangeReadyState(nsIDOMHTMLMediaElement::HAVE_METADATA); + return; + } + if (mDownloadSuspendedByCache && mDecoder && !mDecoder->IsEnded()) { // The decoder has signaled that the download has been suspended by the // media cache. So move readyState into HAVE_ENOUGH_DATA, in case there's @@ -3208,7 +3200,7 @@ void HTMLMediaElement::ChangeReadyState(nsMediaReadyState aState) // Handle raising of "waiting" event during seek (see 4.8.10.9) if (mPlayingBeforeSeek && - oldState < nsIDOMHTMLMediaElement::HAVE_FUTURE_DATA) { + mReadyState < nsIDOMHTMLMediaElement::HAVE_FUTURE_DATA) { DispatchAsyncEvent(NS_LITERAL_STRING("waiting")); } diff --git a/content/media/MediaDecoder.cpp b/content/media/MediaDecoder.cpp index 27f4fad57bfb..d83380aacd58 100644 --- a/content/media/MediaDecoder.cpp +++ b/content/media/MediaDecoder.cpp @@ -942,10 +942,8 @@ void MediaDecoder::UpdatePlaybackRate() void MediaDecoder::NotifySuspendedStatusChanged() { MOZ_ASSERT(NS_IsMainThread()); - if (!mResource) - return; - bool suspended = mResource->IsSuspendedByCache(); - if (mOwner) { + if (mResource && mOwner) { + bool suspended = mResource->IsSuspendedByCache(); mOwner->NotifySuspendedByCache(suspended); UpdateReadyStateForData(); } @@ -958,7 +956,6 @@ void MediaDecoder::NotifyBytesDownloaded() ReentrantMonitorAutoEnter mon(GetReentrantMonitor()); UpdatePlaybackRate(); } - UpdateReadyStateForData(); Progress(false); } @@ -982,9 +979,10 @@ void MediaDecoder::NotifyDownloadEnded(nsresult aStatus) } if (NS_SUCCEEDED(aStatus)) { - UpdateReadyStateForData(); // A final progress event will be fired by the MediaResource calling // DownloadSuspended on the element. + // Also NotifySuspendedStatusChanged() will be called to update readyState + // if download ended with success. } else if (aStatus != NS_BASE_STREAM_CLOSED) { NetworkError(); } diff --git a/content/media/MediaDecoderOwner.h b/content/media/MediaDecoderOwner.h index 7cd2b1e0da8d..1d9cb596d624 100644 --- a/content/media/MediaDecoderOwner.h +++ b/content/media/MediaDecoderOwner.h @@ -108,6 +108,8 @@ public: // The next frame of audio/video is unavailable because the decoder // is paused while it buffers up data NEXT_FRAME_UNAVAILABLE_BUFFERING, + // The next frame of audio/video is unavailable for the decoder is seeking. + NEXT_FRAME_UNAVAILABLE_SEEKING, // The next frame of audio/video is unavailable for some other reasons NEXT_FRAME_UNAVAILABLE, // The next frame is unavailable due to waiting for more Media Source diff --git a/content/media/MediaDecoderStateMachine.cpp b/content/media/MediaDecoderStateMachine.cpp index ae25a00b770b..e959a051a3a4 100644 --- a/content/media/MediaDecoderStateMachine.cpp +++ b/content/media/MediaDecoderStateMachine.cpp @@ -1212,8 +1212,10 @@ void MediaDecoderStateMachine::ClearPositionChangeFlag() MediaDecoderOwner::NextFrameStatus MediaDecoderStateMachine::GetNextFrameStatus() { ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor()); - if (IsBuffering() || IsSeeking()) { + if (IsBuffering()) { return MediaDecoderOwner::NEXT_FRAME_UNAVAILABLE_BUFFERING; + } else if (IsSeeking()) { + return MediaDecoderOwner::NEXT_FRAME_UNAVAILABLE_SEEKING; } else if (HaveNextFrameData()) { return MediaDecoderOwner::NEXT_FRAME_AVAILABLE; } @@ -2886,6 +2888,10 @@ void MediaDecoderStateMachine::UpdateReadyState() { AssertCurrentThreadInMonitor(); MediaDecoderOwner::NextFrameStatus nextFrameStatus = GetNextFrameStatus(); + // FIXME: This optimization could result in inconsistent next frame status + // between the decoder and state machine when GetNextFrameStatus() is called + // by the decoder without updating mLastFrameStatus. + // Note not to regress bug 882027 when fixing this bug. if (nextFrameStatus == mLastFrameStatus) { return; } diff --git a/content/media/omx/AudioOffloadPlayer.cpp b/content/media/omx/AudioOffloadPlayer.cpp index 44d912815162..41a4693b21a8 100644 --- a/content/media/omx/AudioOffloadPlayer.cpp +++ b/content/media/omx/AudioOffloadPlayer.cpp @@ -669,7 +669,7 @@ MediaDecoderOwner::NextFrameStatus AudioOffloadPlayer::GetNextFrameStatus() { MOZ_ASSERT(NS_IsMainThread()); if (mPlayState == MediaDecoder::PLAY_STATE_SEEKING) { - return MediaDecoderOwner::NEXT_FRAME_UNAVAILABLE_BUFFERING; + return MediaDecoderOwner::NEXT_FRAME_UNAVAILABLE_SEEKING; } else if (mPlayState == MediaDecoder::PLAY_STATE_ENDED) { return MediaDecoderOwner::NEXT_FRAME_UNAVAILABLE; } else { From d21e0abb6eb9a6096bbff152482d443b4654cef2 Mon Sep 17 00:00:00 2001 From: JW Wang Date: Wed, 24 Sep 2014 19:26:00 +1200 Subject: [PATCH 58/82] Bug 883731 - Progress update should be associated with network state transitions. Also centralize network state transition code. r=cpearce --- .../html/content/public/HTMLMediaElement.h | 9 ++ content/html/content/src/HTMLMediaElement.cpp | 103 +++++++++++++----- content/media/MediaDecoder.cpp | 15 +-- content/media/MediaDecoder.h | 12 +- content/media/MediaDecoderOwner.h | 2 + content/media/MediaResource.cpp | 5 +- content/media/omx/MediaOmxCommonDecoder.cpp | 1 - 7 files changed, 106 insertions(+), 41 deletions(-) diff --git a/content/html/content/public/HTMLMediaElement.h b/content/html/content/public/HTMLMediaElement.h index 0fe536c1f436..eb4687d28076 100755 --- a/content/html/content/public/HTMLMediaElement.h +++ b/content/html/content/public/HTMLMediaElement.h @@ -204,6 +204,9 @@ public: // ongoing. virtual void DownloadResumed(bool aForceNetworkLoading = false) MOZ_FINAL MOZ_OVERRIDE; + // Called to indicate the download is progressing. + virtual void DownloadProgressed() MOZ_FINAL MOZ_OVERRIDE; + // Called by the media decoder to indicate that the download has stalled // (no data has arrived for a while). virtual void DownloadStalled() MOZ_FINAL MOZ_OVERRIDE; @@ -643,6 +646,12 @@ protected: */ void ChangeReadyState(nsMediaReadyState aState); + /** + * Use this method to change the mNetworkState member, so required + * actions will be taken during the transition. + */ + void ChangeNetworkState(nsMediaNetworkState aState); + /** * These two methods are called by the WakeLockBoolWrapper when the wakelock * has to be created or released. diff --git a/content/html/content/src/HTMLMediaElement.cpp b/content/html/content/src/HTMLMediaElement.cpp index 4cc8be686921..be1952a0cb82 100755 --- a/content/html/content/src/HTMLMediaElement.cpp +++ b/content/html/content/src/HTMLMediaElement.cpp @@ -618,6 +618,11 @@ void HTMLMediaElement::ShutdownDecoder() { RemoveMediaElementFromURITable(); NS_ASSERTION(mDecoder, "Must have decoder to shut down"); + // TODO: This should be handled by ChangeNetworkState() so we have only one + // place to call StopProgress(). + if (mNetworkState == nsIDOMHTMLMediaElement::NETWORK_LOADING) { + mDecoder->StopProgress(); + } mDecoder->Shutdown(); mDecoder = nullptr; } @@ -661,7 +666,6 @@ void HTMLMediaElement::AbortExistingLoads() } mError = nullptr; - mBegun = false; mLoadedDataFired = false; mAutoplaying = true; mIsLoadingFromSourceChildren = false; @@ -675,8 +679,8 @@ void HTMLMediaElement::AbortExistingLoads() mTags = nullptr; if (mNetworkState != nsIDOMHTMLMediaElement::NETWORK_EMPTY) { - mNetworkState = nsIDOMHTMLMediaElement::NETWORK_EMPTY; NS_ASSERTION(!mDecoder && !mSrcStream, "How did someone setup a new stream/decoder already?"); + ChangeNetworkState(nsIDOMHTMLMediaElement::NETWORK_EMPTY); ChangeReadyState(nsIDOMHTMLMediaElement::HAVE_NOTHING); mPaused = true; @@ -702,7 +706,7 @@ void HTMLMediaElement::NoSupportedMediaSourceError() NS_ASSERTION(mDelayingLoadEvent, "Load event not delayed during source selection?"); mError = new MediaError(this, nsIDOMMediaError::MEDIA_ERR_SRC_NOT_SUPPORTED); - mNetworkState = nsIDOMHTMLMediaElement::NETWORK_NO_SOURCE; + ChangeNetworkState(nsIDOMHTMLMediaElement::NETWORK_NO_SOURCE); DispatchAsyncEvent(NS_LITERAL_STRING("error")); // This clears mDelayingLoadEvent, so AddRemoveSelfReference will be called ChangeDelayLoadStatus(false); @@ -746,7 +750,7 @@ void HTMLMediaElement::RunInStableState(nsIRunnable* aRunnable) void HTMLMediaElement::QueueLoadFromSourceTask() { ChangeDelayLoadStatus(true); - mNetworkState = nsIDOMHTMLMediaElement::NETWORK_LOADING; + ChangeNetworkState(nsIDOMHTMLMediaElement::NETWORK_LOADING); RunInStableState( NS_NewRunnableMethod(this, &HTMLMediaElement::LoadFromSourceChildren)); } @@ -757,7 +761,7 @@ void HTMLMediaElement::QueueSelectResourceTask() if (mHaveQueuedSelectResource) return; mHaveQueuedSelectResource = true; - mNetworkState = nsIDOMHTMLMediaElement::NETWORK_NO_SOURCE; + ChangeNetworkState(nsIDOMHTMLMediaElement::NETWORK_NO_SOURCE); RunInStableState( NS_NewRunnableMethod(this, &HTMLMediaElement::SelectResourceWrapper)); } @@ -818,7 +822,7 @@ void HTMLMediaElement::SelectResource() !HasSourceChildren(this)) { // The media element has neither a src attribute nor any source // element children, abort the load. - mNetworkState = nsIDOMHTMLMediaElement::NETWORK_EMPTY; + ChangeNetworkState(nsIDOMHTMLMediaElement::NETWORK_EMPTY); // This clears mDelayingLoadEvent, so AddRemoveSelfReference will be called ChangeDelayLoadStatus(false); return; @@ -826,7 +830,7 @@ void HTMLMediaElement::SelectResource() ChangeDelayLoadStatus(true); - mNetworkState = nsIDOMHTMLMediaElement::NETWORK_LOADING; + ChangeNetworkState(nsIDOMHTMLMediaElement::NETWORK_LOADING); // Load event was delayed, and still is, so no need to call // AddRemoveSelfReference, since it must still be held DispatchAsyncEvent(NS_LITERAL_STRING("loadstart")); @@ -922,7 +926,7 @@ void HTMLMediaElement::LoadFromSourceChildren() // Exhausted candidates, wait for more candidates to be appended to // the media element. mLoadWaitStatus = WAITING_FOR_SOURCE; - mNetworkState = nsIDOMHTMLMediaElement::NETWORK_NO_SOURCE; + ChangeNetworkState(nsIDOMHTMLMediaElement::NETWORK_NO_SOURCE); ChangeDelayLoadStatus(false); ReportLoadError("MediaLoadExhaustedCandidates"); return; @@ -991,8 +995,7 @@ void HTMLMediaElement::LoadFromSourceChildren() void HTMLMediaElement::SuspendLoad() { mSuspendedForPreloadNone = true; - mNetworkState = nsIDOMHTMLMediaElement::NETWORK_IDLE; - DispatchAsyncEvent(NS_LITERAL_STRING("suspend")); + ChangeNetworkState(nsIDOMHTMLMediaElement::NETWORK_IDLE); ChangeDelayLoadStatus(false); } @@ -1003,7 +1006,7 @@ void HTMLMediaElement::ResumeLoad(PreloadAction aAction) mSuspendedForPreloadNone = false; mPreloadAction = aAction; ChangeDelayLoadStatus(true); - mNetworkState = nsIDOMHTMLMediaElement::NETWORK_LOADING; + ChangeNetworkState(nsIDOMHTMLMediaElement::NETWORK_LOADING); if (!mIsLoadingFromSourceChildren) { // We were loading from the element's src attribute. if (NS_FAILED(LoadResource())) { @@ -2155,11 +2158,10 @@ void HTMLMediaElement::SetPlayedOrSeeked(bool aValue) void HTMLMediaElement::ResetConnectionState() { - mBegun = false; SetCurrentTime(0); FireTimeUpdate(false); DispatchAsyncEvent(NS_LITERAL_STRING("ended")); - mNetworkState = nsIDOMHTMLMediaElement::NETWORK_EMPTY; + ChangeNetworkState(nsIDOMHTMLMediaElement::NETWORK_EMPTY); AddRemoveSelfReference(); ChangeDelayLoadStatus(false); ChangeReadyState(nsIDOMHTMLMediaElement::HAVE_NOTHING); @@ -2647,7 +2649,7 @@ nsresult HTMLMediaElement::FinishDecoderSetup(MediaDecoder* aDecoder, nsIStreamListener** aListener, MediaDecoder* aCloneDonor) { - mNetworkState = nsIDOMHTMLMediaElement::NETWORK_LOADING; + ChangeNetworkState(nsIDOMHTMLMediaElement::NETWORK_LOADING); // Force a same-origin check before allowing events for this media resource. mMediaSecurityVerified = false; @@ -2668,6 +2670,8 @@ nsresult HTMLMediaElement::FinishDecoderSetup(MediaDecoder* aDecoder, mDecoder->SetVolume(mMuted ? 0.0 : mVolume); mDecoder->SetPreservesPitch(mPreservesPitch); mDecoder->SetPlaybackRate(mPlaybackRate); + // Start progress timer for we are in NETWORK_LOADING. + mDecoder->StartProgress(); #ifdef MOZ_EME if (mMediaKeys) { @@ -2719,7 +2723,6 @@ nsresult HTMLMediaElement::FinishDecoderSetup(MediaDecoder* aDecoder, NS_ASSERTION(NS_SUCCEEDED(rv) == (MediaElementTableCount(this, mLoadingSrc) == 1), "Media element should have single table entry if decode initialized"); - mBegun = true; return rv; } @@ -2868,8 +2871,7 @@ void HTMLMediaElement::SetupSrcMediaStreamPlayback(DOMMediaStream* aStream) ChangeReadyState(nsIDOMHTMLMediaElement::HAVE_METADATA); DispatchAsyncEvent(NS_LITERAL_STRING("durationchange")); DispatchAsyncEvent(NS_LITERAL_STRING("loadedmetadata")); - DispatchAsyncEvent(NS_LITERAL_STRING("suspend")); - mNetworkState = nsIDOMHTMLMediaElement::NETWORK_IDLE; + ChangeNetworkState(nsIDOMHTMLMediaElement::NETWORK_IDLE); AddRemoveSelfReference(); // FirstFrameLoaded() will be called when the stream has current data. } @@ -2996,13 +2998,12 @@ void HTMLMediaElement::Error(uint16_t aErrorCode) aErrorCode == nsIDOMMediaError::MEDIA_ERR_ABORTED, "Only use nsIDOMMediaError codes!"); mError = new MediaError(this, aErrorCode); - mBegun = false; DispatchAsyncEvent(NS_LITERAL_STRING("error")); if (mReadyState == nsIDOMHTMLMediaElement::HAVE_NOTHING) { - mNetworkState = nsIDOMHTMLMediaElement::NETWORK_EMPTY; + ChangeNetworkState(nsIDOMHTMLMediaElement::NETWORK_EMPTY); DispatchAsyncEvent(NS_LITERAL_STRING("emptied")); } else { - mNetworkState = nsIDOMHTMLMediaElement::NETWORK_IDLE; + ChangeNetworkState(nsIDOMHTMLMediaElement::NETWORK_IDLE); } AddRemoveSelfReference(); ChangeDelayLoadStatus(false); @@ -3076,22 +3077,28 @@ void HTMLMediaElement::NotifySuspendedByCache(bool aIsSuspended) void HTMLMediaElement::DownloadSuspended() { - DispatchAsyncEvent(NS_LITERAL_STRING("progress")); + DownloadProgressed(); if (mBegun) { - mNetworkState = nsIDOMHTMLMediaElement::NETWORK_IDLE; + ChangeNetworkState(nsIDOMHTMLMediaElement::NETWORK_IDLE); AddRemoveSelfReference(); - DispatchAsyncEvent(NS_LITERAL_STRING("suspend")); } } void HTMLMediaElement::DownloadResumed(bool aForceNetworkLoading) { if (mBegun || aForceNetworkLoading) { - mNetworkState = nsIDOMHTMLMediaElement::NETWORK_LOADING; + ChangeNetworkState(nsIDOMHTMLMediaElement::NETWORK_LOADING); AddRemoveSelfReference(); } } +void HTMLMediaElement::DownloadProgressed() +{ + if (mNetworkState == nsIDOMHTMLMediaElement::NETWORK_LOADING) { + DispatchAsyncEvent(NS_LITERAL_STRING("progress")); + } +} + void HTMLMediaElement::DownloadStalled() { if (mNetworkState == nsIDOMHTMLMediaElement::NETWORK_LOADING) { @@ -3175,7 +3182,7 @@ void HTMLMediaElement::UpdateReadyStateForData(MediaDecoderOwner::NextFrameStatu } #ifdef PR_LOGGING -static const char* gReadyStateToString[] = { +static const char* const gReadyStateToString[] = { "HAVE_NOTHING", "HAVE_METADATA", "HAVE_CURRENT_DATA", @@ -3234,6 +3241,50 @@ void HTMLMediaElement::ChangeReadyState(nsMediaReadyState aState) } } +#ifdef PR_LOGGING +static const char* const gNetworkStateToString[] = { + "EMPTY", + "IDLE", + "LOADING", + "NO_SOURCE" + }; +#endif + +void HTMLMediaElement::ChangeNetworkState(nsMediaNetworkState aState) +{ + if (mNetworkState == aState) { + return; + } + + nsMediaNetworkState oldState = mNetworkState; + mNetworkState = aState; + LOG(PR_LOG_DEBUG, ("%p Network state changed to %s", this, gNetworkStateToString[aState])); + + // TODO: |mBegun| reflects the download status. We should be able to remove + // it and check |mNetworkState| only. + + if (oldState == nsIDOMHTMLMediaElement::NETWORK_LOADING) { + // Reset |mBegun| since we're not downloading anymore. + mBegun = false; + if (mDecoder) { + // Stop progress notification when exiting NETWORK_LOADING. + mDecoder->StopProgress(); + } + } + + if (mNetworkState == nsIDOMHTMLMediaElement::NETWORK_LOADING) { + // Download is begun. + mBegun = true; + if (mDecoder) { + // Start progress notification when entering NETWORK_LOADING. + mDecoder->StartProgress(); + } + } else if (mNetworkState == nsIDOMHTMLMediaElement::NETWORK_IDLE && !mError) { + // Fire 'suspend' event when entering NETWORK_IDLE and no error presented. + DispatchAsyncEvent(NS_LITERAL_STRING("suspend")); + } +} + bool HTMLMediaElement::CanActivateAutoplay() { // For stream inputs, we activate autoplay on HAVE_CURRENT_DATA because @@ -3362,7 +3413,7 @@ bool HTMLMediaElement::IsPlaybackEnded() const // TODO: // the current playback position is equal to the effective end of the media resource. // See bug 449157. - return mNetworkState >= nsIDOMHTMLMediaElement::HAVE_METADATA && + return mReadyState >= nsIDOMHTMLMediaElement::HAVE_METADATA && mDecoder ? mDecoder->IsEnded() : false; } diff --git a/content/media/MediaDecoder.cpp b/content/media/MediaDecoder.cpp index d83380aacd58..b36c5dd87eac 100644 --- a/content/media/MediaDecoder.cpp +++ b/content/media/MediaDecoder.cpp @@ -139,7 +139,6 @@ void MediaDecoder::SetDormantIfNecessary(bool aDormant) if(aDormant) { // enter dormant state - StopProgress(); DestroyDecodedStream(); mDecoderStateMachine->SetDormant(true); @@ -500,7 +499,8 @@ void MediaDecoder::Shutdown() ChangeState(PLAY_STATE_SHUTDOWN); - StopProgress(); + // If we hit this assertion, there might be a bug in network state transition. + NS_ASSERTION(!mProgressTimer, "Progress timer should've been stopped."); mOwner = nullptr; MediaShutdownManager::Instance().Unregister(this); @@ -1517,6 +1517,7 @@ static void ProgressCallback(nsITimer* aTimer, void* aClosure) void MediaDecoder::Progress(bool aTimer) { + MOZ_ASSERT(NS_IsMainThread()); if (!mOwner) return; @@ -1532,7 +1533,7 @@ void MediaDecoder::Progress(bool aTimer) now - mProgressTime >= TimeDuration::FromMilliseconds(PROGRESS_MS)) && !mDataTime.IsNull() && now - mDataTime <= TimeDuration::FromMilliseconds(PROGRESS_MS)) { - mOwner->DispatchAsyncEvent(NS_LITERAL_STRING("progress")); + mOwner->DownloadProgressed(); mProgressTime = now; } @@ -1546,8 +1547,8 @@ void MediaDecoder::Progress(bool aTimer) nsresult MediaDecoder::StartProgress() { - if (mProgressTimer) - return NS_OK; + MOZ_ASSERT(NS_IsMainThread()); + NS_ASSERTION(!mProgressTimer, "Already started progress timer."); mProgressTimer = do_CreateInstance("@mozilla.org/timer;1"); return mProgressTimer->InitWithFuncCallback(ProgressCallback, @@ -1558,8 +1559,8 @@ nsresult MediaDecoder::StartProgress() nsresult MediaDecoder::StopProgress() { - if (!mProgressTimer) - return NS_OK; + MOZ_ASSERT(NS_IsMainThread()); + NS_ASSERTION(mProgressTimer, "Already stopped progress timer."); nsresult rv = mProgressTimer->Cancel(); mProgressTimer = nullptr; diff --git a/content/media/MediaDecoder.h b/content/media/MediaDecoder.h index 497e685df80c..d0026a907f6f 100644 --- a/content/media/MediaDecoder.h +++ b/content/media/MediaDecoder.h @@ -689,6 +689,12 @@ public: return mPlayState; } + // Called by the media element to start timer to update download progress. + nsresult StartProgress(); + + // Called by the media element to stop progress information timer. + nsresult StopProgress(); + // Fire progress events if needed according to the time and byte // constraints outlined in the specification. aTimer is true // if the method is called as a result of the progress timer rather @@ -1166,12 +1172,6 @@ protected: // True if the stream is infinite (e.g. a webradio). bool mInfiniteStream; - // Start timer to update download progress information. - nsresult StartProgress(); - - // Stop progress information timer. - nsresult StopProgress(); - // Ensures our media stream has been pinned. void PinForSeek(); diff --git a/content/media/MediaDecoderOwner.h b/content/media/MediaDecoderOwner.h index 1d9cb596d624..cdbac3a9a1bb 100644 --- a/content/media/MediaDecoderOwner.h +++ b/content/media/MediaDecoderOwner.h @@ -18,6 +18,8 @@ class HTMLMediaElement; class MediaDecoderOwner { public: + // Called by the media decoder to indicate that the download is progressing. + virtual void DownloadProgressed() = 0; // Called by the media decoder to indicate that the download has stalled // (no data has arrived for a while). virtual void DownloadStalled() = 0; diff --git a/content/media/MediaResource.cpp b/content/media/MediaResource.cpp index 89befcf8c214..0864c91dbe44 100644 --- a/content/media/MediaResource.cpp +++ b/content/media/MediaResource.cpp @@ -905,8 +905,11 @@ void ChannelMediaResource::Resume() // There is (or may be) data to read at mOffset, so start reading it. // Need to recreate the channel. CacheClientSeek(mOffset, false); + element->DownloadResumed(); + } else { + // The channel remains dead. Do not notify DownloadResumed() which + // will leave the media element in NETWORK_LOADING state. } - element->DownloadResumed(); } } } diff --git a/content/media/omx/MediaOmxCommonDecoder.cpp b/content/media/omx/MediaOmxCommonDecoder.cpp index 8e76dc37d09e..134c1d629c6e 100644 --- a/content/media/omx/MediaOmxCommonDecoder.cpp +++ b/content/media/omx/MediaOmxCommonDecoder.cpp @@ -101,7 +101,6 @@ MediaOmxCommonDecoder::PauseStateMachine() if (!mDecoderStateMachine) { return; } - StopProgress(); mDecoderStateMachine->SetDormant(true); } From 290e16286130be2ef88019e1beb7797ec0512b47 Mon Sep 17 00:00:00 2001 From: JW Wang Date: Mon, 29 Sep 2014 16:53:00 +1300 Subject: [PATCH 59/82] Bug 883731 - Per spec. autoplay activation should be handled by readyState transition. r=cpearce --- content/html/content/src/HTMLMediaElement.cpp | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/content/html/content/src/HTMLMediaElement.cpp b/content/html/content/src/HTMLMediaElement.cpp index be1952a0cb82..616b4bd04be8 100755 --- a/content/html/content/src/HTMLMediaElement.cpp +++ b/content/html/content/src/HTMLMediaElement.cpp @@ -3070,9 +3070,6 @@ void HTMLMediaElement::SeekCompleted() void HTMLMediaElement::NotifySuspendedByCache(bool aIsSuspended) { mDownloadSuspendedByCache = aIsSuspended; - // If this is an autoplay element, we may need to kick off its autoplaying - // now so we consume data and hopefully free up cache space. - CheckAutoplayDataReady(); } void HTMLMediaElement::DownloadSuspended() @@ -3293,8 +3290,7 @@ bool HTMLMediaElement::CanActivateAutoplay() return !mPausedForInactiveDocumentOrChannel && mAutoplaying && mPaused && - (mDownloadSuspendedByCache || - (mDecoder && mReadyState >= nsIDOMHTMLMediaElement::HAVE_ENOUGH_DATA) || + ((mDecoder && mReadyState >= nsIDOMHTMLMediaElement::HAVE_ENOUGH_DATA) || (mSrcStream && mReadyState >= nsIDOMHTMLMediaElement::HAVE_CURRENT_DATA)) && HasAttr(kNameSpaceID_None, nsGkAtoms::autoplay) && mAutoplayEnabled && From c2c7007b5fa607f64d925e115e5311c8ca7aae44 Mon Sep 17 00:00:00 2001 From: Camilo Viecco Date: Wed, 3 Sep 2014 10:24:12 -0700 Subject: [PATCH 60/82] Bug 787133 - (hpkp) Part 1/2. Header Parsing and interface within PSM. r=keeler, r=mcmanus --- b2g/chrome/content/devtools/hud.js | 1 + browser/devtools/webconsole/webconsole.js | 1 + .../en-US/chrome/security/security.properties | 2 + modules/libpref/init/all.js | 4 + .../base/public/nsISiteSecurityService.idl | 22 +- netwerk/protocol/http/nsHttpChannel.cpp | 138 ++++--- netwerk/protocol/http/nsHttpChannel.h | 20 +- netwerk/test/TestSTSParser.cpp | 8 +- security/certverifier/moz.build | 5 + .../boot/src/PublicKeyPinningService.cpp | 7 + .../boot/src/PublicKeyPinningService.h | 9 + .../boot/src/nsSiteSecurityService.cpp | 365 +++++++++++++++--- .../manager/boot/src/nsSiteSecurityService.h | 17 +- security/manager/ssl/src/moz.build | 6 + .../browser/browser_bug627234_perwindowpb.js | 27 +- security/manager/ssl/tests/unit/head_psm.js | 24 ++ .../tests/unit/test_ocsp_no_hsts_upgrade.js | 3 +- .../ssl/tests/unit/test_sss_eviction.js | 3 +- .../ssl/tests/unit/test_sss_savestate.js | 4 +- .../ssl/tests/unit/test_sts_ipv4_ipv6.js | 3 +- .../unit/test_sts_preloadlist_perwindowpb.js | 21 +- 21 files changed, 560 insertions(+), 130 deletions(-) diff --git a/b2g/chrome/content/devtools/hud.js b/b2g/chrome/content/devtools/hud.js index f45fa7b791be..38169779fba9 100644 --- a/b2g/chrome/content/devtools/hud.js +++ b/b2g/chrome/content/devtools/hud.js @@ -269,6 +269,7 @@ let consoleWatcher = { 'Mixed Content Message', 'CSP', 'Invalid HSTS Headers', + 'Invalid HPKP Headers', 'Insecure Password Field', 'SSL', 'CORS' diff --git a/browser/devtools/webconsole/webconsole.js b/browser/devtools/webconsole/webconsole.js index 0813288c9a09..b4fd2e6cec1b 100644 --- a/browser/devtools/webconsole/webconsole.js +++ b/browser/devtools/webconsole/webconsole.js @@ -4674,6 +4674,7 @@ var Utils = { case "Mixed Content Message": case "CSP": case "Invalid HSTS Headers": + case "Invalid HPKP Headers": case "Insecure Password Field": case "SSL": case "CORS": diff --git a/dom/locales/en-US/chrome/security/security.properties b/dom/locales/en-US/chrome/security/security.properties index 4a74b02e5950..5a3037ef4c2c 100644 --- a/dom/locales/en-US/chrome/security/security.properties +++ b/dom/locales/en-US/chrome/security/security.properties @@ -8,6 +8,8 @@ CrossSiteRequestBlocked=Cross-Origin Request Blocked: The Same Origin Policy dis # LOCALIZATION NOTE: Do not translate "Strict-Transport-Security" or "HSTS" InvalidSTSHeaders=The site specified an invalid Strict-Transport-Security header. +# LOCALIZATION NOTE: Do not translate "Public-Key-Pins or HPKP" +InvalidPKPHeaders=The site specified an invalid Public-Key-Pins header. InsecurePasswordsPresentOnPage=Password fields present on an insecure (http://) page. This is a security risk that allows user login credentials to be stolen. InsecureFormActionPasswordsPresent=Password fields present in a form with an insecure (http://) form action. This is a security risk that allows user login credentials to be stolen. InsecurePasswordsPresentOnIframe=Password fields present on an insecure (http://) iframe. This is a security risk that allows user login credentials to be stolen. diff --git a/modules/libpref/init/all.js b/modules/libpref/init/all.js index 81e1a680b4bd..bb1470872e97 100644 --- a/modules/libpref/init/all.js +++ b/modules/libpref/init/all.js @@ -1754,6 +1754,10 @@ pref("security.mixed_content.block_display_content", false); // Disable pinning checks by default. pref("security.cert_pinning.enforcement_level", 0); +// Do not process hpkp headers rooted by not built in roots by default. +// This is to prevent accidental pinning from MITM devices and is used +// for tests. +pref("security.cert_pinning.process_headers_from_non_builtin_roots", false); // Modifier key prefs: default to Windows settings, // menu access key = alt, accelerator key = control. diff --git a/netwerk/base/public/nsISiteSecurityService.idl b/netwerk/base/public/nsISiteSecurityService.idl index 0a9ae76972c2..b5e8c235d042 100644 --- a/netwerk/base/public/nsISiteSecurityService.idl +++ b/netwerk/base/public/nsISiteSecurityService.idl @@ -7,6 +7,7 @@ interface nsIURI; interface nsIObserver; interface nsIHttpChannel; +interface nsISSLStatus; %{C++ template class nsTArray; @@ -22,7 +23,7 @@ namespace mozilla [ref] native nsCStringTArrayRef(nsTArray); [ref] native mozillaPkixTime(mozilla::pkix::Time); -[scriptable, uuid(35816ea0-3ab5-11e4-8613-180373d97f23)] +[scriptable, uuid(46555f70-3ab5-11e4-8613-180373d97f23)] interface nsISiteSecurityService : nsISupports { const uint32_t HEADER_HSTS = 0; @@ -31,14 +32,19 @@ interface nsISiteSecurityService : nsISupports /** * Parses a given HTTP header and records the results internally. - * Currently the only header type supported is HSTS (aka STS). + * Currently two header types are supported: HSTS (aka STS) and HPKP * The format of the HSTS header is defined by the HSTS specification: * https://tools.ietf.org/html/rfc6797 * and allows a host to specify that future HTTP requests should be * upgraded to HTTPS. + * The Format of the HPKP header is currently defined by: + * https://tools.ietf.org/html/draft-ietf-websec-key-pinning-20 + * and allows a host to speficy a subset of trusted anchors to be used + * in future HTTPS connections. * * @param aType the type of security header in question. * @param aSourceURI the URI of the resource with the HTTP header. + * @param aSSLStatus the SSLStatus of the current channel * @param aHeader the HTTP response header specifying security data. * @param aFlags options for this request as defined in nsISocketProvider: * NO_PERMANENT_STORAGE @@ -52,10 +58,22 @@ interface nsISiteSecurityService : nsISupports void processHeader(in uint32_t aType, in nsIURI aSourceURI, in string aHeader, + in nsISSLStatus aSSLStatus, in uint32_t aFlags, [optional] out unsigned long long aMaxAge, [optional] out boolean aIncludeSubdomains); + /** + * Same as processHeader but without checking for the security properties + * of the connection. Use ONLY for testing. + */ + void unsafeProcessHeader(in uint32_t aType, + in nsIURI aSourceURI, + in string aHeader, + in uint32_t aFlags, + [optional] out unsigned long long aMaxAge, + [optional] out boolean aIncludeSubdomains); + /** * Given a header type, removes state relating to that header of a host, * including the includeSubdomains state that would affect subdomains. diff --git a/netwerk/protocol/http/nsHttpChannel.cpp b/netwerk/protocol/http/nsHttpChannel.cpp index 0eb469f657bf..ae2b9b58c907 100644 --- a/netwerk/protocol/http/nsHttpChannel.cpp +++ b/netwerk/protocol/http/nsHttpChannel.cpp @@ -1041,6 +1041,66 @@ nsHttpChannel::ProcessFailedProxyConnect(uint32_t httpStatus) return rv; } +/** + * Process a single security header. Only two types are suported HSTS and HPKP. + */ +nsresult +nsHttpChannel::ProcessSingleSecurityHeader(uint32_t aType, + nsISSLStatus *aSSLStatus, + uint32_t aFlags) +{ + nsHttpAtom atom; + switch (aType) { + case nsISiteSecurityService::HEADER_HSTS: + atom = nsHttp::ResolveAtom("Strict-Transport-Security"); + break; + case nsISiteSecurityService::HEADER_HPKP: + atom = nsHttp::ResolveAtom("Public-Key-Pins"); + break; + default: + NS_NOTREACHED("Invalid security header type"); + return NS_ERROR_FAILURE; + } + + nsAutoCString securityHeader; + nsresult rv = mResponseHead->GetHeader(atom, securityHeader); + if (NS_SUCCEEDED(rv)) { + nsISiteSecurityService* sss = gHttpHandler->GetSSService(); + NS_ENSURE_TRUE(sss, NS_ERROR_OUT_OF_MEMORY); + // Process header will now discard the headers itself if the channel + // wasn't secure (whereas before it had to be checked manually) + rv = sss->ProcessHeader(aType, mURI, securityHeader.get(), aSSLStatus, + aFlags, nullptr, nullptr); + if (NS_FAILED(rv)) { + nsAutoString consoleErrorCategory; + nsAutoString consoleErrorTag; + switch (aType) { + case nsISiteSecurityService::HEADER_HSTS: + consoleErrorTag = NS_LITERAL_STRING("InvalidSTSHeaders"); + consoleErrorCategory = NS_LITERAL_STRING("Invalid HSTS Headers"); + break; + case nsISiteSecurityService::HEADER_HPKP: + consoleErrorTag = NS_LITERAL_STRING("InvalidPKPHeaders"); + consoleErrorCategory = NS_LITERAL_STRING("Invalid HPKP Headers"); + break; + default: + return NS_ERROR_FAILURE; + } + AddSecurityMessage(consoleErrorTag, consoleErrorCategory); + LOG(("nsHttpChannel: Failed to parse %s header, continuing load.\n", + atom.get())); + } + } else { + if (rv != NS_ERROR_NOT_AVAILABLE) { + // All other errors are fatal + NS_ENSURE_SUCCESS(rv, rv); + } + LOG(("nsHttpChannel: No %s header, continuing load.\n", + atom.get())); + } + return NS_OK; +} + /** * Decide whether or not to remember Strict-Transport-Security, and whether * or not to enforce channel integrity. @@ -1049,15 +1109,16 @@ nsHttpChannel::ProcessFailedProxyConnect(uint32_t httpStatus) * it's an HTTPS connection. */ nsresult -nsHttpChannel::ProcessSTSHeader() +nsHttpChannel::ProcessSecurityHeaders() { nsresult rv; bool isHttps = false; rv = mURI->SchemeIs("https", &isHttps); NS_ENSURE_SUCCESS(rv, rv); - // If this channel is not loading securely, STS doesn't do anything. - // The upgrade to HTTPS takes place earlier in the channel load process. + // If this channel is not loading securely, STS or PKP doesn't do anything. + // In the case of HSTS, the upgrade to HTTPS takes place earlier in the + // channel load process. if (!isHttps) return NS_OK; @@ -1065,71 +1126,35 @@ nsHttpChannel::ProcessSTSHeader() rv = mURI->GetAsciiHost(asciiHost); NS_ENSURE_SUCCESS(rv, NS_OK); - // If the channel is not a hostname, but rather an IP, STS doesn't do - // anything. + // If the channel is not a hostname, but rather an IP, do not process STS + // or PKP headers PRNetAddr hostAddr; if (PR_SUCCESS == PR_StringToNetAddr(asciiHost.get(), &hostAddr)) return NS_OK; - nsISiteSecurityService* sss = gHttpHandler->GetSSService(); - NS_ENSURE_TRUE(sss, NS_ERROR_OUT_OF_MEMORY); - // mSecurityInfo may not always be present, and if it's not then it is okay - // to just disregard any STS headers since we know nothing about the + // to just disregard any security headers since we know nothing about the // security of the connection. NS_ENSURE_TRUE(mSecurityInfo, NS_OK); - // Check the trustworthiness of the channel (are there any cert errors?) - // If there are certificate errors, we still load the data, we just ignore - // any STS headers that are present. - bool tlsIsBroken = false; - rv = sss->ShouldIgnoreHeaders(mSecurityInfo, &tlsIsBroken); - NS_ENSURE_SUCCESS(rv, NS_OK); - - // If this was already an STS host, the connection should have been aborted - // by the bad cert handler in the case of cert errors. If it didn't abort the connection, - // there's probably something funny going on. - // If this wasn't an STS host, errors are allowed, but no more STS processing - // will happen during the session. - bool wasAlreadySTSHost; uint32_t flags = NS_UsePrivateBrowsing(this) ? nsISocketProvider::NO_PERMANENT_STORAGE : 0; - rv = sss->IsSecureURI(nsISiteSecurityService::HEADER_HSTS, mURI, flags, - &wasAlreadySTSHost); - // Failure here means STS is broken. Don't prevent the load, but this - // shouldn't fail. - NS_ENSURE_SUCCESS(rv, NS_OK); - MOZ_ASSERT(!(wasAlreadySTSHost && tlsIsBroken), - "connection should have been aborted by nss-bad-cert-handler"); - // Any STS header is ignored if the channel is not trusted due to - // certificate errors (STS Spec 7.1) -- there is nothing else to do, and - // the load may progress. - if (tlsIsBroken) { - LOG(("STS: Transport layer is not trustworthy, ignoring " - "STS headers and continuing load\n")); - return NS_OK; - } + // Get the SSLStatus + nsCOMPtr sslprov = do_QueryInterface(mSecurityInfo); + NS_ENSURE_TRUE(sslprov, NS_ERROR_FAILURE); + nsCOMPtr sslStatus; + rv = sslprov->GetSSLStatus(getter_AddRefs(sslStatus)); + NS_ENSURE_SUCCESS(rv, rv); + NS_ENSURE_TRUE(sslStatus, NS_ERROR_FAILURE); - // If there's a STS header, process it (STS Spec 7.1). At this point in - // processing, the channel is trusted, so the header should not be ignored. - const nsHttpAtom atom = nsHttp::ResolveAtom("Strict-Transport-Security"); - nsAutoCString stsHeader; - rv = mResponseHead->GetHeader(atom, stsHeader); - if (rv == NS_ERROR_NOT_AVAILABLE) { - LOG(("STS: No STS header, continuing load.\n")); - return NS_OK; - } - // All other failures are fatal. + rv = ProcessSingleSecurityHeader(nsISiteSecurityService::HEADER_HSTS, + sslStatus, flags); NS_ENSURE_SUCCESS(rv, rv); - rv = sss->ProcessHeader(nsISiteSecurityService::HEADER_HSTS, mURI, - stsHeader.get(), flags, nullptr, nullptr); - if (NS_FAILED(rv)) { - AddSecurityMessage(NS_LITERAL_STRING("InvalidSTSHeaders"), - NS_LITERAL_STRING("Invalid HSTS Headers")); - LOG(("STS: Failed to parse STS header, continuing load.\n")); - } + rv = ProcessSingleSecurityHeader(nsISiteSecurityService::HEADER_HPKP, + sslStatus, flags); + NS_ENSURE_SUCCESS(rv, rv); return NS_OK; } @@ -1229,8 +1254,9 @@ nsHttpChannel::ProcessResponse() // If proxy CONNECT response needs to complete, wait to process connection // for Strict-Transport-Security. } else { - // Given a successful connection, process any STS data that's relevant. - rv = ProcessSTSHeader(); + // Given a successful connection, process any STS or PKP data that's + // relevant. + rv = ProcessSecurityHeaders(); MOZ_ASSERT(NS_SUCCEEDED(rv), "ProcessSTSHeader failed, continuing load."); } diff --git a/netwerk/protocol/http/nsHttpChannel.h b/netwerk/protocol/http/nsHttpChannel.h index 1946001ff8e6..fc12b3020703 100644 --- a/netwerk/protocol/http/nsHttpChannel.h +++ b/netwerk/protocol/http/nsHttpChannel.h @@ -28,6 +28,7 @@ class nsDNSPrefetch; class nsICancelable; class nsIHttpChannelAuthProvider; class nsInputStreamPump; +class nsISSLStatus; class nsPerformance; namespace mozilla { namespace net { @@ -288,12 +289,21 @@ private: nsresult OpenRedirectChannel(nsresult rv); /** - * A function that takes care of reading STS headers and enforcing STS - * load rules. After a secure channel is erected, STS requires the channel - * to be trusted or any STS header data on the channel is ignored. - * This is called from ProcessResponse. + * A function that takes care of reading STS and PKP headers and enforcing + * STS and PKP load rules. After a secure channel is erected, STS and PKP + * requires the channel to be trusted or any STS or PKP header data on + * the channel is ignored. This is called from ProcessResponse. */ - nsresult ProcessSTSHeader(); + nsresult ProcessSecurityHeaders(); + + /** + * A function to process a single security header (STS or PKP), assumes + * some basic sanity checks have been applied to the channel. Called + * from ProcessSecurityHeaders. + */ + nsresult ProcessSingleSecurityHeader(uint32_t aType, + nsISSLStatus *aSSLStatus, + uint32_t aFlags); void InvalidateCacheEntryForLocation(const char *location); void AssembleCacheKey(const char *spec, uint32_t postID, nsACString &key); diff --git a/netwerk/test/TestSTSParser.cpp b/netwerk/test/TestSTSParser.cpp index d62f2344ab00..788cb01b5bc3 100644 --- a/netwerk/test/TestSTSParser.cpp +++ b/netwerk/test/TestSTSParser.cpp @@ -43,8 +43,8 @@ TestSuccess(const char* hdr, bool extraTokens, uint64_t maxAge = 0; bool includeSubdomains = false; - rv = sss->ProcessHeader(nsISiteSecurityService::HEADER_HSTS, dummyUri, hdr, - 0, &maxAge, &includeSubdomains); + rv = sss->UnsafeProcessHeader(nsISiteSecurityService::HEADER_HSTS, dummyUri, + hdr, 0, &maxAge, &includeSubdomains); EXPECT_SUCCESS(rv, "Failed to process valid header: %s", hdr); REQUIRE_EQUAL(maxAge, expectedMaxAge, "Did not correctly parse maxAge"); @@ -68,8 +68,8 @@ bool TestFailure(const char* hdr, nsresult rv = NS_NewURI(getter_AddRefs(dummyUri), "https://foo.com/bar.html"); EXPECT_SUCCESS(rv, "Failed to create URI"); - rv = sss->ProcessHeader(nsISiteSecurityService::HEADER_HSTS, dummyUri, hdr, - 0, nullptr, nullptr); + rv = sss->UnsafeProcessHeader(nsISiteSecurityService::HEADER_HSTS, dummyUri, + hdr, 0, nullptr, nullptr); EXPECT_FAILURE(rv, "Parsed invalid header: %s", hdr); passed(hdr); return true; diff --git a/security/certverifier/moz.build b/security/certverifier/moz.build index 78b9349b06d7..23c933602628 100644 --- a/security/certverifier/moz.build +++ b/security/certverifier/moz.build @@ -4,6 +4,11 @@ # 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/. +EXPORTS += [ + 'CertVerifier.h', + 'OCSPCache.h', +] + UNIFIED_SOURCES += [ 'CertVerifier.cpp', 'NSSCertDBTrustDomain.cpp', diff --git a/security/manager/boot/src/PublicKeyPinningService.cpp b/security/manager/boot/src/PublicKeyPinningService.cpp index 7fb0c8b997b1..f277a3e790c2 100644 --- a/security/manager/boot/src/PublicKeyPinningService.cpp +++ b/security/manager/boot/src/PublicKeyPinningService.cpp @@ -167,6 +167,13 @@ TransportSecurityPreloadCompare(const void *key, const void *entry) { return strcmp(keyStr, preloadEntry->mHost); } +bool +PublicKeyPinningService::ChainMatchesPinset(const CERTCertList* certList, + const nsTArray& aSHA256keys) +{ + return EvalChainWithHashType(certList, SEC_OID_SHA256, nullptr, &aSHA256keys); +} + /** * Check PKPins on the given certlist against the specified hostname */ diff --git a/security/manager/boot/src/PublicKeyPinningService.h b/security/manager/boot/src/PublicKeyPinningService.h index 664ca262976a..44391d24f2c9 100644 --- a/security/manager/boot/src/PublicKeyPinningService.h +++ b/security/manager/boot/src/PublicKeyPinningService.h @@ -6,6 +6,8 @@ #define PublicKeyPinningService_h #include "cert.h" +#include "nsString.h" +#include "nsTArray.h" #include "pkix/Time.h" namespace mozilla { @@ -29,6 +31,13 @@ public: const char* hostname, mozilla::pkix::Time time, bool enforceTestMode); + /** + * Returns true if there is any intersection between the certificate list + * and the pins specified in the aSHA256key array. Values passed in are + * assumed to be in base64 encoded form + */ + static bool ChainMatchesPinset(const CERTCertList* certList, + const nsTArray& aSHA256keys); }; }} // namespace mozilla::psm diff --git a/security/manager/boot/src/nsSiteSecurityService.cpp b/security/manager/boot/src/nsSiteSecurityService.cpp index 606cbeb6ef3c..575c913f1d51 100644 --- a/security/manager/boot/src/nsSiteSecurityService.cpp +++ b/security/manager/boot/src/nsSiteSecurityService.cpp @@ -8,15 +8,18 @@ #include "mozilla/Preferences.h" #include "mozilla/Base64.h" #include "base64.h" +#include "CertVerifier.h" #include "nsCRTGlue.h" #include "nsISSLStatus.h" #include "nsISSLStatusProvider.h" #include "nsISocketProvider.h" #include "nsIURI.h" #include "nsNetUtil.h" +#include "nsNSSComponent.h" #include "nsSecurityHeaderParser.h" #include "nsString.h" #include "nsThreadUtils.h" +#include "nsXULAppAPI.h" #include "pkix/pkixtypes.h" #include "plstr.h" #include "prlog.h" @@ -24,7 +27,7 @@ #include "prprf.h" #include "PublicKeyPinningService.h" #include "ScopedNSSTypes.h" -#include "nsXULAppAPI.h" +#include "SharedCertVerifier.h" // A note about the preload list: // When a site specifically disables HSTS by sending a header with @@ -242,6 +245,10 @@ nsSiteSecurityService::Init() "network.stricttransportsecurity.preloadlist", true); mozilla::Preferences::AddStrongObserver(this, "network.stricttransportsecurity.preloadlist"); + mProcessPKPHeadersFromNonBuiltInRoots = mozilla::Preferences::GetBool( + "security.cert_pinning.process_headers_from_non_builtin_roots", false); + mozilla::Preferences::AddStrongObserver(this, + "security.cert_pinning.process_headers_from_non_builtin_roots"); mPreloadListTimeOffset = mozilla::Preferences::GetInt( "test.currentTimeOffsetSeconds", 0); mozilla::Preferences::AddStrongObserver(this, @@ -293,6 +300,14 @@ SetStorageKey(nsAutoCString& storageKey, nsCString& hostname, uint32_t aType) } } +// Expire times are in millis. Since Headers max-age is in seconds, and +// PR_Now() is in micros, normalize the units at milliseconds. +static int64_t +ExpireTimeFromMaxAge(int64_t maxAge) +{ + return (PR_Now() / PR_USEC_PER_MSEC) + (maxAge * PR_MSEC_PER_SEC); +} + nsresult nsSiteSecurityService::SetHSTSState(uint32_t aType, nsIURI* aSourceURI, @@ -306,11 +321,7 @@ nsSiteSecurityService::SetHSTSState(uint32_t aType, return RemoveState(aType, aSourceURI, flags); } - // Expire time is millis from now. Since STS max-age is in seconds, and - // PR_Now() is in micros, must equalize the units at milliseconds. - int64_t expiretime = (PR_Now() / PR_USEC_PER_MSEC) + - (maxage * PR_MSEC_PER_SEC); - + int64_t expiretime = ExpireTimeFromMaxAge(maxage); SiteHSTSState siteState(expiretime, SecurityPropertySet, includeSubdomains); nsAutoCString stateString; siteState.ToString(stateString); @@ -335,7 +346,8 @@ nsSiteSecurityService::RemoveState(uint32_t aType, nsIURI* aURI, uint32_t aFlags) { // Only HSTS is supported at the moment. - NS_ENSURE_TRUE(aType == nsISiteSecurityService::HEADER_HSTS, + NS_ENSURE_TRUE(aType == nsISiteSecurityService::HEADER_HSTS || + aType == nsISiteSecurityService::HEADER_HPKP, NS_ERROR_NOT_IMPLEMENTED); nsAutoCString hostname; @@ -377,12 +389,44 @@ NS_IMETHODIMP nsSiteSecurityService::ProcessHeader(uint32_t aType, nsIURI* aSourceURI, const char* aHeader, + nsISSLStatus* aSSLStatus, uint32_t aFlags, - uint64_t *aMaxAge, - bool *aIncludeSubdomains) + uint64_t* aMaxAge, + bool* aIncludeSubdomains) { - // Only HSTS is supported at the moment. - NS_ENSURE_TRUE(aType == nsISiteSecurityService::HEADER_HSTS, + NS_ENSURE_TRUE(aType == nsISiteSecurityService::HEADER_HSTS || + aType == nsISiteSecurityService::HEADER_HPKP, + NS_ERROR_NOT_IMPLEMENTED); + + NS_ENSURE_ARG(aSSLStatus); + return ProcessHeaderInternal(aType, aSourceURI, aHeader, aSSLStatus, aFlags, + aMaxAge, aIncludeSubdomains); +} + +NS_IMETHODIMP +nsSiteSecurityService::UnsafeProcessHeader(uint32_t aType, + nsIURI* aSourceURI, + const char* aHeader, + uint32_t aFlags, + uint64_t* aMaxAge, + bool* aIncludeSubdomains) +{ + return ProcessHeaderInternal(aType, aSourceURI, aHeader, nullptr, aFlags, + aMaxAge, aIncludeSubdomains); +} + +nsresult +nsSiteSecurityService::ProcessHeaderInternal(uint32_t aType, + nsIURI* aSourceURI, + const char* aHeader, + nsISSLStatus* aSSLStatus, + uint32_t aFlags, + uint64_t* aMaxAge, + bool* aIncludeSubdomains) +{ + // Only HSTS and HPKP are supported at the moment. + NS_ENSURE_TRUE(aType == nsISiteSecurityService::HEADER_HSTS || + aType == nsISiteSecurityService::HEADER_HPKP, NS_ERROR_NOT_IMPLEMENTED); if (aMaxAge != nullptr) { @@ -393,6 +437,27 @@ nsSiteSecurityService::ProcessHeader(uint32_t aType, *aIncludeSubdomains = false; } + if (aSSLStatus) { + bool tlsIsBroken = false; + bool trustcheck; + nsresult rv; + rv = aSSLStatus->GetIsDomainMismatch(&trustcheck); + NS_ENSURE_SUCCESS(rv, rv); + tlsIsBroken = tlsIsBroken || trustcheck; + + rv = aSSLStatus->GetIsNotValidAtThisTime(&trustcheck); + NS_ENSURE_SUCCESS(rv, rv); + tlsIsBroken = tlsIsBroken || trustcheck; + + rv = aSSLStatus->GetIsUntrusted(&trustcheck); + NS_ENSURE_SUCCESS(rv, rv); + tlsIsBroken = tlsIsBroken || trustcheck; + if (tlsIsBroken) { + SSSLOG(("SSS: discarding header from untrustworthy connection")); + return NS_ERROR_FAILURE; + } + } + nsAutoCString host; nsresult rv = GetHost(aSourceURI, host); NS_ENSURE_SUCCESS(rv, rv); @@ -401,23 +466,32 @@ nsSiteSecurityService::ProcessHeader(uint32_t aType, return NS_OK; } - char * header = NS_strdup(aHeader); - if (!header) return NS_ERROR_OUT_OF_MEMORY; - rv = ProcessHeaderMutating(aType, aSourceURI, header, aFlags, - aMaxAge, aIncludeSubdomains); - NS_Free(header); + switch (aType) { + case nsISiteSecurityService::HEADER_HSTS: + rv = ProcessSTSHeader(aSourceURI, aHeader, aFlags, aMaxAge, + aIncludeSubdomains); + break; + case nsISiteSecurityService::HEADER_HPKP: + rv = ProcessPKPHeader(aSourceURI, aHeader, aSSLStatus, aFlags, aMaxAge, + aIncludeSubdomains); + break; + default: + MOZ_CRASH("unexpected header type"); + } return rv; } -nsresult -nsSiteSecurityService::ProcessHeaderMutating(uint32_t aType, - nsIURI* aSourceURI, - char* aHeader, - uint32_t aFlags, - uint64_t *aMaxAge, - bool *aIncludeSubdomains) +static nsresult +ParseSSSHeaders(uint32_t aType, + const char* aHeader, + bool& foundIncludeSubdomains, + bool& foundMaxAge, + bool& foundUnrecognizedDirective, + int64_t& maxAge, + nsTArray& sha256keys) { - SSSLOG(("SSS: processing header '%s'", aHeader)); + // Stric transport security and Public Key Pinning have very similar + // Header formats. // "Strict-Transport-Security" ":" OWS // STS-d *( OWS ";" OWS STS-d OWS) @@ -429,6 +503,26 @@ nsSiteSecurityService::ProcessHeaderMutating(uint32_t aType, // // includeSubDomains = [ "includeSubDomains" ] // + + // "Public-Key-Pins ":" OWS + // PKP-d *( OWS ";" OWS PKP-d OWS) + // + // ; PKP directive + // PKP-d = maxAge / includeSubDomains / reportUri / pin-directive + // + // maxAge = "max-age" "=" delta-seconds v-ext + // + // includeSubDomains = [ "includeSubDomains" ] + // + // reportURi = "report-uri" "=" quoted-string + // + // pin-directive = "pin-" token "=" quoted-string + // + // the only valid token currently specified is sha256 + // the quoted string for a pin directive is the base64 encoding + // of the hash of the public key of the fingerprint + // + // The order of the directives is not significant. // All directives must appear only once. // Directive names are case-insensitive. @@ -437,14 +531,12 @@ nsSiteSecurityService::ProcessHeaderMutating(uint32_t aType, // Unrecognized directives (that are otherwise syntactically valid) are // ignored, and the rest of the header is parsed as normal. - bool foundMaxAge = false; - bool foundIncludeSubdomains = false; - bool foundUnrecognizedDirective = false; - int64_t maxAge = 0; + bool foundReportURI = false; NS_NAMED_LITERAL_CSTRING(max_age_var, "max-age"); NS_NAMED_LITERAL_CSTRING(include_subd_var, "includesubdomains"); - + NS_NAMED_LITERAL_CSTRING(pin_sha256_var, "pin-sha256"); + NS_NAMED_LITERAL_CSTRING(report_uri_var, "report-uri"); nsSecurityHeaderParser parser(aHeader); nsresult rv = parser.Parse(); @@ -452,10 +544,11 @@ nsSiteSecurityService::ProcessHeaderMutating(uint32_t aType, SSSLOG(("SSS: could not parse header")); return rv; } - mozilla::LinkedList *directives = parser.GetDirectives(); + mozilla::LinkedList* directives = parser.GetDirectives(); - for (nsSecurityHeaderDirective *directive = directives->getFirst(); + for (nsSecurityHeaderDirective* directive = directives->getFirst(); directive != nullptr; directive = directive->getNext()) { + SSSLOG(("SSS: found directive %s\n", directive->mName.get())); if (directive->mName.Length() == max_age_var.Length() && directive->mName.EqualsIgnoreCase(max_age_var.get(), max_age_var.Length())) { @@ -498,12 +591,178 @@ nsSiteSecurityService::ProcessHeaderMutating(uint32_t aType, directive->mValue.get())); return NS_ERROR_FAILURE; } - } else { + } else if (aType == nsISiteSecurityService::HEADER_HPKP && + directive->mName.Length() == pin_sha256_var.Length() && + directive->mName.EqualsIgnoreCase(pin_sha256_var.get(), + pin_sha256_var.Length())) { + SSSLOG(("SSS: found pinning entry '%s' length=%d", + directive->mValue.get(), directive->mValue.Length())); + if (!stringIsBase64EncodingOf256bitValue(directive->mValue)) { + return NS_ERROR_FAILURE; + } + sha256keys.AppendElement(directive->mValue); + } else if (aType == nsISiteSecurityService::HEADER_HPKP && + directive->mName.Length() == report_uri_var.Length() && + directive->mName.EqualsIgnoreCase(report_uri_var.get(), + report_uri_var.Length())) { + // We doni't support the report-uri yet, but to avoid unrecognized + // directive warnings, we still have to handle its presence + if (foundReportURI) { + SSSLOG(("SSS: found two report-uri directives")); + return NS_ERROR_FAILURE; + } + SSSLOG(("SSS: found report-uri directive")); + foundReportURI = true; + } else { SSSLOG(("SSS: ignoring unrecognized directive '%s'", directive->mName.get())); foundUnrecognizedDirective = true; } } + return NS_OK; +} + +nsresult +nsSiteSecurityService::ProcessPKPHeader(nsIURI* aSourceURI, + const char* aHeader, + nsISSLStatus* aSSLStatus, + uint32_t aFlags, + uint64_t* aMaxAge, + bool* aIncludeSubdomains) +{ + SSSLOG(("SSS: processing HPKP header '%s'", aHeader)); + NS_ENSURE_ARG(aSSLStatus); + + const uint32_t aType = nsISiteSecurityService::HEADER_HPKP; + bool foundMaxAge = false; + bool foundIncludeSubdomains = false; + bool foundUnrecognizedDirective = false; + int64_t maxAge = 0; + nsTArray sha256keys; + nsresult rv = ParseSSSHeaders(aType, aHeader, foundIncludeSubdomains, + foundMaxAge, foundUnrecognizedDirective, + maxAge, sha256keys); + NS_ENSURE_SUCCESS(rv, rv); + + // after processing all the directives, make sure we came across max-age + // somewhere. + if (!foundMaxAge) { + SSSLOG(("SSS: did not encounter required max-age directive")); + return NS_ERROR_FAILURE; + } + + // before we add the pin we need to ensure it will not break the site as + // currently visited so: + // 1. recompute a valid chain (no external ocsp) + // 2. use this chain to check if things would have broken! + nsAutoCString host; + rv = GetHost(aSourceURI, host); + NS_ENSURE_SUCCESS(rv, rv); + nsCOMPtr cert; + rv = aSSLStatus->GetServerCert(getter_AddRefs(cert)); + NS_ENSURE_SUCCESS(rv, rv); + NS_ENSURE_TRUE(cert, NS_ERROR_FAILURE); + ScopedCERTCertificate nssCert(cert->GetCert()); + NS_ENSURE_TRUE(nssCert, NS_ERROR_FAILURE); + + mozilla::pkix::Time now(mozilla::pkix::Now()); + ScopedCERTCertList certList; + RefPtr certVerifier(GetDefaultCertVerifier()); + NS_ENSURE_TRUE(certVerifier, NS_ERROR_UNEXPECTED); + if (certVerifier->VerifySSLServerCert(nssCert, nullptr, // stapled ocsp + now, nullptr, // pinarg + host.get(), // hostname + false, // don't store intermediates + CertVerifier::FLAG_LOCAL_ONLY, + &certList) != SECSuccess) { + return NS_ERROR_FAILURE; + } + + CERTCertListNode* rootNode = CERT_LIST_TAIL(certList); + if (CERT_LIST_END(rootNode, certList)) { + return NS_ERROR_FAILURE; + } + bool isBuiltIn = false; + SECStatus srv = IsCertBuiltInRoot(rootNode->cert, isBuiltIn); + if (srv != SECSuccess) { + return NS_ERROR_FAILURE; + } + + if (!isBuiltIn && !mProcessPKPHeadersFromNonBuiltInRoots) { + return NS_OK; + } + + // if maxAge == 0 we must delete all state, for now no hole-punching + if (maxAge == 0) { + return RemoveState(aType, aSourceURI, aFlags); + } + + if (!PublicKeyPinningService::ChainMatchesPinset(certList, sha256keys)) { + // is invalid + SSSLOG(("SSS: Pins provided by %s are invalid no match with certList\n", host.get())); + return NS_ERROR_FAILURE; + } + + // finally we need to ensure that there is a "backup pin" ie. There must be + // at least one fingerprint hash that does NOT valiate against the verified + // chain (Section 2.5 of the spec) + bool hasBackupPin = false; + for (uint32_t i = 0; i < sha256keys.Length(); i++) { + nsTArray singlePin; + singlePin.AppendElement(sha256keys[i]); + if (!PublicKeyPinningService:: + ChainMatchesPinset(certList, singlePin)) { + hasBackupPin = true; + } + } + if (!hasBackupPin) { + // is invalid + SSSLOG(("SSS: Pins provided by %s are invalid no backupPin\n", host.get())); + return NS_ERROR_FAILURE; + } + + int64_t expireTime = ExpireTimeFromMaxAge(maxAge); + SiteHPKPState dynamicEntry(expireTime, SecurityPropertySet, + foundIncludeSubdomains, sha256keys); + SSSLOG(("SSS: about to set pins for %s, expires=%ld now=%ld maxAge=%ld\n", + host.get(), expireTime, PR_Now() / PR_USEC_PER_MSEC, maxAge)); + + rv = SetHPKPState(host.get(), dynamicEntry, aFlags); + NS_ENSURE_SUCCESS(rv, rv); + + if (aMaxAge != nullptr) { + *aMaxAge = (uint64_t)maxAge; + } + + if (aIncludeSubdomains != nullptr) { + *aIncludeSubdomains = foundIncludeSubdomains; + } + + return foundUnrecognizedDirective + ? NS_SUCCESS_LOSS_OF_INSIGNIFICANT_DATA + : NS_OK; +} + +nsresult +nsSiteSecurityService::ProcessSTSHeader(nsIURI* aSourceURI, + const char* aHeader, + uint32_t aFlags, + uint64_t* aMaxAge, + bool* aIncludeSubdomains) +{ + SSSLOG(("SSS: processing HSTS header '%s'", aHeader)); + + const uint32_t aType = nsISiteSecurityService::HEADER_HSTS; + bool foundMaxAge = false; + bool foundIncludeSubdomains = false; + bool foundUnrecognizedDirective = false; + int64_t maxAge = 0; + nsTArray unusedSHA256keys; // Requred for sane internal interface + + nsresult rv = ParseSSSHeaders(aType, aHeader, foundIncludeSubdomains, + foundMaxAge, foundUnrecognizedDirective, + maxAge, unusedSHA256keys); + NS_ENSURE_SUCCESS(rv, rv); // after processing all the directives, make sure we came across max-age // somewhere. @@ -523,8 +782,9 @@ nsSiteSecurityService::ProcessHeaderMutating(uint32_t aType, *aIncludeSubdomains = foundIncludeSubdomains; } - return foundUnrecognizedDirective ? NS_SUCCESS_LOSS_OF_INSIGNIFICANT_DATA - : NS_OK; + return foundUnrecognizedDirective + ? NS_SUCCESS_LOSS_OF_INSIGNIFICANT_DATA + : NS_OK; } NS_IMETHODIMP @@ -765,12 +1025,20 @@ nsSiteSecurityService::GetKeyPinsForHostname(const char* aHostname, SSSLOG(("storagekey '%s'\n", storageKey.get())); mozilla::DataStorageType storageType = mozilla::DataStorage_Persistent; nsCString value = mSiteStateStorage->Get(storageKey, storageType); - //decode now + // decode now SiteHPKPState foundEntry(value); if (foundEntry.mState != SecurityPropertySet || foundEntry.IsExpired(aEvalTime) || foundEntry.mSHA256keys.Length() < 1 ) { - return NS_OK; + // not in permanent storage, try now private + value = mSiteStateStorage->Get(storageKey, mozilla::DataStorage_Private); + SiteHPKPState privateEntry(value); + if (privateEntry.mState != SecurityPropertySet || + privateEntry.IsExpired(aEvalTime) || + privateEntry.mSHA256keys.Length() < 1 ) { + return NS_OK; + } + foundEntry = privateEntry; } pinArray = foundEntry.mSHA256keys; *aIncludeSubdomains = foundEntry.mIncludeSubdomains; @@ -782,7 +1050,7 @@ NS_IMETHODIMP nsSiteSecurityService::SetKeyPins(const char* aHost, bool aIncludeSubdomains, uint32_t aMaxAge, uint32_t aPinCount, const char** aSha256Pins, - /*out*/bool* aResult) + /*out*/ bool* aResult) { NS_ENSURE_ARG_POINTER(aHost); NS_ENSURE_ARG_POINTER(aResult); @@ -790,10 +1058,7 @@ nsSiteSecurityService::SetKeyPins(const char* aHost, bool aIncludeSubdomains, SSSLOG(("Top of SetPins")); - // Expire time is millis from now. Since HPKP max-age is in seconds, and - // PR_Now() is in micros, must normalize the units at milliseconds. - int64_t expireTime = (PR_Now() / PR_USEC_PER_MSEC) + - (aMaxAge * PR_MSEC_PER_SEC); + int64_t expireTime = ExpireTimeFromMaxAge(aMaxAge); nsTArray sha256keys; for (unsigned int i = 0; i < aPinCount; i++) { nsAutoCString pin(aSha256Pins[i]); @@ -805,14 +1070,24 @@ nsSiteSecurityService::SetKeyPins(const char* aHost, bool aIncludeSubdomains, } SiteHPKPState dynamicEntry(expireTime, SecurityPropertySet, aIncludeSubdomains, sha256keys); + // we always store data in permanent storage (ie no flags) + return SetHPKPState(aHost, dynamicEntry, 0); +} +nsresult +nsSiteSecurityService::SetHPKPState(const char* aHost, SiteHPKPState& entry, + uint32_t aFlags) +{ + SSSLOG(("Top of SetPKPState")); nsAutoCString host(aHost); nsAutoCString storageKey; SetStorageKey(storageKey, host, nsISiteSecurityService::HEADER_HPKP); - // Note: setPins always stores data in the persistent storage - mozilla::DataStorageType storageType = mozilla::DataStorage_Persistent; + bool isPrivate = aFlags & nsISocketProvider::NO_PERMANENT_STORAGE; + mozilla::DataStorageType storageType = isPrivate + ? mozilla::DataStorage_Private + : mozilla::DataStorage_Persistent; nsAutoCString stateString; - dynamicEntry.ToString(stateString); + entry.ToString(stateString); nsresult rv = mSiteStateStorage->Put(storageKey, stateString, storageType); NS_ENSURE_SUCCESS(rv, rv); return NS_OK; @@ -838,6 +1113,8 @@ nsSiteSecurityService::Observe(nsISupports *subject, "network.stricttransportsecurity.preloadlist", true); mPreloadListTimeOffset = mozilla::Preferences::GetInt("test.currentTimeOffsetSeconds", 0); + mProcessPKPHeadersFromNonBuiltInRoots = mozilla::Preferences::GetBool( + "security.cert_pinning.process_headers_from_non_builtin_roots", false); } return NS_OK; diff --git a/security/manager/boot/src/nsSiteSecurityService.h b/security/manager/boot/src/nsSiteSecurityService.h index 69f5ed24eff7..0221e6850aa8 100644 --- a/security/manager/boot/src/nsSiteSecurityService.h +++ b/security/manager/boot/src/nsSiteSecurityService.h @@ -15,6 +15,7 @@ #include "prtime.h" class nsIURI; +class nsISSLStatus; // {16955eee-6c48-4152-9309-c42a465138a1} #define NS_SITE_SECURITY_SERVICE_CID \ @@ -128,13 +129,23 @@ private: nsresult GetHost(nsIURI *aURI, nsACString &aResult); nsresult SetHSTSState(uint32_t aType, nsIURI* aSourceURI, int64_t maxage, bool includeSubdomains, uint32_t flags); - nsresult ProcessHeaderMutating(uint32_t aType, nsIURI* aSourceURI, - char* aHeader, uint32_t flags, - uint64_t *aMaxAge, bool *aIncludeSubdomains); + nsresult ProcessHeaderInternal(uint32_t aType, nsIURI* aSourceURI, + const char* aHeader, nsISSLStatus* aSSLStatus, + uint32_t aFlags, uint64_t* aMaxAge, + bool* aIncludeSubdomains); + nsresult ProcessSTSHeader(nsIURI* aSourceURI, const char* aHeader, + uint32_t flags, uint64_t* aMaxAge, + bool* aIncludeSubdomains); + nsresult ProcessPKPHeader(nsIURI* aSourceURI, const char* aHeader, + nsISSLStatus* aSSLStatus, uint32_t flags, + uint64_t* aMaxAge, bool* aIncludeSubdomains); + nsresult SetHPKPState(const char* aHost, SiteHPKPState& entry, uint32_t flags); + const nsSTSPreload *GetPreloadListEntry(const char *aHost); bool mUsePreloadList; int64_t mPreloadListTimeOffset; + bool mProcessPKPHeadersFromNonBuiltInRoots; nsRefPtr mSiteStateStorage; }; diff --git a/security/manager/ssl/src/moz.build b/security/manager/ssl/src/moz.build index eebafbe3d37b..cd8371d08fb2 100644 --- a/security/manager/ssl/src/moz.build +++ b/security/manager/ssl/src/moz.build @@ -6,11 +6,17 @@ EXPORTS += [ 'CryptoTask.h', + 'nsClientAuthRemember.h', 'nsCrypto.h', + 'nsNSSCallbacks.h', + 'nsNSSCertificate.h', + 'nsNSSComponent.h', + 'nsNSSHelper.h', 'nsNSSShutDown.h', 'nsRandomGenerator.h', 'NSSErrorsService.h', 'ScopedNSSTypes.h', + 'SharedCertVerifier.h', ] EXPORTS.mozilla += [ diff --git a/security/manager/ssl/tests/mochitest/browser/browser_bug627234_perwindowpb.js b/security/manager/ssl/tests/mochitest/browser/browser_bug627234_perwindowpb.js index 2a635e0b0936..c4b8a80855cc 100644 --- a/security/manager/ssl/tests/mochitest/browser/browser_bug627234_perwindowpb.js +++ b/security/manager/ssl/tests/mochitest/browser/browser_bug627234_perwindowpb.js @@ -2,6 +2,29 @@ * 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/. */ +let FakeSSLStatus = function() { +}; + +FakeSSLStatus.prototype = { + serverCert: null, + cipherName: null, + keyLength: 2048, + isDomainMismatch: false, + isNotValidAtThisTime: false, + isUntrusted: false, + isExtendedValidation: false, + getInterface: function(aIID) { + return this.QueryInterface(aIID); + }, + QueryInterface: function(aIID) { + if (aIID.equals(Ci.nsISSLStatus) || + aIID.equals(Ci.nsISupports)) { + return this; + } + throw Components.results.NS_ERROR_NO_INTERFACE; + }, +} + // This is a template to help porting global private browsing tests // to per-window private browsing tests function test() { @@ -20,10 +43,10 @@ function test() { function doTest(aIsPrivateMode, aWindow, aCallback) { aWindow.gBrowser.selectedBrowser.addEventListener("load", function onLoad() { aWindow.gBrowser.selectedBrowser.removeEventListener("load", onLoad, true); - + let sslStatus = new FakeSSLStatus(); uri = aWindow.Services.io.newURI("https://localhost/img.png", null, null); gSSService.processHeader(Ci.nsISiteSecurityService.HEADER_HSTS, uri, - "max-age=1000", privacyFlags(aIsPrivateMode)); + "max-age=1000", sslStatus, privacyFlags(aIsPrivateMode)); ok(gSSService.isSecureHost(Ci.nsISiteSecurityService.HEADER_HSTS, "localhost", privacyFlags(aIsPrivateMode)), "checking sts host"); aCallback(); diff --git a/security/manager/ssl/tests/unit/head_psm.js b/security/manager/ssl/tests/unit/head_psm.js index 7305dc9d8a91..e02a7f639a64 100644 --- a/security/manager/ssl/tests/unit/head_psm.js +++ b/security/manager/ssl/tests/unit/head_psm.js @@ -518,3 +518,27 @@ function startOCSPResponder(serverPort, identity, invalidIdentities, } }; } + +// A prototype for a fake, error-free sslstatus +let FakeSSLStatus = function() { +}; + +FakeSSLStatus.prototype = { + serverCert: null, + cipherName: null, + keyLength: 2048, + isDomainMismatch: false, + isNotValidAtThisTime: false, + isUntrusted: false, + isExtendedValidation: false, + getInterface: function(aIID) { + return this.QueryInterface(aIID); + }, + QueryInterface: function(aIID) { + if (aIID.equals(Ci.nsISSLStatus) || + aIID.equals(Ci.nsISupports)) { + return this; + } + throw Components.results.NS_ERROR_NO_INTERFACE; + }, +} diff --git a/security/manager/ssl/tests/unit/test_ocsp_no_hsts_upgrade.js b/security/manager/ssl/tests/unit/test_ocsp_no_hsts_upgrade.js index 74eae1cc274a..069587710fc2 100644 --- a/security/manager/ssl/tests/unit/test_ocsp_no_hsts_upgrade.js +++ b/security/manager/ssl/tests/unit/test_ocsp_no_hsts_upgrade.js @@ -42,8 +42,9 @@ function run_test() { let SSService = Cc["@mozilla.org/ssservice;1"] .getService(Ci.nsISiteSecurityService); let uri = Services.io.newURI("http://localhost", null, null); + let sslStatus = new FakeSSLStatus(); SSService.processHeader(Ci.nsISiteSecurityService.HEADER_HSTS, uri, - "max-age=10000", 0); + "max-age=10000", sslStatus, 0); do_check_true(SSService.isSecureHost(Ci.nsISiteSecurityService.HEADER_HSTS, "localhost", 0)); diff --git a/security/manager/ssl/tests/unit/test_sss_eviction.js b/security/manager/ssl/tests/unit/test_sss_eviction.js index 16943bf7e653..7b1718be8075 100644 --- a/security/manager/ssl/tests/unit/test_sss_eviction.js +++ b/security/manager/ssl/tests/unit/test_sss_eviction.js @@ -44,10 +44,11 @@ function do_state_read(aSubject, aTopic, aData) { do_check_true(gSSService.isSecureHost(Ci.nsISiteSecurityService.HEADER_HSTS, "frequentlyused.example.com", 0)); + let sslStatus = new FakeSSLStatus(); for (let i = 0; i < 2000; i++) { let uri = Services.io.newURI("http://bad" + i + ".example.com", null, null); gSSService.processHeader(Ci.nsISiteSecurityService.HEADER_HSTS, uri, - "max-age=1000", 0); + "max-age=1000", sslStatus, 0); } do_test_pending(); Services.obs.addObserver(do_state_written, "data-storage-written", false); diff --git a/security/manager/ssl/tests/unit/test_sss_savestate.js b/security/manager/ssl/tests/unit/test_sss_savestate.js index 067e25779e7b..048380acc8b7 100644 --- a/security/manager/ssl/tests/unit/test_sss_savestate.js +++ b/security/manager/ssl/tests/unit/test_sss_savestate.js @@ -111,8 +111,10 @@ function run_test() { let maxAge = "max-age=" + (i * 1000); // alternate setting includeSubdomains let includeSubdomains = (i % 2 == 0 ? "; includeSubdomains" : ""); + let sslStatus = new FakeSSLStatus(); SSService.processHeader(Ci.nsISiteSecurityService.HEADER_HSTS, - uris[uriIndex], maxAge + includeSubdomains, 0); + uris[uriIndex], maxAge + includeSubdomains, + sslStatus, 0); } do_test_pending(); diff --git a/security/manager/ssl/tests/unit/test_sts_ipv4_ipv6.js b/security/manager/ssl/tests/unit/test_sts_ipv4_ipv6.js index ce9c15f574ee..9a0ed6f79f93 100644 --- a/security/manager/ssl/tests/unit/test_sts_ipv4_ipv6.js +++ b/security/manager/ssl/tests/unit/test_sts_ipv4_ipv6.js @@ -1,4 +1,5 @@ function check_ip(s, v, ip) { + let sslStatus = new FakeSSLStatus(); do_check_false(s.isSecureHost(Ci.nsISiteSecurityService.HEADER_HSTS, ip, 0)); let str = "https://"; @@ -16,7 +17,7 @@ function check_ip(s, v, ip) { let parsedMaxAge = {}; let parsedIncludeSubdomains = {}; s.processHeader(Ci.nsISiteSecurityService.HEADER_HSTS, uri, - "max-age=1000;includeSubdomains", 0, + "max-age=1000;includeSubdomains", sslStatus , 0, parsedMaxAge, parsedIncludeSubdomains); /* Test that processHeader will ignore headers for an uri, if the uri diff --git a/security/manager/ssl/tests/unit/test_sts_preloadlist_perwindowpb.js b/security/manager/ssl/tests/unit/test_sts_preloadlist_perwindowpb.js index f021e8f55ffe..30444a78ca0d 100644 --- a/security/manager/ssl/tests/unit/test_sts_preloadlist_perwindowpb.js +++ b/security/manager/ssl/tests/unit/test_sts_preloadlist_perwindowpb.js @@ -16,6 +16,7 @@ Observer.prototype = { }; var gObserver = new Observer(); +var sslStatus = new FakeSSLStatus(); function cleanup() { Services.obs.removeObserver(gObserver, "last-pb-context-exited"); @@ -70,7 +71,7 @@ function test_part1() { // site from the list var uri = Services.io.newURI("http://bugzilla.mozilla.org", null, null); gSSService.processHeader(Ci.nsISiteSecurityService.HEADER_HSTS, uri, - "max-age=0", 0); + "max-age=0", sslStatus, 0); do_check_false(gSSService.isSecureHost(Ci.nsISiteSecurityService.HEADER_HSTS, "bugzilla.mozilla.org", 0)); do_check_false(gSSService.isSecureHost(Ci.nsISiteSecurityService.HEADER_HSTS, @@ -78,7 +79,7 @@ function test_part1() { // check that processing another header (with max-age non-zero) will // re-enable a site's sts status gSSService.processHeader(Ci.nsISiteSecurityService.HEADER_HSTS, uri, - "max-age=1000", 0); + "max-age=1000", sslStatus, 0); do_check_true(gSSService.isSecureHost(Ci.nsISiteSecurityService.HEADER_HSTS, "bugzilla.mozilla.org", 0)); // but this time include subdomains was not set, so test for that @@ -90,7 +91,7 @@ function test_part1() { // will not remove that (ancestor) site from the list var uri = Services.io.newURI("http://subdomain.www.torproject.org", null, null); gSSService.processHeader(Ci.nsISiteSecurityService.HEADER_HSTS, uri, - "max-age=0", 0); + "max-age=0", sslStatus, 0); do_check_true(gSSService.isSecureHost(Ci.nsISiteSecurityService.HEADER_HSTS, "www.torproject.org", 0)); do_check_false(gSSService.isSecureHost(Ci.nsISiteSecurityService.HEADER_HSTS, @@ -98,7 +99,7 @@ function test_part1() { var uri = Services.io.newURI("http://subdomain.bugzilla.mozilla.org", null, null); gSSService.processHeader(Ci.nsISiteSecurityService.HEADER_HSTS, uri, - "max-age=0", 0); + "max-age=0", sslStatus, 0); // we received a header with "max-age=0", so we have "no information" // regarding the sts state of subdomain.bugzilla.mozilla.org specifically, // but it is actually still an STS host, because of the preloaded @@ -118,7 +119,7 @@ function test_part1() { "another.subdomain.bugzilla.mozilla.org", 0)); gSSService.processHeader(Ci.nsISiteSecurityService.HEADER_HSTS, uri, - "max-age=1000", 0); + "max-age=1000", sslStatus, 0); // Here's what we have now: // |-- bugzilla.mozilla.org (in preload list, includes subdomains) IS sts host // |-- subdomain.bugzilla.mozilla.org (include subdomains is false) IS sts host @@ -141,7 +142,7 @@ function test_part1() { "login.persona.org", 0)); var uri = Services.io.newURI("http://login.persona.org", null, null); gSSService.processHeader(Ci.nsISiteSecurityService.HEADER_HSTS, uri, - "max-age=1", 0); + "max-age=1", sslStatus, 0); do_timeout(1250, function() { do_check_false(gSSService.isSecureHost(Ci.nsISiteSecurityService.HEADER_HSTS, "login.persona.org", 0)); @@ -161,7 +162,7 @@ function test_private_browsing1() { var uri = Services.io.newURI("http://bugzilla.mozilla.org", null, null); gSSService.processHeader(Ci.nsISiteSecurityService.HEADER_HSTS, uri, - "max-age=0", IS_PRIVATE); + "max-age=0", sslStatus, IS_PRIVATE); do_check_false(gSSService.isSecureHost(Ci.nsISiteSecurityService.HEADER_HSTS, "bugzilla.mozilla.org", IS_PRIVATE)); do_check_false(gSSService.isSecureHost(Ci.nsISiteSecurityService.HEADER_HSTS, @@ -169,7 +170,7 @@ function test_private_browsing1() { // check adding it back in gSSService.processHeader(Ci.nsISiteSecurityService.HEADER_HSTS, uri, - "max-age=1000", IS_PRIVATE); + "max-age=1000", sslStatus, IS_PRIVATE); do_check_true(gSSService.isSecureHost(Ci.nsISiteSecurityService.HEADER_HSTS, "bugzilla.mozilla.org", IS_PRIVATE)); // but no includeSubdomains this time @@ -178,7 +179,7 @@ function test_private_browsing1() { // do the hokey-pokey... gSSService.processHeader(Ci.nsISiteSecurityService.HEADER_HSTS, uri, - "max-age=0", IS_PRIVATE); + "max-age=0", sslStatus, IS_PRIVATE); do_check_false(gSSService.isSecureHost(Ci.nsISiteSecurityService.HEADER_HSTS, "bugzilla.mozilla.org", IS_PRIVATE)); do_check_false(gSSService.isSecureHost(Ci.nsISiteSecurityService.HEADER_HSTS, @@ -194,7 +195,7 @@ function test_private_browsing1() { "login.persona.org", IS_PRIVATE)); var uri = Services.io.newURI("http://login.persona.org", null, null); gSSService.processHeader(Ci.nsISiteSecurityService.HEADER_HSTS, uri, - "max-age=1", IS_PRIVATE); + "max-age=1", sslStatus, IS_PRIVATE); do_timeout(1250, function() { do_check_false(gSSService.isSecureHost(Ci.nsISiteSecurityService.HEADER_HSTS, "login.persona.org", IS_PRIVATE)); From 886005b84a22ad09a9e120a848a8116d10b986be Mon Sep 17 00:00:00 2001 From: Camilo Viecco Date: Mon, 29 Sep 2014 20:31:08 -0700 Subject: [PATCH 61/82] Bug 787133 - (hpkp) Part 2/2. Tests r=keeler --- browser/base/content/test/general/browser.ini | 4 +- .../content/test/general/browser_blockHPKP.js | 115 ++++++++++++++++ .../content/test/general/pinning_headers.sjs | 23 ++++ build/pgo/certs/alternateroot.ca | 18 +++ build/pgo/certs/cert8.db | Bin 65536 -> 65536 bytes build/pgo/certs/key3.db | Bin 98304 -> 98304 bytes build/pgo/server-locations.txt | 5 + security/manager/ssl/tests/unit/head_psm.js | 3 +- .../tests/unit/test_pinning_header_parsing.js | 129 ++++++++++++++++++ security/manager/ssl/tests/unit/xpcshell.ini | 1 + 10 files changed, 296 insertions(+), 2 deletions(-) create mode 100644 browser/base/content/test/general/browser_blockHPKP.js create mode 100644 browser/base/content/test/general/pinning_headers.sjs create mode 100644 build/pgo/certs/alternateroot.ca create mode 100644 security/manager/ssl/tests/unit/test_pinning_header_parsing.js diff --git a/browser/base/content/test/general/browser.ini b/browser/base/content/test/general/browser.ini index 75269e1812ca..5d4f7e7a8267 100644 --- a/browser/base/content/test/general/browser.ini +++ b/browser/base/content/test/general/browser.ini @@ -65,6 +65,7 @@ support-files = offlineQuotaNotification.html page_style_sample.html parsingTestHelpers.jsm + pinning_headers.sjs popup_blocker.html print_postdata.sjs redirect_bug623155.sjs @@ -493,4 +494,5 @@ skip-if = e10s # Bug ?????? - test directly manipulates content (content.documen [browser_bug1045809.js] skip-if = e10s # Bug 1068360 - [e10s] Mixed content blocker doorhanger doesn't work [browser_e10s_switchbrowser.js] - +[browser_blockHPKP.js] +skip-if = e10s # bug ?????? - test directly manipulates content (content.document.getElementById) diff --git a/browser/base/content/test/general/browser_blockHPKP.js b/browser/base/content/test/general/browser_blockHPKP.js new file mode 100644 index 000000000000..a24bac12b52a --- /dev/null +++ b/browser/base/content/test/general/browser_blockHPKP.js @@ -0,0 +1,115 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=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/. */ + +// Test that visiting a site pinned with HPKP headers does not succeed when it +// uses a certificate with a key not in the pinset. This should result in an +// about:neterror page +// Also verify that removal of the HPKP headers succeeds (via HPKP headers) +// and that after removal the visit to the site with the previously +// unauthorized pins succeeds. +// +// This test required three certs to be created in build/pgo/certs: +// 1. A new trusted root: +// a. certutil -S -s "Alternate trusted authority" -s "CN=Alternate Trusted Authority" -t "C,," -x -m 1 -v 120 -n "alternateTrustedAuthority" -Z SHA256 -g 2048 -2 -d . +// b. (export) certutil -L -d . -n "alternateTrustedAuthority" -a -o alternateroot.ca +// (files ended in .ca are added as trusted roots by the mochitest harness) +// 2. A good pinning server cert (signed by the pgo root): +// certutil -S -n "dynamicPinningGood" -s "CN=dynamic-pinning.example.com" -c "pgo temporary ca" -t "P,," -k rsa -g 2048 -Z SHA256 -m 8939454 -v 120 -8 "*.include-subdomains.pinning-dynamic.example.com,*.pinning-dynamic.example.com" -d . +// 3. A certificate with a different issuer, so as to cause a key pinning violation." +// certutil -S -n "dynamicPinningBad" -s "CN=bad.include-subdomains.pinning-dynamic.example.com" -c "alternateTrustedAuthority" -t "P,," -k rsa -g 2048 -Z SHA256 -m 893945439 -v 120 -8 "bad.include-subdomains.pinning-dynamic.example.com" -d . + +const gSSService = Cc["@mozilla.org/ssservice;1"] + .getService(Ci.nsISiteSecurityService); +const gIOService = Cc["@mozilla.org/network/io-service;1"] + .getService(Ci.nsIIOService); + +const kPinningDomain = "include-subdomains.pinning-dynamic.example.com"; +const khpkpPinninEnablePref = "security.cert_pinning.process_headers_from_non_builtin_roots"; +const kpkpEnforcementPref = "security.cert_pinning.enforcement_level"; +const kBadPinningDomain = "bad.include-subdomains.pinning-dynamic.example.com"; +const kURLPath = "/browser/browser/base/content/test/general/pinning_headers.sjs?"; + +function test() { + waitForExplicitFinish(); + // Enable enforcing strict pinning and processing headers from + // non-builtin roots. + Services.prefs.setIntPref(kpkpEnforcementPref, 2); + Services.prefs.setBoolPref(khpkpPinninEnablePref, true); + registerCleanupFunction(function () { + Services.prefs.clearUserPref(kpkpEnforcementPref); + Services.prefs.clearUserPref(khpkpPinninEnablePref); + let uri = gIOService.newURI("https://" + kPinningDomain, null, null); + gSSService.removeState(Ci.nsISiteSecurityService.HEADER_HPKP, uri, 0); + }); + whenNewTabLoaded(window, loadPinningPage); +} + +// Start by making a successful connection to a domain that will pin a site +function loadPinningPage() { + gBrowser.selectedBrowser.addEventListener("load", + successfulPinningPageListener, + true); + + gBrowser.selectedBrowser.loadURI("https://" + kPinningDomain + kURLPath + "valid"); +} + +// After the site is pinned try to load with a subdomain site that should +// fail to validate +let successfulPinningPageListener = { + handleEvent: function() { + gBrowser.selectedBrowser.removeEventListener("load", this, true); + gBrowser.addProgressListener(certErrorProgressListener); + gBrowser.selectedBrowser.loadURI("https://" + kBadPinningDomain); + } +}; + +// The browser should load about:neterror, when this happens, proceed +// to load the pinning domain again, this time removing the pinning information +let certErrorProgressListener = { + buttonClicked: false, + onStateChange: function(aWebProgress, aRequest, aStateFlags, aStatus) { + if (aStateFlags & Ci.nsIWebProgressListener.STATE_STOP) { + let self = this; + // Can't directly call button.click() in onStateChange + executeSoon(function() { + let button = content.document.getElementById("errorTryAgain"); + // If about:neterror hasn't fully loaded, the button won't be present. + // It will eventually be there, however. + if (button && !self.buttonClicked) { + gBrowser.removeProgressListener(self); + gBrowser.selectedBrowser.addEventListener("load", + successfulPinningRemovalPageListener, + true); + gBrowser.selectedBrowser.loadURI("https://" + kPinningDomain + kURLPath + "zeromaxagevalid"); + } + }); + } + } +}; + +// After the pinning information has been removed (successful load) proceed +// to load again with the invalid pin domain. +let successfulPinningRemovalPageListener = { + handleEvent: function() { + gBrowser.selectedBrowser.removeEventListener("load", this, true); + gBrowser.selectedBrowser.addEventListener("load", + successfulLoadListener, + true); + + gBrowser.selectedBrowser.loadURI("https://" + kBadPinningDomain); + } +}; + +// Finally, we should successfully load +// https://bad.include-subdomains.pinning-dynamic.example.com. +let successfulLoadListener = { + handleEvent: function() { + gBrowser.selectedBrowser.removeEventListener("load", this, true); + gBrowser.removeTab(gBrowser.selectedTab); + ok(true, "load complete"); + finish(); + } +}; diff --git a/browser/base/content/test/general/pinning_headers.sjs b/browser/base/content/test/general/pinning_headers.sjs new file mode 100644 index 000000000000..51496183a7d2 --- /dev/null +++ b/browser/base/content/test/general/pinning_headers.sjs @@ -0,0 +1,23 @@ +const INVALIDPIN1 = "pin-sha256=\"d6qzRu9zOECb90Uez27xWltNsj0e1Md7GkYYkVoZWmM=\";"; +const INVALIDPIN2 = "pin-sha256=\"AAAAAAAAAAAAAAAAAAAAAAAAAj0e1Md7GkYYkVoZWmM=\";"; +const VALIDPIN = "pin-sha256=\"hXweb81C3HnmM2Ai1dnUzFba40UJMhuu8qZmvN/6WWc=\";"; + +function handleRequest(request, response) +{ + // avoid confusing cache behaviors + response.setHeader("Cache-Control", "no-cache", false); + + response.setHeader("Content-Type", "text/plain; charset=utf-8", false); + switch (request.queryString) { + case "zeromaxagevalid": + response.setHeader("Public-Key-Pins", "max-age=0;" + VALIDPIN + + INVALIDPIN2 + "includeSubdomains"); + break; + case "valid": + default: + response.setHeader("Public-Key-Pins", "max-age=50000;" + VALIDPIN + + INVALIDPIN2 + "includeSubdomains"); + } + + response.write("Hello world!" + request.host); +} diff --git a/build/pgo/certs/alternateroot.ca b/build/pgo/certs/alternateroot.ca new file mode 100644 index 000000000000..058b9f56d8b7 --- /dev/null +++ b/build/pgo/certs/alternateroot.ca @@ -0,0 +1,18 @@ +-----BEGIN CERTIFICATE----- +MIIC2jCCAcKgAwIBAgIBATANBgkqhkiG9w0BAQsFADAmMSQwIgYDVQQDExtBbHRl +cm5hdGUgVHJ1c3RlZCBBdXRob3JpdHkwHhcNMTQwOTI1MjEyMTU0WhcNMjQwOTI1 +MjEyMTU0WjAmMSQwIgYDVQQDExtBbHRlcm5hdGUgVHJ1c3RlZCBBdXRob3JpdHkw +ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDBT+BwAhO52IWgSIdZZifU +9LHOs3IR/+8DCC0WP5d/OuyKlZ6Rqd0tsd3i7durhQyjHSbLf2lJStcnFjcVEbEn +NI76RuvlN8xLLn5eV+2Ayr4cZYKztudwRmw+DV/iYAiMSy0hs7m3ssfX7qpoi1aN +RjUanwU0VTCPQhF1bEKAC2du+C5Z8e92zN5t87w7bYr7lt+m8197XliXEu+0s9Rg +nGwGaZ296BIRz6NOoJYTa43n06LU1I1+Z4d6lPdzUFrSR0GBaMhUSurUBtOin3yW +iMhg1VHX/KwqGc4als5GyCVXy8HGrA/0zQPOhetxrlhEVAdK/xBt7CZvByj1Rcc7 +AgMBAAGjEzARMA8GA1UdEwQIMAYBAf8CAQAwDQYJKoZIhvcNAQELBQADggEBAJq/ +hogSRqzPWTwX4wTn/DVSNdWwFLv53qep9YrSMJ8ZsfbfK9Es4VP4dBLRQAVMJ0Z5 +mW1I6d/n0KayTanuUBvemYdxPi/qQNSs8UJcllqdhqWzmzAg6a0LxrMnEeKzPBPD +6q8PwQ7tYP+B4sBN9tnnsnyPgti9ZiNZn5FwXZliHXseQ7FE9/SqHlLw5LXW3YtK +juti6RmuV6fq3j+D4oeC5vb1mKgIyoTqGN6ze57v8RHi+pQ8Q+kmoUn/L3Z2YmFe +4SKN/4WoyXr8TdejpThGOCGCAd3565s5gOx5QfSQX11P8NZKO8hcN0tme3VzmGpH +K0Z/6MTmdpNaTwQ6odk= +-----END CERTIFICATE----- diff --git a/build/pgo/certs/cert8.db b/build/pgo/certs/cert8.db index c56a04063425d15ebef80e5c54417c66b375458e..508b7defb4479958f4530bcdeb01b63c073be182 100644 GIT binary patch delta 3200 zcmbVOXH-+!8oenfMFawZGzU=w0*N;ak*f5nj-WD>p#>6(B!D5|iIf|n6s2egQpSS9 z03s*@0mt%aibzpZK$=4l5u^zWO)QTI%?G#^k9q5^`{Ug8t#kJM&i?k^3 z?atS!(1n3)n4Xo)u00>Ib=YcUTV-=aKE)CRTZIw@5A;j45IO|SIiV*j%ZYaUix=WW z000aS0svP6(bCJu$IHjVkVK^cfC~3JnH3D8aXUfz4G3!_LR4CxOe0Z!2sD!1F)H1k zMk31T(`lZ*R4-Z}0^Enw1~umk|G}Qnx;GV7#S>OFBuQ_>oCiH(v@b4B*w9dk|uz^o0 z!YpE15C#)qv-~v{`+)+0#qAb*Vn86yw*+q%;r#!!WWDG$SHTAik$Oly_{bc5_#pQ^ zwgUc2^i(`f4b)WCRQ(Z;$7$G02&yh$u7JJxcbK{6vCn0JP%bA*?d}B&;ncS-@(BbD z2bgh5Q5=Q(+}<}9ZgY`!$X2q{y#)QqEDo2;o0eB$&|af&bTT<1TxT}k;CfD4eK8U3 z<@vMvt{1=YYkKpv&Ljn!b0`FL-LIR3YbD)el#@IRa5u%2TC^f9Ow(+H@baqYs^v!!3=`^{)GRG`m72?(+MMkyM=v z9aCmvb|J?<&Zs^gYIIH~)#8-`3fuWGQhc2H%gL5^q!WbY_x^IqJ*ut7r02!mEyjK> z!`qKOyQmd%wf*3i--9p?L^ZqK&h+d7&nf3b^-$-hIcCr+SvUd)z|x&S2hg6&Vp$R7 zQP7GBD*Z#sF=dt3Vk9WI?8{s_JBYyw${iW9Gd*Y1cjimSgp=O$VO_EQXuMm5yc0!xvW+F(>mGZB=8!-~E9CTog z3u4^KS){tDuP>1+3Ec5uv*;8E1^~A69P9w3xcqAanXrco9E8DE{mlx7t)Ug*HVMIb zrs7p`syMvb5>uC!%lDC4OLW*NS=*uV!(D!G(R(f7*UTdA+!Y!p^6NNMk=YRhFGgH9 z3DO!mckwDKyB(9?-aY&}Cwx=7EV>r*GPh_}5Le$KlCPi^_sL}No%+)Q*ia{%VMa}f z42j9P)8}VG-Y@9f?ZO*#0JEKQ?~j5f%^z-hM*kdZf|p9=QL_fm8;H=!1`J5R!{;N` zZfqprX$NKe-`bROpA$PX$DMEhJ$bGkOqI-^>QsKI^5*DA8tSDU&p`!~z!ZvEe`nu|%z{JNA1tLiQX)_6 z-!q`sm^)_RkZ6A;B7<`YlE`H&J{rgqD&IWpG8@)ic4(@#uORq5v!xhv z-(i=^@^egal?~cvm~S*aadX>|(Vn8F_GpW^LDzoC+csGP9lEExBbmKZlgT%DYt9U8 z?cfAm9T^kp{*<6&*pI$$KD#F%z?Isc#+ya2dN!JL81| zfZ}?K;oK7?7L4(`BRP7$Wx_CdHjIJWx(oXXQ;tc%cw=6xRH>X%u~9+ou2Mwp=2$SQ z6d|rzEvP-$tWb+)#lNWb^R}~6I@SyB+px{qf(CBy(y%Qpmwa1!&f(YTvFz9^40NNj zNVD_Zp*ArZ_Oez%Pv9l%mXj4`Vsgc)T`5C-GIv><C~P=a`Kczj5A6*{6Tm$ z5%*g~-P>&zZsg$N^1>&XdGvuZr~Kf z#i6|Fw-ul8Kg8z|lcl8$=}lK^rFUN7@xbQ=W!+H>gjspW>m+PctEowo7@%#`Rj~}< z-z<7sA|_#+29L_LE4_8ICbF!inRL{>Yp+~&Jv+zSDHHvp-b_bI*`ABad`?gZYr!$} zL0xd)f@ACj)tKvS;c}l}C8P6Wtc5DbWJ!i~beBg^t`Ni|dB9=%Budgp6Bn-lsU?L-g9xP#Wi z4g+Ze;>rF*w@ttg02ZB-3?WvG=<>wH4cW_2`Ce?+C|(6={@4XfQUI`A`iOG@{lD4=?B)jg T6^Pbw=#|jdDCGQfyTIyi?m delta 1364 zcmZo@U}PHy1j{ z%1&-@VBg#q`<`R+jB?(|`{S-mo*&00VPR-wViqONYh-L-WN2gv;TkmFn0zpg;`i6wM=*Nzf}3j^Jbe(*3DF?KRz+kYu=PVrd>A*Ws=vMbB6U?3SkOL+yCaU z!J3jMpO@4zDy)B|!?=7J-^+(><$IW>&63y}`KspszJgs_C*M?QdYvFP zUGq+Ntj?P+>J5)syc}XyTd_E0-bcPTh5W-$K5jbBI#I~m>frNhHkUJVjBX3=@~qx& z?!Nioy3a}`v2N9zX2sc?ZylR(y8Y9Fr(RjxVtiC0Pkuf(<*s;fidWt=ZNnovnU!W~ zJ1lN&*NG%*SQgHTqPrHPRp$YE{}sf(U&!15(kck9yF z@_qlTW}md4D>>PF-s^fsW&>f6T2&Sv11>fWZ8k<$R(57a18$HYKMM;p6YBy4I}k^n z#oWNuz<7b-0{u4KjFOT9D}DXcip1Q4oK(H!{9OIaypq(S+|-oJ#FA89VAPeQ76S$J zl8bT-d_npZSlkR;44l}6fOfDjzV>XD0zx(^#^w@E#>pW`PW3tGOX@y7ouyR%o5N1) zE2aYv23KJz7V3P8?&YP(A5# z+S)`Vd3wQ?T{*Mfm~4&Qdwaw6#Xrns&ZaBvIu)kI_`%TX!;%-rR$BdOuCbF_xvpMf zdlg6gPA$vKgNFa)f`tW~9Bd2>HyMCn^23GMlNapgp8P&FnUQUBW1K7_!(@Y{ o?JU69k%Mv{EX;u^;ox9mV7Lv9AZe813~am}EZ1!ITem3?03@alE&u=k diff --git a/build/pgo/certs/key3.db b/build/pgo/certs/key3.db index 5a45b6c4e761fce905755443c48efecf6a89f91b..f3c610fe83155c083cc23beaf04cb0f7d8e85caa 100644 GIT binary patch delta 14295 zcma*tQ*f_clqcZWwr$%wv2EKrvH!7c+qP}n=82ONJDIP$znST&>Wg`+*3I5k`|7QF z_WG^u?+@Yc4-rp^4hjSWgz!%x{!@_u6xct-)4(^32m|uZA_&YYGzd%+4ALt!6etid z5K|!7a}u%&D&V7tc)qR(^R;;M6JRgF%7%UKM9O@>_ZC-QLnGHJ3)?cwZ z`j;hKPG0YO183giv`FZ>Pb|7lHK=sz{PY<~>Q=v`P#bx2DCkSBl#Wl@r#pS^<8XZr z{v0HG>qA>3I*a$&0=Oq0IJk4j=mv5~n~u}g1X9rS5P(_s4rEjy2s59HdXkXgX6L&k&{(&yCC^{wVuZ`9FW5 z_*v>Q9sX<__F*APfQwDiA&QZ&prDCwvVCD?EZX@utnkoH<*@IdwjXEa$Gfs$0 zJ0P1a4p-);6`@?(eP2^fB8Sx-qI*-F+Qp6c383|E!Bof-+M-_<)3ZiR{I#AyyT$zX z8O3ykx{ZMpO+5dE>@&31+Sr&k!m^A zX>*(QpO|tJsNJ?FYQa5jun-j;tRgenCf(WUNpQ7cqB?YzxkN(kv^Q;y!u?1ozP$hP zALE1yF!GPjagQGmsAAAH&&rD<@v%frF%R!&*sMq_R6~uA-vIvu55VE2 zo-KRV^>C*nU2AylzO~?ZA7|_(K>P`bgnGSue_WxA=J*i@po|ACH+)6ErJ+u zKjlP}-aI0(eGv3im&Q@xS3Au0C!p~6!hL5yIHGH7nzwk~dv!<>7Ne%n+siL;Q0YIg zB5d-snDWZ+33_(jzgtvO-tr)E7B}n{uAFSty-Jn}_2p*$;pF7x)Y_w+`hUKDxA&{> zHlX2)S;EaezlHk~N*7dQTk8h_p*v93M~<#H?AD;*MtqB!j?6&O#e7&Tx&ds=GgdEj z_yZGnV_GSD2x%XpN7E-MNYvCC^Z1u;t9Vi8rkjC2Ro99yc==xE(j;&Vjh+=NfBcO{ zJ+^;!%t3X_(ei{0xyWlcKZ4UV)jdyo&u;Np^MK_;ocWdUN;LJWq|pi(OH-Q&(tF27 zYCxJ_x1ZK#-$jYx_e1m^+5_S)L@P0-o)R%BRlq~O1bh5YWV@zB5~6`} zD-2ybEz}SWZ4g*D*Hg3;ZOhss#VKNMwzZc^X13U$*V91$yrDn1rdr!@infR!nj*8} zG4u1f8*|z^EQ!FE#r%4oVQ;^BC-mSrzXQpMGB*wkys!kdRHT~(llL0LmLNbg&mbsz z%ws4>=xa8y=2AN@3bc0*8DPGnNwHCcO1?|Qp?+2}l(bb>9jMydbC;5$>awi&!Ty*n6T;`ugPJViu@h^fbww%-WU}2kvq2H|>(H8p_mmZMmH2s{Q2G-7 zy@7r=F^4n^#nH8v^<-sX{`S36QAmW;1kChE3$dH4x%XFR1ZM_sFiC{WqK)S+Ul(RN15jVAPw~9%p&kGE6t!ldpgXj&&v| zC*6Ti?k6_7!nT+J7^;`}l;{3S^9<1V@v^yjCrkU74D(e}k``^HMXP$d?+Wi^O5a2^ zpC|>dH7R_lFEDlL8o7BH!Yfp4&~3xeYcMM}JC5G+AQ($99K5;M_GOv9S=&x%1`uPA zwCr3I@i^6w!rP#XO}aDy~0LmHx(}Q0tT6X;%%uV^t$Q2bA5&;y1^P?OL3< zL!UeN;=Wj*@i&}c!tZzq*zOvGve_Xr)_EPYD_&DqSIFiW#vJ@(p<2Ko>c@4l<}wU> zgbQS!yJ=GS6=q!Qdo%ko0qqs__=r zx7Wm;4TS#_KI$2G>s>`SjQdqjU#v^EIDUgdk*HvExX#L$w7%~9bRARsZ$Mh>@pLUm z(OSi$pYz;X=BhH}c9y>IYj z&?>C=%&4kCOfzAkU9l}1Qe)QO&c+zOdj#4KS$7s<493lZ-{kLi zz5Xcyl;o*?+3)-e+*|mBQ6cx6$|7ii+i!vfgkcbN-pnwHX?y@<<6$hU^k~}d`V?my zP>jc%XX6*QwRnMH`f67VI9U4?XTu#E7O^x_Ok)}?r?Dqx(h)d0DNm*y4x_NAO!I1T zTtda zPGTlbupiaqV;X=InMR%dEDDxc@4?)sLL&O)bbq&U+|Y=;brj;6HWcWMr6`%jS^n-K zVSHkp_rND1Zsc5EgXSLD%jE&Fyq^bxXLQ=`x0zoSRf@4>=3#B@#o8j;kR?rWY$UEF zxT?m^AM=rGsTJeTVN$|+lcD5_(iLL1vs95{N;)`3sV+cP-XpQ@iQt;i@3GgDGW-BHh6vM3f`vvXj*yi)jF0I?mA$~W(% z!?wD4Q%-MbhlvcQv`uL@Qs7QkvYIRcp^ z5&iekq@0llnT~dTFUAS?;`J@duW{Z!qs%$2lB7uXJ`cvI(%fL2dap;^b!K1$E8*v_ z$1|5CB4Q;TrQ7R9$zX=!#iQjcORRdu(8M&6$IKn|mxq@P`sng|9P}6d}Li9;7 zK_CEBm~pNlJ}358)0!;9)J3j{;qRdgeMi@^BMwZ}k*n%osGvA3`^@bmfv7%5%xG8P zbeWhNm}{MZwW`N8o}%Rt+6fVu$TnzH#f+9ju+6SCO}(Z_1hVC6%rwWPzwEB%kgkw^ z(Uu@;4jH>Wp8oHSfQbqMmL^m(^d$*L1qQ&~HunNIIH`G$LqM7_BNp$+efsmS0qK*U zsq*@MgskcoQ=$H+&VRQo-Hbd|=#f7lqsoOJNL>YKw^m5TM%O&uEi8+TSa~i0i<8P@ zqP*bT_E6dsgVEK=6w%;*@vh%~kq+ZFPZDT>4(w!X{o~U0hiabX`h|1e;Q$?B<@y5a zFTsxRK!6qoVmC-!0zRqx?;wxFRg|YBgclP1sX%B&c6X7H+^eF@a#W77n4I21gK22L ztVmHa4N1z(T9+3Xo7#b1g=EX!BhbPd3i-|9je^z^gl|>x!ET)$i_YVgh8`zF1dP-_ zCXJ#!VEFF!lg>>iUv9~!PA-PHfXnn#Y?5W>N|qYag||8r7FMYo zadyDFP6Cb-XHEzj;+f*?2D#A7QF4B&ON~3@j%R2a^h>3MDS54|EkIxo7n^4SnvJ6| z)BO8zdE%2w*UJWm?y8Q1fm}08=>uZUUvAQmCB-2vyT7b(Hf{wuUh6%+jqy<*WM4f4 zwI}*@Cp;NDG0-nItVc4;T&cSzVOV4`Ybesm3BI5BQMLyzJU7Nll{5 zay(pgaQXq?`3=GEuVD)xw6eP3pdW_Kj=h(GpT8X)?}toKS|Qqm;g;wkgac|HV&z2qM#0(9w;oEH9UgcLK&CZ5o z*DIwFd!~zL0brqRM6Aq>^&TLHaL~>wq4Sm66)tv&`ysciTkmvWv@_HpF8YuFQ&m^4 z#jZlY7N$Z>q+Y9*?{G|x`lI!dGQP^rnZ@o{BTem4@bNRilw^q=5vLXz6f{b+&u3go zlC@#nsf>_(XU4*IN8gQD*YtPjkV-0Cw-nVr#yA4l8sOi}zU8DV$~|+WWzT(*432Q~ z5!dVHX?4&4T)kBrVCl7A*GxPV&L%U}ue?o_V=Pxkbv@BkQ+x4mv2IVqK z23H!cAJAywSU#k*5>K*kLLYssjy-e9JXd|1*hgFe^m8qZhL^Ixj|u5MBOaH97j{^J z`~_-|Nlz)}0W)d;@q3TUmIux|Ir0TnqrSCc(VH19ZH>2gWERafssx$y~z2HSAgRmcAasAkDbwzRtY znn-SfkIG4(f@nab@z*W6p`k;@3cr#!*o(Fhx)Mts^9$8jP${t4xxJ}bOjUVgo+8=J zYvG3#jG$$+0+NA!Mdd&g14%wDGv_;?0t^Pt` zAi(Lhj zxFhekCjeq_yxur{rN=eDqyJFpE#*98%Dx)zys6(k7wjhdAaV;-Ove0UyP!XxoUj(#C#H0rf9t_AIaT0D4 zb{e7=V!8=gNDd-N2+8TcHweXLIyo&|3w-*4f-S{XzLBNJ+$8_v;He2uBS$M0&2#aJ z&H@&HeSmqZzQOk5^wy21&Gxu?G6=}a~VW&{!k6l!Ph=m-9Tp_??4FKI1mGEY#PZ%!SNYo_a z4p&@N{{(cD%o6ia2%X%&?YYqlb4t&c2JvJ1;S?6aob|X|a)PIB-OC8DSPdB7U2@v;bWQqFpc+R;y8_prcjPH%8?B&#EZ%jQnGN`F^*dR5 zx?YsvjrUe90biE!*Ldske4^9B^B$u@R^3LtE6LWjcz<$?!}}aAz9!f18obERA$;|v zV9Z{=ZC}}8*%YVw)^=;v$bRGW*o_%WXLr2`jc2ptWjl-)Fg5rivY)twDST#S%zhGt zvpv!YvzYp{CJcvT)}X}veBR!shg-SUvs{;@M>?we1IF55?d{NGp?!kmf;6_lR9v2N zLtibq40#0D&sDI-PEFkMX78~{C8+B!3jsb;M)|IZdCuonzvK!M0!Qtae(CD9Cz#^+ zt{T&e2Wot(_}D@V&8E_G4Z6QNwxMVzwJmE6FA#UrDCIz{<^>{8kq~AsR3NF`{Z*4W zB*2+M0)U)13ybi`LnMF~Poj_Sc{Q!99&@5EqrKXCso*b|BtSaLb7N)h!AWndc5esnBDyGTfaK4tHtyv1yWzu-W;JM&=-2F~^qbklAM9PlxfBh>prQ5KiTqU79e$&?SGm9= zY`_=F%E9(aRoG!b`@W(EEZOjKX1v=qmHQ)XV}pmQQiqqNb+{KCT&5N_GLZoAc7Lc+ zSh=w&#rw=$yij2tes;WBiG<m>7Fe>gPA#~=tA7F~=Z0v! zFZVafJC!#n+ofZxytfscm8DCJ`x6Y4%PH+~|Go?&?vsmZeAR9y1%p1M^f+`C6>*Vj zYX>OkJI>ZHZ|J*_l`a~iU+S(J;9`-UAh>(}f z`pDuvtg%5A2Rz%F?AZ=z47v}uR_QCq{H|^aXKRm_M#?)fBuO^n(ghal1+>K4In8N1 z#DHj|B*_PY&VxNfa)qDqQR_rLeM8=OmvmhT*mW z-*`*&k!rFt;uQRXLz==i!J|#H5N57Kqgm$(la?sdKxEg3vK=`4>(`=clDyOs<)f*D zXX#Xg*uOgR;-t8T^mf^t2N=59d%9jvyk$jj86EmA;~t3KE}x-2EttX}W`t z*+Yt(-4Sc3$vt{E>D|>b`)A5dgLuPM1fQ62u-=bG#e0%Jps&j@SoB1nRagFT4 zWCUoBsbY^p&b5}E+xX!@Z-|xXPLMBiP>BUTp=l|`di%gq1rnUX7D5O>&i@{_V9$ZD zx`@HF2hunrj^|Eo41Y4pqRaFAwx#IA1;}`~cd?=Yi;anBJ~C4CiG*i!i^um30uf2XEY)2i&#}^Eg;U-hr;O6Yyyan6wAc3t zy{#_;zBb@?hs<6uAn?$)y%DDG@cKPxZ&Q7Lvc##6xOWgCw@6FZJZ~UkN#P8Pc4@ts z;tftU^aMBTqbo-$;u11Vt?(8LQS=U{7;couy&uIkQ5?V2AAbnAbPtG5Gxn^D&mHh- z`h)@{P>>4~NzBRo21g`Ne1Kl_+xjk%_Kn9vBgVcGZAg1yoXj8YFWOAOfX9L2752P!r?(d8R3=!i&1*_f>yCxt`Y@gX(4~!zI30ZFxIw}A)k!^ z-+80P>2?z&-Ct;JV1cGu(T;q8DIuG9K$F;q%rsM=Yn69#8XW7tly#s6s1fqgRhW%R z@liP%RCc_8A18@|egsWEX(?!3Ib3^EdN*_sWN-j4!d>{AjU8C3(oY-+ za~`sz9preTLke{KIcCpKG^4H&@HbW1jSlyD`g^-gh~+~z{JK(zQAE1-QFC#t6`=G_ zi2KO7LpB2Gca&nzA3~DWaw|$oli(kpi`1MLXJ`R8ZHie=UO@a?CRB%Z#P&RJC~x00 z?rS8Sc=t}v`&&q~_v#kjHxBJ1ZBI*f4e#j2sUg+)B(Cqn)s$L}+3;eW^sZpwv@bR8 z&iO~cjeBWWn@A5u%2z%r8v}_NYQZbw52YHC_}kL(=Hyq(o}u&;JH`DM z$*_RvPNoERa}j<`{lh_`Iu#V^c9Z1T-%Qj2uNyu)CM7Agl3h_{BcDiM$R?E*c{wXZINde*Pw7v^|U=MWp6_do#Oz^u1g(ea1W)|tJ zdo(bvzYoA&n)>|Vi>040G+DMb$%_Clvv4UhF4F_p4N!CPb?1f`J8ho269@&lKUaIo z)me-yEjOv0gE!~~UMpW4j6?dd>i&W8_#LGiz!Uu=RsI{e2@%5)fyvHoGm2k$zT{uf zPLKOYU8a7;@pB4f^v1CCk~H{RJ7_=?a=txulAtuKsP~$&=vQ&YmTkl}dLq_|$%NeA=u9{5IEN8Je~m zdQ#zsR#qh2>_PF5drkbb(CYb zUs6?=F}p9Wt28BgwAa<6Ttl>UU3>CDcrNScl@NzaNzBf@ZiW`7#^L+=*EV{y*NVk+ z>2lXWZ=bcTMnyYIK;Pw2O^)SL5jp{II1_g!01lc0sf=d5Jy=YV&SJNqVSFpEJ0IuR z4lmgZIk~yj`tUrY;y$HN;Ed;uf~)d!}0srr*FM#2|MDkJDtg&lWp5 z53zhhbBsya2_sEmmS=Zf%ww7rq|=hEsHGSwVg*3mlg>HtWnSP*3?=^orpWw8K zoywYUXVblTkNVqIPN+<$d`c1oX!p!85>qTYtJU5k{s4_T5`Qb{jKBK^*T0?mtt)iJt*p;fGZ?dVp@n{vF5~|!&HQ56sp*@ zL86fKVKp;$fx-)&+-dW$I*gBIPqx-vAhuppJIRHJkU`!86#S*BMrG3j_zOMKxio6I zg(LvpP^0&h7shO^ZHG{Vx%seekQb>g^&rZ`a30m>&p?uHDc+victF0uek~<68qQwe z_L-_P(I$Grkx(VWy7RpVqiJ+5BS9)NE&d@xBRWull0seFVu?h+gG^{oLy0gWx&$VtnDAt73}fX~RgIg!7!E!KnI>ABEC6^Y@%>Lo?p4%YwbVmeRNdeviW z)UWKY2Vd@@b2wO|Jf0SpdVOucE~0o89>>fWVA%aqtTu9mqQeXZFqs9cL@SR5M%9@#1Zu!SehRAU2>wkBDp$ni-xKOq@T?%k*mhZK2@14kP zqPZ|+^E4iNW~r?MJccFFh|nKTVP&^LW|HacgWVsJl)(I%(%v2Z{fVzpJ z;dgez#5&V-TpSVC)to6@vUqR{_B!vfRw_?mB?}V1WJtEj$wJHv7f3-mMK>4()F900 ze2n74v-#qKU<%DjWS$3Ra=F-Hd+56k2F~--Q4Bi0S9Qz9uN9 zyZ^_7b)mLsvn01z=7Y|gldB-4R@~0ye?2hxjhV;QA0J}2sxQ8|?kb10c+{Qi;a|~u z;$LZKj32CvTmIZ-Os|CT5%1tMt^?oC1cE*H=WY>Ta1}~fw60y9&I5uVj{`hs(5+k=H2`A0UF_p*(dq>% zFy-0Ld*Avui%8DjQCdoCNCP#Kox_3^PdO7eOU|)qrhCnAnpUH+^l*Zm5|{&E#ik#X z8c~kZjX4&)%Sf!xsVhDmpb_d|N@eo!I#1ozg2_E4a=FlIn>2G59;ldPqf#h0i_q=v z^dPAs#;UvgEUyfMVN0#FH97GfPN^QRbgZ#hHdjxu1p^v@XO;RO+mQeQB`wk`AL`<0BE{7VDfr(iL*Bu_-l z3jq$Dvo_nILl5XJ*5-kS-Smv2Jz-#wLvfpG83wDR`h&Mp3Npjx4p-vco~5o2@8(Z< z`qAw8OB&ViyCW5Z8~u%dkM&)kAICm)>UbGG_GZQ)(t^=*U#U;(%(o3d^hCv`^{m8` zOm)~CiloK84P(3KY!=4B1ohp^Sr{{BL7#J3g%E6oOT0Jk#kn`1iMF zJuScvmrB`xlo5)z%SBC5XUGhIqFg%S(&8wL!@yJF>}teJ^phlq!@cRK}9br{}LwJo@rCI(hc z10#)TJ*#OoVW2@hnvC{+NjI`f&jrt1$Cb?RUYjHdkP=(e3FBnjf_X2HZR`; zGt?VJi*pIUuQT$rfb+=Q*_?No`%7>D)?g`t-mA`}7!-e<{S~8n5HoN{#CfI~L|V4i zIWvj6$1Pp8HJ-b)1G4ZT?7+{w^_GCGjbswxo{d~C3YEAT-ew+PHizoDK+9A2OWTF`SqQxz;D3qvQ#%!GR>2?a zLvarIBCH5havD&+Aeu%jd7AjBy1q5i5(C1U?NN`HI5}BeZ%5&d?DZup)tHGO8}w}? zWrJzIV)*%hc4@1)pyz5NE6}lch3{nY76lgSGAkB+!D7_wUx;PN?StITVsyiQJrKA2 zaDXo^J)p58U?s(v*TmD^4N+s3i}@`VVU=oeXaZc^2eIPDw)lO}o!r?kZ6D%z840<5 zuGgM-;);$VCRVtQgah-RQwnLgWN0*KeF%MU4ImIKARvAqjsM7<_?tBTRv`X=OfrD> z%4P9MwOLW#KO_4i#|4+WPlW$mOWdnXfrUHQO$mynj-jwtEbHXpR zgLfpri+Rcv>n;AFw1vb~$4#TNS`kYD#n-=+!l1^L-!Hy9Q~He({tMm>&qlwD&|pA0 z&&rSo<4Ni?xRU&JUOqYk5+JKbtr*iptyo}ZT z%E^bi%n8daDJ-s{7?f&9YE3zdST_9CX>}1)_|UTdow(@hHF}DgwkC*E7T7lJE>M|S zUe)*(O@VJcd;}SVgLep534mxK-;Sl(Bs4F1D#;*Q)Qw;2!$e zQYOoy-P8jrUliiMhSxzC(6AXf2G&y)>!%3c*Hh^?N$jtJ@hZKCisr^c3Sl=enmMnr zj>N*EcH+Oxz46yF0t`lc`zG9+QuNjIGFTk)|F}VJF4i7|0MN($TDm=6jBjg>%pm>} zP5GZ3nlMo7EpW@s5Bup4A(_Z&k_Ns1wp{3DP;^P} zKw+EwBeGN_6M4>hT-4z!M#ZZdN*m;LkjgpW9Hr6sK?Oc?Pf%F1B1llGvcW(Eust?k z0K-*=y+Goc1(3}S5K));tv+*{R>YCIkN5W^9{%C{Iin90P|fyQ{1t)L*Ze&@JMel@ zeX1Wr^`jqr*0aKEv@jU5$}gWMrVy96X!bgI;$Wbg|E+wGm$4kVAvpO2YZ{E)BWp2n9G(zVkydq||65wF0Lh*e$`X@Fn9bOgecUniy zX<_=0vI`69TPxCU_s=T+putx-E!coi+Qe9?FC0HDX>)u3~dkS>P!vMUgAr&|=MZg^~+-L(~7T0<6J=JZfae0zgX4PFtSNpK%B zAFZXrG5qRPAV|UNAOoabz>4~6VVyRB*T1($)-kz}XK7oa-Cs83_g7pN|8bNn55K*} zQwdf#Bt(fCPCN2|VN<$hb<uSqc!ue|WF8jClawji`s6c|W_=J2 z1z*Kn^-b6yzNrHd&+}VySC|j}3Ezqxk_DqzY@D;E4GNT~Or0qoBCq zo=%1ZC`B98)HPaD&)q4z25;U32j@@7;Q;Aerbu3CZgnHw3wSW;j-HPskwzK5Ad)>9 zKIB{$sk~tF1x-(5x~62xq+;B(X=w`X@V2^@f%pt9=NU552qPoVP(^KiBlnnB7V+C0 zhP=^8oW#WRj94Y8CZodI2vs(Lds3Ktw5f7WrbvS+Ne)Oj2o?CK8!&N+Ibz`OUI485 z6hDf)rt%<$VHHQ-<1a=&Ka!d<*@oL`-?5Q!iNWGFlMEjOy0BMG&DKw;#~n z&?68d;A227Kx6;mRLg(QwBFgkaugqf*o4QDKsH&CSy&Pg;r_K~+R6IiiP=j?UhZ3m z>y;~^Hmmmps3y;dfICJ){yN_uRHX(a>Xds^SdoLol1nG!7TX2TI4MbTcP(pLd>|)B zpMKJ0J+nXCfo}YY(6EXdK)ID5b07EujDfux23_*4%DP-{g^s7T+HB z;~6pzn*P#@g3ym>TZa!rR6?E{2y=mmWi_46Q zBGg(5r?l4>8)>?}i0o{WRN>$Ndr0UPMD-GqX<$)IhV};<8O6Bv#seIlFkMXE?Aq>* zgWJw_&QZ=hAWvjKokaSZI~DF3My5Qlna=ISIGr6t-QKf>BtZkpLz*#3>GvsDj%-Yb z+HrwNOH)x4T!uaU+z%?d7eYSpx(j=RuR6r=wtMQD_|Ral>O@DpVh2(P__qU|4#?*i z`~uc{&38YMc$@=?7aN4E0$JvwLjW?JigcpjS>7?#bhmlFa(;(NKfdc!QJl zm;%Lwbel)Eri@ei1z%3V`qs*hsm`(1kgA2ROU=BS;;SWq5S*R&l-RTRXfi;LGiB9} zPIldkan=3$Q41NoEmWy4Lr*&{1o_T3)?q6X;SA4xn)f3HKRn1<-GKP zD`Dsya0XArZ$?W2Y7sG@PUTEOn!At2h!f>2+{~X|EF7xxrQBo}T~dm6@5fq$#&6Hn zY43wS7|-@BU|h_tD%NDGjw;8^x1$60_AIGnd#SfQu0>L6=fAX+jMOosuVSgrQ19w2 z6-m^Wr?I?_FPIuSui}WY1DOTban7L=F8#-%6a*Azd>Mnh-cnjJ>uKrp6hY982;H3r znidfb0>9FA^TEecZJVr`R5E%vwzLSF50M;1S;pG_PRj8 zN{F{J#tWi_p^0IOz|Bqw#vM}xY-_gq=2qJKs1Eb%Z7tR?Jn3E8lyR%St_{4G${H8I z_zY~{<|U?X9DBFC2AT-1DmE5`3$7saF#ac$rnJrA{KRqHZfSOANl)_Z74jo)lkOrg z?JwnfXl%M)zf^#N7w}b%IfxRKHVs9NU;MhIk;Zj36q>Lf_iD0y2A4I zEd~t!pH}K_uAqq3m7AhoyB|i)k(c@g>64*aZ3~vaXrj-r{GdouVZSaSBbrpFM9$Xg z?!gU-Vq0ur#afkXoxV&j#UX%2AVgD#I31t;C7s}LWoa6`xytIEfdBbp$GFh|5FtaJ zJG4q>bwX}>WqT~3hAoFP!jU|bC8X-dj)`k?uc$H+RG*%r9qPIP^@0O-UAPMzo4PM< zpqr!EHmg9f79;$@*tn^!J)Zm?J3h(<7$S>>pW1)m3HC9CzZ9H|aLKHB-$gFTv4`Be zy(2d%6?x1EleuTA0MjdZ{(Sg=ogEyR>pdb$_jh04-N0OJLR!AR^ks)>&$C^Zm{8>$ zcc@v^PBG@3;Z66PS5Ee+xZkC-OtuajgC}SVL6;cdp0s;?Kbib|tJ^%E1>5s$O5Sby z5BZ}7#eE~xFURtDS(xf^cp1wCpvNiC0p#f_~i-bvK!MA-ie6h2gF delta 469 zcmZo@U~6b#o1iYr!oa}54aEFF%nrmLzTrljBm7(p48Qmp1iW~raBk)j_`$iE>D52h zMG69p?>93V{O1>9V`$(%#RHNS;f!PRVT)q}iuV}_~R?3}!phl>ep+yS0bJV40S+y74q{g(kn1cA^5g>nQ4W-ViDM0CaAXLdE||dRv^o64N`72E-Tst^QCfIAV*ulS o{_O$*j2}?Qi3Nsyz^LK>%~{QPlx;iPQBY`aM}+o+G)4tU0N`wiH~;_u diff --git a/build/pgo/server-locations.txt b/build/pgo/server-locations.txt index 2f5bf7beda66..b6051fa9797b 100644 --- a/build/pgo/server-locations.txt +++ b/build/pgo/server-locations.txt @@ -222,3 +222,8 @@ http://example.fi:80 privileged https://marketplace.firefox.com:443 privileged https://marketplace-dev.allizom.org:443 privileged https://marketplace.allizom.org:443 privileged + +# Host for HPKP +https://include-subdomains.pinning-dynamic.example.com:443 privileged,cert=dynamicPinningGood +https://bad.include-subdomains.pinning-dynamic.example.com:443 privileged,cert=dynamicPinningBad + diff --git a/security/manager/ssl/tests/unit/head_psm.js b/security/manager/ssl/tests/unit/head_psm.js index e02a7f639a64..a1a0c4d59b76 100644 --- a/security/manager/ssl/tests/unit/head_psm.js +++ b/security/manager/ssl/tests/unit/head_psm.js @@ -520,7 +520,8 @@ function startOCSPResponder(serverPort, identity, invalidIdentities, } // A prototype for a fake, error-free sslstatus -let FakeSSLStatus = function() { +let FakeSSLStatus = function(certificate) { + this.serverCert = certificate; }; FakeSSLStatus.prototype = { diff --git a/security/manager/ssl/tests/unit/test_pinning_header_parsing.js b/security/manager/ssl/tests/unit/test_pinning_header_parsing.js new file mode 100644 index 000000000000..12630a0e415c --- /dev/null +++ b/security/manager/ssl/tests/unit/test_pinning_header_parsing.js @@ -0,0 +1,129 @@ +/* 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/. */ + +// The purpose of this test is to check that parsing of HPKP headers +// is correct. + +let profileDir = do_get_profile(); +const certdb = Cc["@mozilla.org/security/x509certdb;1"] + .getService(Ci.nsIX509CertDB); +let gSSService = Cc["@mozilla.org/ssservice;1"] + .getService(Ci.nsISiteSecurityService); + +function certFromFile(filename) { + let der = readFile(do_get_file("test_pinning_dynamic/" + filename, false)); + return certdb.constructX509(der, der.length); +} + +function loadCert(cert_name, trust_string) { + let cert_filename = cert_name + ".der"; + addCertFromFile(certdb, "test_pinning_dynamic/" + cert_filename, trust_string); + return certFromFile(cert_filename); +} + +function checkFailParseInvalidPin(pinValue) { + let sslStatus = new FakeSSLStatus( + certFromFile('cn-a.pinning2.example.com-pinningroot.der')); + let uri = Services.io.newURI("https://a.pinning2.example.com", null, null); + try { + gSSService.processHeader(Ci.nsISiteSecurityService.HEADER_HPKP, uri, + pinValue, sslStatus, 0); + do_check_true(false); // this should not run + } catch (e) { + do_check_true(true); + } +} + +function checkPassValidPin(pinValue, settingPin) { + let sslStatus = new FakeSSLStatus( + certFromFile('cn-a.pinning2.example.com-pinningroot.der')); + let uri = Services.io.newURI("https://a.pinning2.example.com", null, null); + + // setup preconditions for the test, if setting ensure there is no previors + // state, if removing ensure there is a valid pin in place. + if (settingPin) { + gSSService.removeState(Ci.nsISiteSecurityService.HEADER_HPKP, uri, 0); + } else { + // add a known valid pin! + let validPinValue ="max-age=5000;" + VALID_PIN1 + BACKUP_PIN1; + gSSService.processHeader(Ci.nsISiteSecurityService.HEADER_HPKP, uri, + validPinValue, sslStatus, 0); + } + try { + gSSService.processHeader(Ci.nsISiteSecurityService.HEADER_HPKP, uri, + pinValue, sslStatus, 0); + do_check_true(true); + } catch (e) { + do_check_true(false); + } + // after processing ensure that the postconditions are true, if setting + // the host must be pinned, if removing the host must not be pinned + let hostIsPinned = gSSService.isSecureHost(Ci.nsISiteSecurityService.HEADER_HPKP, + "a.pinning2.example.com", 0); + if (settingPin) { + do_check_true(hostIsPinned); + } else { + do_check_true(!hostIsPinned); + } +} + +function checkPassSettingPin(pinValue) { + return checkPassValidPin(pinValue, true); +} + +function checkPassRemovingPin(pinValue) { + return checkPassValidPin(pinValue, false); +} + +const NON_ISSUED_KEY_HASH1 = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="; +const NON_ISSUED_KEY_HASH2 = "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ="; +const PINNING_ROOT_KEY_HASH ="kXoHD1ZGyMuowchJwy+xgHlzh0kJFoI9KX0o0IrzTps="; +const MAX_AGE_ZERO = "max-age=0;" +const VALID_PIN1 = "pin-sha256=\""+ PINNING_ROOT_KEY_HASH + "\";"; +const BACKUP_PIN1 = "pin-sha256=\""+ NON_ISSUED_KEY_HASH1 + "\";"; +const BACKUP_PIN2 = "pin-sha256=\""+ NON_ISSUED_KEY_HASH2 + "\";"; +const BROKEN_PIN1 = "pin-sha256=\"jdjsjsjs\";"; +const GOOD_MAX_AGE = "max-age=69403;"; +const INCLUDE_SUBDOMAINS = "includeSubdomains;"; +const REPORT_URI = "report-uri=\"https://www.example.com/report/\";"; +const UNRECOGNIZED_DIRECTIVE = "unreconized-dir=12343;"; + +function run_test() { + Services.prefs.setIntPref("security.cert_pinning.enforcement_level", 2); + Services.prefs.setBoolPref("security.cert_pinning.process_headers_from_non_builtin_roots", true); + + loadCert("pinningroot", "CTu,CTu,CTu"); + loadCert("badca", "CTu,CTu,CTu"); + + checkFailParseInvalidPin("max-age=INVALID"); + // check that incomplete headers are failure + checkFailParseInvalidPin(GOOD_MAX_AGE); + checkFailParseInvalidPin(VALID_PIN1); + checkFailParseInvalidPin(REPORT_URI); + checkFailParseInvalidPin(UNRECOGNIZED_DIRECTIVE); + checkFailParseInvalidPin(VALID_PIN1 + BACKUP_PIN1); + checkFailParseInvalidPin(GOOD_MAX_AGE + VALID_PIN1); + checkFailParseInvalidPin(GOOD_MAX_AGE + VALID_PIN1 + BROKEN_PIN1); + // next ensure a backup pin is present + checkFailParseInvalidPin(GOOD_MAX_AGE + VALID_PIN1 + VALID_PIN1); + // next section ensure duplicate directives result in failure + checkFailParseInvalidPin(GOOD_MAX_AGE + GOOD_MAX_AGE + VALID_PIN1 + BACKUP_PIN1); + checkFailParseInvalidPin(GOOD_MAX_AGE + VALID_PIN1 + BACKUP_PIN1 + INCLUDE_SUBDOMAINS + INCLUDE_SUBDOMAINS); + checkFailParseInvalidPin(GOOD_MAX_AGE + VALID_PIN1 + BACKUP_PIN1 + REPORT_URI + REPORT_URI); + checkFailParseInvalidPin("thisisinvalidtest"); + checkFailParseInvalidPin("invalid" + GOOD_MAX_AGE + VALID_PIN1 + BACKUP_PIN1); + + checkPassRemovingPin("max-age=0"); //test removal without terminating ';' + checkPassRemovingPin(MAX_AGE_ZERO); + checkPassRemovingPin(MAX_AGE_ZERO + VALID_PIN1); + + checkPassRemovingPin(VALID_PIN1 + MAX_AGE_ZERO + VALID_PIN1); + checkPassSettingPin(GOOD_MAX_AGE + VALID_PIN1 + BACKUP_PIN1); + checkPassSettingPin(GOOD_MAX_AGE + VALID_PIN1 + BACKUP_PIN2); + checkPassSettingPin(GOOD_MAX_AGE + VALID_PIN1 + BACKUP_PIN2 + INCLUDE_SUBDOMAINS); + checkPassSettingPin(VALID_PIN1 + GOOD_MAX_AGE + BACKUP_PIN2 + INCLUDE_SUBDOMAINS); + checkPassSettingPin(VALID_PIN1 + GOOD_MAX_AGE + BACKUP_PIN2 + REPORT_URI + INCLUDE_SUBDOMAINS); + checkPassSettingPin(INCLUDE_SUBDOMAINS + VALID_PIN1 + GOOD_MAX_AGE + BACKUP_PIN2); + checkPassSettingPin(GOOD_MAX_AGE + VALID_PIN1 + BACKUP_PIN1 + UNRECOGNIZED_DIRECTIVE); +} diff --git a/security/manager/ssl/tests/unit/xpcshell.ini b/security/manager/ssl/tests/unit/xpcshell.ini index 439757fda180..e139b1dec26b 100644 --- a/security/manager/ssl/tests/unit/xpcshell.ini +++ b/security/manager/ssl/tests/unit/xpcshell.ini @@ -39,6 +39,7 @@ skip-if = buildapp == "b2g" && processor = "arm" [test_sss_savestate.js] [test_pinning_dynamic.js] +[test_pinning_header_parsing.js] [test_certificate_usages.js] [test_ocsp_stapling.js] From ab3a06137447a0335a1daa6c985dc90167fa9ad1 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Mon, 29 Sep 2014 19:08:45 -0700 Subject: [PATCH 62/82] Bug 1074062 - Fix a DMD link error that only affects some Macs. r=froydnj. --- memory/replace/dmd/moz.build | 1 + 1 file changed, 1 insertion(+) diff --git a/memory/replace/dmd/moz.build b/memory/replace/dmd/moz.build index d50d631ae881..25fe052c2b1e 100644 --- a/memory/replace/dmd/moz.build +++ b/memory/replace/dmd/moz.build @@ -10,6 +10,7 @@ EXPORTS += [ SOURCES += [ '../../../mfbt/HashFunctions.cpp', + '../../../mfbt/JSONWriter.cpp', '../../../xpcom/base/nsStackWalk.cpp', 'DMD.cpp', ] From 6cfdbefa4d9fd8d154aac6ebbb73f30399cee663 Mon Sep 17 00:00:00 2001 From: "Carsten \"Tomcat\" Book" Date: Tue, 30 Sep 2014 08:16:27 +0200 Subject: [PATCH 63/82] Backed out changeset 405b97368630 (bug 1074004) --- media/libnestegg/README_MOZILLA | 2 +- media/libnestegg/src/halloc.c | 13 +++++++------ 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/media/libnestegg/README_MOZILLA b/media/libnestegg/README_MOZILLA index 11ce46ea96ec..40f66f037347 100644 --- a/media/libnestegg/README_MOZILLA +++ b/media/libnestegg/README_MOZILLA @@ -5,4 +5,4 @@ Makefile.in build files for the Mozilla build system. The nestegg git repository is: git://github.com/kinetiknz/nestegg.git -The git commit ID used was ec23378e70030bacf3b4905c444c38ef4a37bceb. +The git commit ID used was 2f596487949b192fb15866c6f8ecc6b04df92cb8. diff --git a/media/libnestegg/src/halloc.c b/media/libnestegg/src/halloc.c index 3d0e75cb588e..441107d83bc3 100644 --- a/media/libnestegg/src/halloc.c +++ b/media/libnestegg/src/halloc.c @@ -40,8 +40,10 @@ typedef struct hblock * */ realloc_t halloc_allocator = NULL; +realloc_t halloc_wrapped_allocator = NULL; #define allocator halloc_allocator +#define wrapped_allocator halloc_wrapped_allocator /* * static methods @@ -62,10 +64,7 @@ void * halloc(void * ptr, size_t len) /* set up default allocator */ if (! allocator) { - if (halloc_set_allocator(realloc) == 0) - { - halloc_set_allocator(_realloc); - } + halloc_set_allocator(realloc); assert(allocator); } @@ -190,6 +189,7 @@ int halloc_set_allocator(realloc_t realloc_func) * * Thanks to Stan Tobias for pointing this tricky part out. */ + allocator = realloc_func; if (! (p = malloc(1))) /* hmm */ return -1; @@ -197,10 +197,11 @@ int halloc_set_allocator(realloc_t realloc_func) if ((p = realloc_func(p, 0))) { /* realloc_func cannot be used as free() */ + allocator = _realloc; + wrapped_allocator = realloc_func; free(p); return 0; } - allocator = realloc_func; return 1; } @@ -210,7 +211,7 @@ static void * _realloc(void * ptr, size_t n) * free'ing realloc() */ if (n) - return realloc(ptr, n); + return wrapped_allocator(ptr, n); free(ptr); return NULL; } From a20d7f582bf354f0ce1a472c7acf27eb8e109c40 Mon Sep 17 00:00:00 2001 From: "Carsten \"Tomcat\" Book" Date: Tue, 30 Sep 2014 08:16:31 +0200 Subject: [PATCH 64/82] Backed out changeset 2af5453da3ab (bug 1074004) --- content/media/gtest/TestWebMWriter.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/content/media/gtest/TestWebMWriter.cpp b/content/media/gtest/TestWebMWriter.cpp index fd58ed071fa8..99339c0f4536 100644 --- a/content/media/gtest/TestWebMWriter.cpp +++ b/content/media/gtest/TestWebMWriter.cpp @@ -240,7 +240,8 @@ static int webm_read(void* aBuffer, size_t aLength, void* aUserData) // Check the read length. if (aLength > ioData->data.Length()) { - return 0; + NS_ERROR("Invalid read length"); + return -1; } // Check eos. @@ -291,7 +292,7 @@ static int webm_seek(int64_t aOffset, int aWhence, void* aUserData) return -1; } - return 0; + return 1; } static int64_t webm_tell(void* aUserData) From 1179dc5061e087e46a89e20d2181f7b3b7793780 Mon Sep 17 00:00:00 2001 From: "Carsten \"Tomcat\" Book" Date: Tue, 30 Sep 2014 08:16:55 +0200 Subject: [PATCH 65/82] Backed out changeset 571cef4dcf52 (bug 1074004) for m-oth test failure --HG-- rename : media/libnestegg/README.md => media/libnestegg/README --- media/libnestegg/{README.md => README} | 2 - media/libnestegg/README_MOZILLA | 2 +- media/libnestegg/include/nestegg.h | 5 +- media/libnestegg/src/halloc.c | 21 +-- media/libnestegg/src/nestegg.c | 241 ++++++++----------------- media/libnestegg/update.sh | 2 +- 6 files changed, 86 insertions(+), 187 deletions(-) rename media/libnestegg/{README.md => README} (63%) diff --git a/media/libnestegg/README.md b/media/libnestegg/README similarity index 63% rename from media/libnestegg/README.md rename to media/libnestegg/README index aba7dc2727f6..47c8237d2f1a 100644 --- a/media/libnestegg/README.md +++ b/media/libnestegg/README @@ -1,5 +1,3 @@ -[![Build Status](https://travis-ci.org/kinetiknz/nestegg.svg?branch=master)](https://travis-ci.org/kinetiknz/nestegg) - See INSTALL for build instructions. Licensed under an ISC-style license. See LICENSE for details. diff --git a/media/libnestegg/README_MOZILLA b/media/libnestegg/README_MOZILLA index 40f66f037347..c7857bbdbe88 100644 --- a/media/libnestegg/README_MOZILLA +++ b/media/libnestegg/README_MOZILLA @@ -5,4 +5,4 @@ Makefile.in build files for the Mozilla build system. The nestegg git repository is: git://github.com/kinetiknz/nestegg.git -The git commit ID used was 2f596487949b192fb15866c6f8ecc6b04df92cb8. +The git commit ID used was 46ab96bcc8b099704cc8a15993f80fe0269a5284. diff --git a/media/libnestegg/include/nestegg.h b/media/libnestegg/include/nestegg.h index 04fc3e9c0555..06f9c83b8772 100644 --- a/media/libnestegg/include/nestegg.h +++ b/media/libnestegg/include/nestegg.h @@ -381,11 +381,8 @@ int nestegg_sniff(unsigned char const * buffer, size_t length); * Set the underlying allocation function for library allocations. * * @param realloc_func The desired function. - * @retval 0 realloc_func(p, 0) does not act as free() - * @retval 1 realloc_func(p, 0) acts as free() - * @retval -1 malloc failed during realloc_func test */ -int nestegg_set_halloc_func(void * (* realloc_func)(void *, size_t)); +void nestegg_set_halloc_func(void * (* realloc_func)(void *, size_t)); #if defined(__cplusplus) } diff --git a/media/libnestegg/src/halloc.c b/media/libnestegg/src/halloc.c index 441107d83bc3..5758fc07b9e7 100644 --- a/media/libnestegg/src/halloc.c +++ b/media/libnestegg/src/halloc.c @@ -40,15 +40,13 @@ typedef struct hblock * */ realloc_t halloc_allocator = NULL; -realloc_t halloc_wrapped_allocator = NULL; #define allocator halloc_allocator -#define wrapped_allocator halloc_wrapped_allocator /* * static methods */ -int halloc_set_allocator(realloc_t realloc_func); +static void _set_allocator(void); static void * _realloc(void * ptr, size_t n); static int _relate(hblock_t * b, hblock_t * p); @@ -64,7 +62,7 @@ void * halloc(void * ptr, size_t len) /* set up default allocator */ if (! allocator) { - halloc_set_allocator(realloc); + _set_allocator(); assert(allocator); } @@ -174,7 +172,7 @@ char * h_strdup(const char * str) /* * static stuff */ -int halloc_set_allocator(realloc_t realloc_func) +static void _set_allocator(void) { void * p; assert(! allocator); @@ -189,20 +187,17 @@ int halloc_set_allocator(realloc_t realloc_func) * * Thanks to Stan Tobias for pointing this tricky part out. */ - allocator = realloc_func; + allocator = realloc; if (! (p = malloc(1))) /* hmm */ - return -1; + return; - if ((p = realloc_func(p, 0))) + if ((p = realloc(p, 0))) { - /* realloc_func cannot be used as free() */ + /* realloc cannot be used as free() */ allocator = _realloc; - wrapped_allocator = realloc_func; free(p); - return 0; } - return 1; } static void * _realloc(void * ptr, size_t n) @@ -211,7 +206,7 @@ static void * _realloc(void * ptr, size_t n) * free'ing realloc() */ if (n) - return wrapped_allocator(ptr, n); + return realloc(ptr, n); free(ptr); return NULL; } diff --git a/media/libnestegg/src/nestegg.c b/media/libnestegg/src/nestegg.c index 2f52888db39b..f74ba04bc870 100644 --- a/media/libnestegg/src/nestegg.c +++ b/media/libnestegg/src/nestegg.c @@ -321,18 +321,9 @@ struct block_additional { struct block_additional * next; }; -#define NE_IO_BUFSZ 16384 - -struct nestegg_io_buf { - nestegg_io io; - unsigned char buffer[NE_IO_BUFSZ]; - size_t bufsz; - int offset; -}; - /* Public (opaque) Structures */ struct nestegg { - struct nestegg_io_buf * io; + nestegg_io * io; nestegg_log log; struct pool_ctx * alloc_pool; uint64_t last_id; @@ -555,99 +546,19 @@ ne_alloc(size_t size) } static int -ne_io_read(struct nestegg_io_buf * io, void * buffer, size_t length) +ne_io_read(nestegg_io * io, void * buffer, size_t length) { - int64_t off; - int r; - size_t avail; - - assert(io->offset == -1 || (io->offset >= 0 && (unsigned int) io->offset < io->bufsz)); - - /* Too big to buffer, invalidate buffer and read through */ - if (length > io->bufsz) { - if (io->offset != -1) { - r = io->io.seek(-(io->bufsz - io->offset), NESTEGG_SEEK_CUR, io->io.userdata); - if (r != 0) { - return -1; - } - } - io->offset = -1; - return io->io.read(buffer, length, io->io.userdata); - } - - /* Buffer invalid */ - if (io->offset == -1) { - off = io->io.tell(io->io.userdata); - if (off == -1) { - return -1; - } - /* Refill buffer */ - r = io->io.read(io->buffer, io->bufsz, io->io.userdata); - if (r != 1) { - /* Read truncated due to being within io->bufsz of EOS, reset read - position and switch to read through mode */ - io->offset = -1; - io->bufsz = 0; - if (r == 0) { - r = io->io.seek(off, NESTEGG_SEEK_SET, io->io.userdata); - } - if (r == 0) { - return io->io.read(buffer, length, io->io.userdata); - } - return -1; - } - if (r == 1) { - io->offset = 0; - } - } - - /* Service request with what we have */ - avail = length; - if (io->bufsz - io->offset < length) { - avail = io->bufsz - io->offset; - } - memcpy(buffer, io->buffer + io->offset, avail); - io->offset += avail; - - if ((unsigned int) io->offset == io->bufsz) { - io->offset = -1; - } - - /* Still more to read, invalidate buffer and read more */ - if (length - avail > 0) { - return ne_io_read(io, (char *) buffer + avail, length - avail); - } - - return 1; + return io->read(buffer, length, io->userdata); } static int -ne_io_seek(struct nestegg_io_buf * io, int64_t offset, int whence) +ne_io_seek(nestegg_io * io, int64_t offset, int whence) { - /* Invalidate buffer */ - io->offset = -1; - - return io->io.seek(offset, whence, io->io.userdata); -} - -static int64_t -ne_io_tell(struct nestegg_io_buf * io) -{ - int64_t off; - - off = io->io.tell(io->io.userdata); - if (off == -1) { - return -1; - } - if (io->offset == -1) { - return off; - } - assert(off >= (int64_t) io->bufsz - io->offset); - return off - io->bufsz + (unsigned int) io->offset; + return io->seek(offset, whence, io->userdata); } static int -ne_io_read_skip(struct nestegg_io_buf * io, size_t length) +ne_io_read_skip(nestegg_io * io, size_t length) { size_t get; unsigned char buf[8192]; @@ -664,8 +575,14 @@ ne_io_read_skip(struct nestegg_io_buf * io, size_t length) return r; } +static int64_t +ne_io_tell(nestegg_io * io) +{ + return io->tell(io->userdata); +} + static int -ne_bare_read_vint(struct nestegg_io_buf * io, uint64_t * value, uint64_t * length, enum vint_mask maskflag) +ne_bare_read_vint(nestegg_io * io, uint64_t * value, uint64_t * length, enum vint_mask maskflag) { int r; unsigned char b; @@ -702,19 +619,19 @@ ne_bare_read_vint(struct nestegg_io_buf * io, uint64_t * value, uint64_t * lengt } static int -ne_read_id(struct nestegg_io_buf * io, uint64_t * value, uint64_t * length) +ne_read_id(nestegg_io * io, uint64_t * value, uint64_t * length) { return ne_bare_read_vint(io, value, length, MASK_NONE); } static int -ne_read_vint(struct nestegg_io_buf * io, uint64_t * value, uint64_t * length) +ne_read_vint(nestegg_io * io, uint64_t * value, uint64_t * length) { return ne_bare_read_vint(io, value, length, MASK_FIRST_BIT); } static int -ne_read_svint(struct nestegg_io_buf * io, int64_t * value, uint64_t * length) +ne_read_svint(nestegg_io * io, int64_t * value, uint64_t * length) { int r; uint64_t uvalue; @@ -736,7 +653,7 @@ ne_read_svint(struct nestegg_io_buf * io, int64_t * value, uint64_t * length) } static int -ne_read_uint(struct nestegg_io_buf * io, uint64_t * val, uint64_t length) +ne_read_uint(nestegg_io * io, uint64_t * val, uint64_t length) { unsigned char b; int r; @@ -758,7 +675,7 @@ ne_read_uint(struct nestegg_io_buf * io, uint64_t * val, uint64_t length) } static int -ne_read_int(struct nestegg_io_buf * io, int64_t * val, uint64_t length) +ne_read_int(nestegg_io * io, int64_t * val, uint64_t length) { int r; uint64_t uval, base; @@ -771,8 +688,8 @@ ne_read_int(struct nestegg_io_buf * io, int64_t * val, uint64_t length) base = 1; base <<= length * 8 - 1; if (uval >= base) { - base = 1; - base <<= length * 8; + base = 1; + base <<= length * 8; } else { base = 0; } @@ -785,7 +702,7 @@ ne_read_int(struct nestegg_io_buf * io, int64_t * val, uint64_t length) } static int -ne_read_float(struct nestegg_io_buf * io, double * val, uint64_t length) +ne_read_float(nestegg_io * io, double * val, uint64_t length) { union { uint64_t u; @@ -819,9 +736,9 @@ ne_read_string(nestegg * ctx, char ** val, uint64_t length) if (!str) return -1; if (length) { - r = ne_io_read(ctx->io, (unsigned char *) str, length); - if (r != 1) - return r; + r = ne_io_read(ctx->io, (unsigned char *) str, length); + if (r != 1) + return r; } str[length] = '\0'; *val = str; @@ -1101,9 +1018,8 @@ ne_read_simple(nestegg * ctx, struct ebml_element_desc * desc, size_t length) break; case TYPE_MASTER: case TYPE_UNKNOWN: - default: - r = 0; assert(0); + r = 0; break; } @@ -1220,7 +1136,7 @@ ne_xiph_lace_value(unsigned char ** np) } static int -ne_read_xiph_lace_value(struct nestegg_io_buf * io, uint64_t * value, size_t * consumed) +ne_read_xiph_lace_value(nestegg_io * io, uint64_t * value, size_t * consumed) { int r; uint64_t lace; @@ -1243,7 +1159,7 @@ ne_read_xiph_lace_value(struct nestegg_io_buf * io, uint64_t * value, size_t * c } static int -ne_read_xiph_lacing(struct nestegg_io_buf * io, size_t block, size_t * read, uint64_t n, uint64_t * sizes) +ne_read_xiph_lacing(nestegg_io * io, size_t block, size_t * read, uint64_t n, uint64_t * sizes) { int r; size_t i = 0; @@ -1266,7 +1182,7 @@ ne_read_xiph_lacing(struct nestegg_io_buf * io, size_t block, size_t * read, uin } static int -ne_read_ebml_lacing(struct nestegg_io_buf * io, size_t block, size_t * read, uint64_t n, uint64_t * sizes) +ne_read_ebml_lacing(nestegg_io * io, size_t block, size_t * read, uint64_t n, uint64_t * sizes) { int r; uint64_t lace, sum, length; @@ -1876,9 +1792,9 @@ struct sniff_buffer { }; static int -ne_buffer_read(void * buffer, size_t length, void * userdata) +ne_buffer_read(void * buffer, size_t length, void * user_data) { - struct sniff_buffer * sb = userdata; + struct sniff_buffer * sb = user_data; int rv = 1; size_t available = sb->length - sb->offset; @@ -1893,21 +1809,21 @@ ne_buffer_read(void * buffer, size_t length, void * userdata) } static int -ne_buffer_seek(int64_t offset, int whence, void * userdata) +ne_buffer_seek(int64_t offset, int whence, void * user_data) { - struct sniff_buffer * sb = userdata; + struct sniff_buffer * sb = user_data; int64_t o = sb->offset; switch(whence) { - case NESTEGG_SEEK_SET: - o = offset; - break; - case NESTEGG_SEEK_CUR: - o += offset; - break; - case NESTEGG_SEEK_END: - o = sb->length + offset; - break; + case NESTEGG_SEEK_SET: + o = offset; + break; + case NESTEGG_SEEK_CUR: + o += offset; + break; + case NESTEGG_SEEK_END: + o = sb->length + offset; + break; } if (o < 0 || o > (int64_t) sb->length) @@ -1918,9 +1834,9 @@ ne_buffer_seek(int64_t offset, int whence, void * userdata) } static int64_t -ne_buffer_tell(void * userdata) +ne_buffer_tell(void * user_data) { - struct sniff_buffer * sb = userdata; + struct sniff_buffer * sb = user_data; return sb->offset; } @@ -1944,9 +1860,7 @@ ne_match_webm(nestegg_io io, int64_t max_offset) nestegg_destroy(ctx); return -1; } - ctx->io->io = io; - ctx->io->bufsz = NE_IO_BUFSZ; - ctx->io->offset = -1; + *ctx->io = io; ctx->alloc_pool = ne_pool_init(); if (!ctx->alloc_pool) { nestegg_destroy(ctx); @@ -2004,9 +1918,7 @@ nestegg_init(nestegg ** context, nestegg_io io, nestegg_log callback, int64_t ma nestegg_destroy(ctx); return -1; } - ctx->io->io = io; - ctx->io->bufsz = NE_IO_BUFSZ; - ctx->io->offset = -1; + *ctx->io = io; ctx->log = callback; ctx->alloc_pool = ne_pool_init(); if (!ctx->alloc_pool) { @@ -2352,35 +2264,35 @@ nestegg_track_codec_data(nestegg * ctx, unsigned int track, unsigned int item, return -1; if (nestegg_track_codec_id(ctx, track) != NESTEGG_CODEC_VORBIS - && nestegg_track_codec_id(ctx, track) != NESTEGG_CODEC_OPUS) + && nestegg_track_codec_id(ctx, track) != NESTEGG_CODEC_OPUS) return -1; if (ne_get_binary(entry->codec_private, &codec_private) != 0) return -1; if (nestegg_track_codec_id(ctx, track) == NESTEGG_CODEC_VORBIS) { - p = codec_private.data; - count = *p++ + 1; + p = codec_private.data; + count = *p++ + 1; - if (count > 3) - return -1; - - i = 0; - total = 0; - while (--count) { - sizes[i] = ne_xiph_lace_value(&p); - total += sizes[i]; - i += 1; - } - sizes[i] = codec_private.length - total - (p - codec_private.data); - - for (i = 0; i < item; ++i) { - if (sizes[i] > LIMIT_FRAME) + if (count > 3) return -1; - p += sizes[i]; - } - *data = p; - *length = sizes[item]; + + i = 0; + total = 0; + while (--count) { + sizes[i] = ne_xiph_lace_value(&p); + total += sizes[i]; + i += 1; + } + sizes[i] = codec_private.length - total - (p - codec_private.data); + + for (i = 0; i < item; ++i) { + if (sizes[i] > LIMIT_FRAME) + return -1; + p += sizes[i]; + } + *data = p; + *length = sizes[item]; } else { *data = codec_private.data; *length = codec_private.length; @@ -2582,7 +2494,7 @@ nestegg_free_packet(nestegg_packet * pkt) free(block_additional); } - free(pkt); + free(pkt); } int @@ -2676,31 +2588,28 @@ int nestegg_has_cues(nestegg * ctx) { return ctx->segment.cues.cue_point.head || - ne_find_seek_for_id(ctx->segment.seek_head.head, ID_CUES); + ne_find_seek_for_id(ctx->segment.seek_head.head, ID_CUES); } int nestegg_sniff(unsigned char const * buffer, size_t length) { nestegg_io io; - struct sniff_buffer userdata; + struct sniff_buffer user_data; - userdata.buffer = buffer; - userdata.length = length; - userdata.offset = 0; + user_data.buffer = buffer; + user_data.length = length; + user_data.offset = 0; io.read = ne_buffer_read; io.seek = ne_buffer_seek; io.tell = ne_buffer_tell; - io.userdata = &userdata; + io.userdata = &user_data; return ne_match_webm(io, length); } -/* From halloc.c */ -int halloc_set_allocator(realloc_t realloc_func); - -int +void nestegg_set_halloc_func(void * (* realloc_func)(void *, size_t)) { - return halloc_set_allocator(realloc_func); + halloc_allocator = realloc_func; } diff --git a/media/libnestegg/update.sh b/media/libnestegg/update.sh index 2d97f3eaec48..27d4ec58b4a6 100755 --- a/media/libnestegg/update.sh +++ b/media/libnestegg/update.sh @@ -7,7 +7,7 @@ cp $1/halloc/src/halloc.c src cp $1/halloc/src/hlist.h src cp $1/halloc/src/macros.h src cp $1/LICENSE . -cp $1/README.md . +cp $1/README . cp $1/AUTHORS . if [ -d $1/.git ]; then rev=$(cd $1 && git rev-parse --verify HEAD) From 87cc3a26429d44ad2eec0107dddc4f85fa28c159 Mon Sep 17 00:00:00 2001 From: Jonathan Kew Date: Tue, 30 Sep 2014 07:27:55 +0100 Subject: [PATCH 66/82] Bug 727276 - Use emoji-style variation selector to help GetCommonFallbackFonts make an appropriate choice. r=roc --- gfx/thebes/gfxAndroidPlatform.cpp | 7 +++- gfx/thebes/gfxAndroidPlatform.h | 2 +- gfx/thebes/gfxPangoFonts.cpp | 2 +- gfx/thebes/gfxPangoFonts.h | 4 +-- gfx/thebes/gfxPlatform.h | 2 +- gfx/thebes/gfxPlatformFontList.cpp | 10 +++--- gfx/thebes/gfxPlatformFontList.h | 6 ++-- gfx/thebes/gfxPlatformMac.cpp | 6 +++- gfx/thebes/gfxPlatformMac.h | 2 +- gfx/thebes/gfxTextRun.cpp | 45 +++++++++++++++++++------ gfx/thebes/gfxTextRun.h | 9 ++--- gfx/thebes/gfxWindowsPlatform.cpp | 18 +++++++--- gfx/thebes/gfxWindowsPlatform.h | 2 +- layout/generic/MathMLTextRunFactory.cpp | 2 +- 14 files changed, 81 insertions(+), 36 deletions(-) diff --git a/gfx/thebes/gfxAndroidPlatform.cpp b/gfx/thebes/gfxAndroidPlatform.cpp index 9190915a5abe..afd5446a0daa 100644 --- a/gfx/thebes/gfxAndroidPlatform.cpp +++ b/gfx/thebes/gfxAndroidPlatform.cpp @@ -173,13 +173,18 @@ IsJapaneseLocale() } void -gfxAndroidPlatform::GetCommonFallbackFonts(const uint32_t aCh, +gfxAndroidPlatform::GetCommonFallbackFonts(uint32_t aCh, uint32_t aNextCh, int32_t aRunScript, nsTArray& aFontList) { static const char kDroidSansJapanese[] = "Droid Sans Japanese"; static const char kMotoyaLMaru[] = "MotoyaLMaru"; + if (aNextCh == 0xfe0fu) { + // if char is followed by VS16, try for a color emoji glyph + aFontList.AppendElement("Noto Color Emoji"); + } + if (IS_IN_BMP(aCh)) { // try language-specific "Droid Sans *" and "Noto Sans *" fonts for // certain blocks, as most devices probably have these diff --git a/gfx/thebes/gfxAndroidPlatform.h b/gfx/thebes/gfxAndroidPlatform.h index b2abde0c4b2f..d4e49df2334b 100644 --- a/gfx/thebes/gfxAndroidPlatform.h +++ b/gfx/thebes/gfxAndroidPlatform.h @@ -58,7 +58,7 @@ public: const uint8_t* aFontData, uint32_t aLength); - virtual void GetCommonFallbackFonts(const uint32_t aCh, + virtual void GetCommonFallbackFonts(uint32_t aCh, uint32_t aNextCh, int32_t aRunScript, nsTArray& aFontList); diff --git a/gfx/thebes/gfxPangoFonts.cpp b/gfx/thebes/gfxPangoFonts.cpp index be3eb1c319e2..46d5180f0c92 100644 --- a/gfx/thebes/gfxPangoFonts.cpp +++ b/gfx/thebes/gfxPangoFonts.cpp @@ -1410,7 +1410,7 @@ gfxPangoFontGroup::GetFontSet(PangoLanguage *aLang) already_AddRefed gfxPangoFontGroup::FindFontForChar(uint32_t aCh, uint32_t aPrevCh, - int32_t aRunScript, + uint32_t aNextCh, int32_t aRunScript, gfxFont *aPrevMatchedFont, uint8_t *aMatchType) { diff --git a/gfx/thebes/gfxPangoFonts.h b/gfx/thebes/gfxPangoFonts.h index a8cad6353f99..d2a5b4e7c9c9 100644 --- a/gfx/thebes/gfxPangoFonts.h +++ b/gfx/thebes/gfxPangoFonts.h @@ -37,8 +37,8 @@ public: virtual void UpdateUserFonts(); virtual already_AddRefed - FindFontForChar(uint32_t aCh, uint32_t aPrevCh, int32_t aRunScript, - gfxFont *aPrevMatchedFont, + FindFontForChar(uint32_t aCh, uint32_t aPrevCh, uint32_t aNextCh, + int32_t aRunScript, gfxFont *aPrevMatchedFont, uint8_t *aMatchType); static void Shutdown(); diff --git a/gfx/thebes/gfxPlatform.h b/gfx/thebes/gfxPlatform.h index 9e351dc08191..c65751647929 100644 --- a/gfx/thebes/gfxPlatform.h +++ b/gfx/thebes/gfxPlatform.h @@ -449,7 +449,7 @@ public: // returns a list of commonly used fonts for a given character // these are *possible* matches, no cmap-checking is done at this level - virtual void GetCommonFallbackFonts(const uint32_t /*aCh*/, + virtual void GetCommonFallbackFonts(uint32_t /*aCh*/, uint32_t /*aNextCh*/, int32_t /*aRunScript*/, nsTArray& /*aFontList*/) { diff --git a/gfx/thebes/gfxPlatformFontList.cpp b/gfx/thebes/gfxPlatformFontList.cpp index f5c38fed0ec0..298c9befda7c 100644 --- a/gfx/thebes/gfxPlatformFontList.cpp +++ b/gfx/thebes/gfxPlatformFontList.cpp @@ -536,7 +536,7 @@ gfxPlatformFontList::GetFontFamilyList(nsTArray >& aFami } gfxFontEntry* -gfxPlatformFontList::SystemFindFontForChar(const uint32_t aCh, +gfxPlatformFontList::SystemFindFontForChar(uint32_t aCh, uint32_t aNextCh, int32_t aRunScript, const gfxFontStyle* aStyle) { @@ -570,7 +570,8 @@ gfxPlatformFontList::SystemFindFontForChar(const uint32_t aCh, // search commonly available fonts bool common = true; gfxFontFamily *fallbackFamily = nullptr; - fontEntry = CommonFontFallback(aCh, aRunScript, aStyle, &fallbackFamily); + fontEntry = CommonFontFallback(aCh, aNextCh, aRunScript, aStyle, + &fallbackFamily); // if didn't find a font, do system-wide fallback (except for specials) uint32_t cmapCount = 0; @@ -638,7 +639,7 @@ gfxPlatformFontList::FindFontForCharProc(nsStringHashKey::KeyType aKey, nsRefPtr #define NUM_FALLBACK_FONTS 8 gfxFontEntry* -gfxPlatformFontList::CommonFontFallback(const uint32_t aCh, +gfxPlatformFontList::CommonFontFallback(uint32_t aCh, uint32_t aNextCh, int32_t aRunScript, const gfxFontStyle* aMatchStyle, gfxFontFamily** aMatchedFamily) @@ -646,7 +647,8 @@ gfxPlatformFontList::CommonFontFallback(const uint32_t aCh, nsAutoTArray defaultFallbacks; uint32_t i, numFallbacks; - gfxPlatform::GetPlatform()->GetCommonFallbackFonts(aCh, aRunScript, + gfxPlatform::GetPlatform()->GetCommonFallbackFonts(aCh, aNextCh, + aRunScript, defaultFallbacks); numFallbacks = defaultFallbacks.Length(); for (i = 0; i < numFallbacks; i++) { diff --git a/gfx/thebes/gfxPlatformFontList.h b/gfx/thebes/gfxPlatformFontList.h index 5a5cda7331a6..a2ba11bd2120 100644 --- a/gfx/thebes/gfxPlatformFontList.h +++ b/gfx/thebes/gfxPlatformFontList.h @@ -120,8 +120,8 @@ public: virtual void GetFontFamilyList(nsTArray >& aFamilyArray); - virtual gfxFontEntry* - SystemFindFontForChar(const uint32_t aCh, + gfxFontEntry* + SystemFindFontForChar(uint32_t aCh, uint32_t aNextCh, int32_t aRunScript, const gfxFontStyle* aStyle); @@ -211,7 +211,7 @@ protected: void* userArg); // returns default font for a given character, null otherwise - gfxFontEntry* CommonFontFallback(const uint32_t aCh, + gfxFontEntry* CommonFontFallback(uint32_t aCh, uint32_t aNextCh, int32_t aRunScript, const gfxFontStyle* aMatchStyle, gfxFontFamily** aMatchedFamily); diff --git a/gfx/thebes/gfxPlatformMac.cpp b/gfx/thebes/gfxPlatformMac.cpp index b1211adc5df8..deba369b5fa3 100644 --- a/gfx/thebes/gfxPlatformMac.cpp +++ b/gfx/thebes/gfxPlatformMac.cpp @@ -229,10 +229,14 @@ static const char kFontSTIXGeneral[] = "STIXGeneral"; static const char kFontTamilMN[] = "Tamil MN"; void -gfxPlatformMac::GetCommonFallbackFonts(const uint32_t aCh, +gfxPlatformMac::GetCommonFallbackFonts(uint32_t aCh, uint32_t aNextCh, int32_t aRunScript, nsTArray& aFontList) { + if (aNextCh == 0xfe0f) { + aFontList.AppendElement(kFontAppleColorEmoji); + } + aFontList.AppendElement(kFontLucidaGrande); if (!IS_IN_BMP(aCh)) { diff --git a/gfx/thebes/gfxPlatformMac.h b/gfx/thebes/gfxPlatformMac.h index a2691d2bf53f..9427e42d3040 100644 --- a/gfx/thebes/gfxPlatformMac.h +++ b/gfx/thebes/gfxPlatformMac.h @@ -60,7 +60,7 @@ public: nsTArray& aListOfFonts); nsresult UpdateFontList(); - virtual void GetCommonFallbackFonts(const uint32_t aCh, + virtual void GetCommonFallbackFonts(uint32_t aCh, uint32_t aNextCh, int32_t aRunScript, nsTArray& aFontList); diff --git a/gfx/thebes/gfxTextRun.cpp b/gfx/thebes/gfxTextRun.cpp index 063a8dc93838..db04ba516970 100644 --- a/gfx/thebes/gfxTextRun.cpp +++ b/gfx/thebes/gfxTextRun.cpp @@ -1884,7 +1884,8 @@ gfxFontGroup::MakeSpaceTextRun(const Parameters *aParams, uint32_t aFlags) // find one that does. uint8_t matchType; nsRefPtr spaceFont = - FindFontForChar(' ', 0, MOZ_SCRIPT_LATIN, nullptr, &matchType); + FindFontForChar(' ', 0, 0, MOZ_SCRIPT_LATIN, nullptr, + &matchType); if (spaceFont) { textRun->SetSpaceGlyph(spaceFont, aParams->mContext, 0, orientation); @@ -2489,7 +2490,7 @@ gfxFontGroup::GetUnderlineOffset() } already_AddRefed -gfxFontGroup::FindFontForChar(uint32_t aCh, uint32_t aPrevCh, +gfxFontGroup::FindFontForChar(uint32_t aCh, uint32_t aPrevCh, uint32_t aNextCh, int32_t aRunScript, gfxFont *aPrevMatchedFont, uint8_t *aMatchType) { @@ -2649,7 +2650,7 @@ gfxFontGroup::FindFontForChar(uint32_t aCh, uint32_t aPrevCh, // -- otherwise look for other stuff *aMatchType = gfxTextRange::kSystemFallback; - font = WhichSystemFontSupportsChar(aCh, aRunScript); + font = WhichSystemFontSupportsChar(aCh, aNextCh, aRunScript); return font.forget(); } @@ -2662,6 +2663,13 @@ void gfxFontGroup::ComputeRanges(nsTArray& aRanges, NS_ASSERTION(aLength > 0, "don't call ComputeRanges for zero-length text"); uint32_t prevCh = 0; + uint32_t nextCh = aString[0]; + if (sizeof(T) == sizeof(char16_t)) { + if (aLength > 1 && NS_IS_HIGH_SURROGATE(nextCh) && + NS_IS_LOW_SURROGATE(aString[1])) { + nextCh = SURROGATE_TO_UCS4(nextCh, aString[1]); + } + } int32_t lastRangeIndex = -1; // initialize prevFont to the group's primary font, so that this will be @@ -2678,15 +2686,28 @@ void gfxFontGroup::ComputeRanges(nsTArray& aRanges, const uint32_t origI = i; // save off in case we increase for surrogate // set up current ch - uint32_t ch = aString[i]; + uint32_t ch = nextCh; + + // Get next char (if any) so that FindFontForChar can look ahead + // for a possible variation selector. - // in 16-bit case only, check for surrogate pair if (sizeof(T) == sizeof(char16_t)) { - if ((i + 1 < aLength) && NS_IS_HIGH_SURROGATE(ch) && - NS_IS_LOW_SURROGATE(aString[i + 1])) { + // In 16-bit case only, check for surrogate pairs. + if (ch > 0xffffu) { i++; - ch = SURROGATE_TO_UCS4(ch, aString[i]); } + if (i < aLength - 1) { + nextCh = aString[i + 1]; + if ((i + 2 < aLength) && NS_IS_HIGH_SURROGATE(nextCh) && + NS_IS_LOW_SURROGATE(aString[i + 2])) { + nextCh = SURROGATE_TO_UCS4(nextCh, aString[i + 2]); + } + } else { + nextCh = 0; + } + } else { + // 8-bit case is trivial. + nextCh = i < aLength - 1 ? aString[i + 1] : 0; } if (ch == 0xa0) { @@ -2695,7 +2716,8 @@ void gfxFontGroup::ComputeRanges(nsTArray& aRanges, // find the font for this char nsRefPtr font = - FindFontForChar(ch, prevCh, aRunScript, prevFont, &matchType); + FindFontForChar(ch, prevCh, nextCh, aRunScript, prevFont, + &matchType); #ifndef RELEASE_BUILD if (MOZ_UNLIKELY(mTextPerf)) { @@ -2931,11 +2953,12 @@ gfxFontGroup::WhichPrefFontSupportsChar(uint32_t aCh) } already_AddRefed -gfxFontGroup::WhichSystemFontSupportsChar(uint32_t aCh, int32_t aRunScript) +gfxFontGroup::WhichSystemFontSupportsChar(uint32_t aCh, uint32_t aNextCh, + int32_t aRunScript) { gfxFontEntry *fe = gfxPlatformFontList::PlatformFontList()-> - SystemFindFontForChar(aCh, aRunScript, &mStyle); + SystemFindFontForChar(aCh, aNextCh, aRunScript, &mStyle); if (fe) { bool wantBold = mStyle.ComputeWeight() >= 6; nsRefPtr font = diff --git a/gfx/thebes/gfxTextRun.h b/gfx/thebes/gfxTextRun.h index 54f1ff58d66c..ffbddc3b20f3 100644 --- a/gfx/thebes/gfxTextRun.h +++ b/gfx/thebes/gfxTextRun.h @@ -818,15 +818,16 @@ public: virtual gfxFloat GetUnderlineOffset(); virtual already_AddRefed - FindFontForChar(uint32_t ch, uint32_t prevCh, int32_t aRunScript, - gfxFont *aPrevMatchedFont, + FindFontForChar(uint32_t ch, uint32_t prevCh, uint32_t aNextCh, + int32_t aRunScript, gfxFont *aPrevMatchedFont, uint8_t *aMatchType); // search through pref fonts for a character, return nullptr if no matching pref font virtual already_AddRefed WhichPrefFontSupportsChar(uint32_t aCh); - virtual already_AddRefed - WhichSystemFontSupportsChar(uint32_t aCh, int32_t aRunScript); + already_AddRefed + WhichSystemFontSupportsChar(uint32_t aCh, uint32_t aNextCh, + int32_t aRunScript); template void ComputeRanges(nsTArray& mRanges, diff --git a/gfx/thebes/gfxWindowsPlatform.cpp b/gfx/thebes/gfxWindowsPlatform.cpp index c606674ccec6..18d7129dd795 100644 --- a/gfx/thebes/gfxWindowsPlatform.cpp +++ b/gfx/thebes/gfxWindowsPlatform.cpp @@ -725,18 +725,29 @@ static const char kFontUtsaah[] = "Utsaah"; static const char kFontYuGothic[] = "Yu Gothic"; void -gfxWindowsPlatform::GetCommonFallbackFonts(const uint32_t aCh, +gfxWindowsPlatform::GetCommonFallbackFonts(uint32_t aCh, uint32_t aNextCh, int32_t aRunScript, nsTArray& aFontList) { + if (aNextCh == 0xfe0fu) { + aFontList.AppendElement(kFontSegoeUIEmoji); + } + // Arial is used as the default fallback for system fallback aFontList.AppendElement(kFontArial); if (!IS_IN_BMP(aCh)) { uint32_t p = aCh >> 16; if (p == 1) { // SMP plane - aFontList.AppendElement(kFontSegoeUIEmoji); - aFontList.AppendElement(kFontSegoeUISymbol); + if (aNextCh == 0xfe0eu) { + aFontList.AppendElement(kFontSegoeUISymbol); + aFontList.AppendElement(kFontSegoeUIEmoji); + } else { + if (aNextCh != 0xfe0fu) { + aFontList.AppendElement(kFontSegoeUIEmoji); + } + aFontList.AppendElement(kFontSegoeUISymbol); + } aFontList.AppendElement(kFontEbrima); aFontList.AppendElement(kFontNirmalaUI); aFontList.AppendElement(kFontCambriaMath); @@ -814,7 +825,6 @@ gfxWindowsPlatform::GetCommonFallbackFonts(const uint32_t aCh, case 0x2b: case 0x2c: aFontList.AppendElement(kFontSegoeUI); - aFontList.AppendElement(kFontSegoeUIEmoji); aFontList.AppendElement(kFontSegoeUISymbol); aFontList.AppendElement(kFontCambria); aFontList.AppendElement(kFontMeiryo); diff --git a/gfx/thebes/gfxWindowsPlatform.h b/gfx/thebes/gfxWindowsPlatform.h index 6399aaf9c4d6..1bf6db6059a7 100644 --- a/gfx/thebes/gfxWindowsPlatform.h +++ b/gfx/thebes/gfxWindowsPlatform.h @@ -186,7 +186,7 @@ public: nsresult UpdateFontList(); - virtual void GetCommonFallbackFonts(const uint32_t aCh, + virtual void GetCommonFallbackFonts(uint32_t aCh, uint32_t aNextCh, int32_t aRunScript, nsTArray& aFontList); diff --git a/layout/generic/MathMLTextRunFactory.cpp b/layout/generic/MathMLTextRunFactory.cpp index 48f8cb24a576..f13cec0c4d74 100644 --- a/layout/generic/MathMLTextRunFactory.cpp +++ b/layout/generic/MathMLTextRunFactory.cpp @@ -669,7 +669,7 @@ MathMLTextRunFactory::RebuildTextRun(nsTransformedTextRun* aTextRun, // character is actually available. uint8_t matchType; nsRefPtr mathFont = fontGroup-> - FindFontForChar(ch2, 0, HB_SCRIPT_COMMON, nullptr, &matchType); + FindFontForChar(ch2, 0, 0, HB_SCRIPT_COMMON, nullptr, &matchType); if (mathFont) { // Don't apply the CSS style if there is a math font for at least one // of the transformed character in this text run. From 80eb7570079cc04a7572888f58449244d1919125 Mon Sep 17 00:00:00 2001 From: Jonathan Kew Date: Tue, 30 Sep 2014 07:28:28 +0100 Subject: [PATCH 67/82] Bug 727276 - Reftests for emoji-style variation selectors. r=roc --- layout/reftests/text/emoji-03-notref.html | 19 +++++++++++++++++++ layout/reftests/text/emoji-03-ref.html | 19 +++++++++++++++++++ layout/reftests/text/emoji-03.html | 19 +++++++++++++++++++ layout/reftests/text/emoji-04-ref.html | 20 ++++++++++++++++++++ layout/reftests/text/emoji-04.html | 20 ++++++++++++++++++++ layout/reftests/text/emoji-05-notref.html | 20 ++++++++++++++++++++ layout/reftests/text/emoji-05.html | 20 ++++++++++++++++++++ layout/reftests/text/reftest.list | 8 ++++++++ 8 files changed, 145 insertions(+) create mode 100644 layout/reftests/text/emoji-03-notref.html create mode 100644 layout/reftests/text/emoji-03-ref.html create mode 100644 layout/reftests/text/emoji-03.html create mode 100644 layout/reftests/text/emoji-04-ref.html create mode 100644 layout/reftests/text/emoji-04.html create mode 100644 layout/reftests/text/emoji-05-notref.html create mode 100644 layout/reftests/text/emoji-05.html diff --git a/layout/reftests/text/emoji-03-notref.html b/layout/reftests/text/emoji-03-notref.html new file mode 100644 index 000000000000..56a521afa136 --- /dev/null +++ b/layout/reftests/text/emoji-03-notref.html @@ -0,0 +1,19 @@ + + + + +Emoji rendering should be affected by VS15/VS16 + + + +
+❤️ +
+ + diff --git a/layout/reftests/text/emoji-03-ref.html b/layout/reftests/text/emoji-03-ref.html new file mode 100644 index 000000000000..b82fbe9f80e5 --- /dev/null +++ b/layout/reftests/text/emoji-03-ref.html @@ -0,0 +1,19 @@ + + + + +Emoji rendering should be affected by VS15/VS16 + + + +
+❤︎ +
+ + diff --git a/layout/reftests/text/emoji-03.html b/layout/reftests/text/emoji-03.html new file mode 100644 index 000000000000..7c2d71de4fd7 --- /dev/null +++ b/layout/reftests/text/emoji-03.html @@ -0,0 +1,19 @@ + + + + +Emoji rendering should be affected by VS15/VS16 + + + +
+❤ +
+ + diff --git a/layout/reftests/text/emoji-04-ref.html b/layout/reftests/text/emoji-04-ref.html new file mode 100644 index 000000000000..354df991e6ac --- /dev/null +++ b/layout/reftests/text/emoji-04-ref.html @@ -0,0 +1,20 @@ + + + + +Emoji-style glyph should ignore color + + + +
+❤️ +
+ + diff --git a/layout/reftests/text/emoji-04.html b/layout/reftests/text/emoji-04.html new file mode 100644 index 000000000000..87a02555a1c1 --- /dev/null +++ b/layout/reftests/text/emoji-04.html @@ -0,0 +1,20 @@ + + + + +Emoji-style glyph should ignore color + + + +
+❤️ +
+ + diff --git a/layout/reftests/text/emoji-05-notref.html b/layout/reftests/text/emoji-05-notref.html new file mode 100644 index 000000000000..574d30a6b112 --- /dev/null +++ b/layout/reftests/text/emoji-05-notref.html @@ -0,0 +1,20 @@ + + + + +Text-style glyph should respect color + + + +
+❤︎ +
+ + diff --git a/layout/reftests/text/emoji-05.html b/layout/reftests/text/emoji-05.html new file mode 100644 index 000000000000..7401d1c1bca7 --- /dev/null +++ b/layout/reftests/text/emoji-05.html @@ -0,0 +1,20 @@ + + + + +Text-style glyph should respect color + + + +
+❤︎ +
+ + diff --git a/layout/reftests/text/reftest.list b/layout/reftests/text/reftest.list index 71d2c575bd15..8e4551eeb5a0 100644 --- a/layout/reftests/text/reftest.list +++ b/layout/reftests/text/reftest.list @@ -176,6 +176,14 @@ skip-if(B2G) == 726392-3.html 726392-3-ref.html != emoji-01.html emoji-01-notref.html != emoji-02.html emoji-02-notref.html +# Bug 727276: tests with variation selectors 15 and 16 to control emoji rendering style +== emoji-03.html emoji-03-ref.html +# the next two will fail on OS X 10.6 and on Windows prior to 8.1 because no color emoji font is present, +# and also on Linux/Android/B2G platforms until we have color emoji fonts there +fails-if(OSX==10.6||/^Windows\x20NT\x20(5|6\.[0-2])/.test(http.oscpu)||gtk2Widget||Android||B2G) != emoji-03.html emoji-03-notref.html +fails-if(OSX==10.6||/^Windows\x20NT\x20(5|6\.[0-2])/.test(http.oscpu)||gtk2Widget||Android||B2G) == emoji-04.html emoji-04-ref.html +!= emoji-05.html emoji-05-notref.html + # check that Graphite shaping (bug 631479) is working pref(gfx.font_rendering.graphite.enabled,true) HTTP(..) == graphite-01.html graphite-01-ref.html # Test 02 (using Pig Latin) is fuzzy on Win7 because glyph positioning is not guaranteed to match exactly From 55f3a61af322ccc13c5a22ffe96aa27f3e8e543c Mon Sep 17 00:00:00 2001 From: Jonathan Kew Date: Tue, 30 Sep 2014 07:37:40 +0100 Subject: [PATCH 68/82] Bug 1065002 pt 1.1 - Rename gfxFont::GetMetrics to GetHorizontalMetrics, and add a GetMetrics wrapper to access it. r=jdaggett --- gfx/thebes/gfxDWriteFonts.cpp | 2 +- gfx/thebes/gfxDWriteFonts.h | 6 +++--- gfx/thebes/gfxFT2FontBase.cpp | 4 ++-- gfx/thebes/gfxFT2FontBase.h | 4 +++- gfx/thebes/gfxFont.h | 10 ++++++++-- gfx/thebes/gfxGDIFont.cpp | 2 +- gfx/thebes/gfxGDIFont.h | 4 ++-- gfx/thebes/gfxMacFont.h | 8 ++++---- 8 files changed, 24 insertions(+), 16 deletions(-) diff --git a/gfx/thebes/gfxDWriteFonts.cpp b/gfx/thebes/gfxDWriteFonts.cpp index 117c36afcddb..c15ada25df8a 100644 --- a/gfx/thebes/gfxDWriteFonts.cpp +++ b/gfx/thebes/gfxDWriteFonts.cpp @@ -126,7 +126,7 @@ gfxDWriteFont::CopyWithAntialiasOption(AntialiasOption anAAOption) } const gfxFont::Metrics& -gfxDWriteFont::GetMetrics() +gfxDWriteFont::GetHorizontalMetrics() { return *mMetrics; } diff --git a/gfx/thebes/gfxDWriteFonts.h b/gfx/thebes/gfxDWriteFonts.h index a2adea8ebf2b..5cf89ddfaa09 100644 --- a/gfx/thebes/gfxDWriteFonts.h +++ b/gfx/thebes/gfxDWriteFonts.h @@ -30,8 +30,6 @@ public: virtual gfxFont* CopyWithAntialiasOption(AntialiasOption anAAOption); - virtual const gfxFont::Metrics& GetMetrics(); - virtual uint32_t GetSpaceGlyph(); virtual bool SetupCairoFont(gfxContext *aContext); @@ -71,6 +69,8 @@ public: virtual cairo_scaled_font_t *GetCairoScaledFont(); protected: + virtual const Metrics& GetHorizontalMetrics(); + bool GetFakeMetricsForArialBlack(DWRITE_FONT_METRICS *aFontMetrics); void ComputeMetrics(AntialiasOption anAAOption); @@ -87,7 +87,7 @@ protected: nsRefPtr mFontFace; cairo_font_face_t *mCairoFontFace; - gfxFont::Metrics *mMetrics; + Metrics *mMetrics; // cache of glyph widths in 16.16 fixed-point pixels nsAutoPtr > mGlyphWidths; diff --git a/gfx/thebes/gfxFT2FontBase.cpp b/gfx/thebes/gfxFT2FontBase.cpp index 07c4aea861e3..13562936a6a0 100644 --- a/gfx/thebes/gfxFT2FontBase.cpp +++ b/gfx/thebes/gfxFT2FontBase.cpp @@ -108,7 +108,7 @@ gfxFT2FontBase::GetGlyphExtents(uint32_t aGlyph, cairo_text_extents_t* aExtents) } const gfxFont::Metrics& -gfxFT2FontBase::GetMetrics() +gfxFT2FontBase::GetHorizontalMetrics() { if (mHasMetrics) return mMetrics; @@ -146,7 +146,7 @@ gfxFT2FontBase::GetSpaceGlyph() { NS_ASSERTION(GetStyle()->size != 0, "forgot to short-circuit a text run with zero-sized font?"); - GetMetrics(); + GetHorizontalMetrics(); return mSpaceGlyph; } diff --git a/gfx/thebes/gfxFT2FontBase.h b/gfx/thebes/gfxFT2FontBase.h index 58bc60661bae..c8d53b4999fd 100644 --- a/gfx/thebes/gfxFT2FontBase.h +++ b/gfx/thebes/gfxFT2FontBase.h @@ -21,7 +21,6 @@ public: uint32_t GetGlyph(uint32_t aCharCode); void GetGlyphExtents(uint32_t aGlyph, cairo_text_extents_t* aExtents); - virtual const gfxFont::Metrics& GetMetrics(); virtual uint32_t GetSpaceGlyph(); virtual bool ProvidesGetGlyph() const { return true; } virtual uint32_t GetGlyph(uint32_t unicode, uint32_t variation_selector); @@ -34,7 +33,10 @@ public: virtual FontType GetType() const { return FONT_TYPE_FT2; } mozilla::gfx::FontOptions* GetFontOptions() { return &mFontOptions; } + protected: + virtual const Metrics& GetHorizontalMetrics(); + uint32_t mSpaceGlyph; bool mHasMetrics; Metrics mMetrics; diff --git a/gfx/thebes/gfxFont.h b/gfx/thebes/gfxFont.h index 1bee7e5a46ae..4ea1e71da30b 100644 --- a/gfx/thebes/gfxFont.h +++ b/gfx/thebes/gfxFont.h @@ -1441,7 +1441,11 @@ public: // no '0' glyph in this font, // equal to .aveCharWidth }; - virtual const gfxFont::Metrics& GetMetrics() = 0; + + const Metrics& GetMetrics() + { + return GetHorizontalMetrics(); + } /** * We let layout specify spacing on either side of any @@ -1735,6 +1739,8 @@ public: GetSubSuperscriptFont(int32_t aAppUnitsPerDevPixel); protected: + virtual const Metrics& GetHorizontalMetrics() = 0; + // Output a single glyph at *aPt, which is updated by the glyph's advance. // Normal glyphs are simply accumulated in aBuffer until it is full and // gets flushed, but SVG or color-font glyphs will instead be rendered @@ -1988,7 +1994,7 @@ protected: // some fonts have bad metrics, this method sanitize them. // if this font has bad underline offset, aIsBadUnderlineFont should be true. - void SanitizeMetrics(gfxFont::Metrics *aMetrics, bool aIsBadUnderlineFont); + void SanitizeMetrics(Metrics *aMetrics, bool aIsBadUnderlineFont); bool RenderSVGGlyph(gfxContext *aContext, gfxPoint aPoint, DrawMode aDrawMode, uint32_t aGlyphId, gfxTextContextPaint *aContextPaint) const; diff --git a/gfx/thebes/gfxGDIFont.cpp b/gfx/thebes/gfxGDIFont.cpp index 3472d70c000a..70c02a2da3be 100644 --- a/gfx/thebes/gfxGDIFont.cpp +++ b/gfx/thebes/gfxGDIFont.cpp @@ -106,7 +106,7 @@ gfxGDIFont::ShapeText(gfxContext *aContext, } const gfxFont::Metrics& -gfxGDIFont::GetMetrics() +gfxGDIFont::GetHorizontalMetrics() { if (!mMetrics) { Initialize(); diff --git a/gfx/thebes/gfxGDIFont.h b/gfx/thebes/gfxGDIFont.h index c56cb17f98ff..4b5e15a3226c 100644 --- a/gfx/thebes/gfxGDIFont.h +++ b/gfx/thebes/gfxGDIFont.h @@ -34,8 +34,6 @@ public: cairo_scaled_font_t *CairoScaledFont() { return mScaledFont; } /* overrides for the pure virtual methods in gfxFont */ - virtual const gfxFont::Metrics& GetMetrics(); - virtual uint32_t GetSpaceGlyph(); virtual bool SetupCairoFont(gfxContext *aContext); @@ -71,6 +69,8 @@ public: virtual FontType GetType() const { return FONT_TYPE_GDI; } protected: + virtual const Metrics& GetHorizontalMetrics(); + /* override to ensure the cairo font is set up properly */ virtual bool ShapeText(gfxContext *aContext, const char16_t *aText, diff --git a/gfx/thebes/gfxMacFont.h b/gfx/thebes/gfxMacFont.h index 3942213c0393..565797636d01 100644 --- a/gfx/thebes/gfxMacFont.h +++ b/gfx/thebes/gfxMacFont.h @@ -24,10 +24,6 @@ public: CGFontRef GetCGFontRef() const { return mCGFont; } /* overrides for the pure virtual methods in gfxFont */ - virtual const gfxFont::Metrics& GetMetrics() { - return mMetrics; - } - virtual uint32_t GetSpaceGlyph() { return mSpaceGlyph; } @@ -51,6 +47,10 @@ public: virtual FontType GetType() const { return FONT_TYPE_MAC; } protected: + virtual const Metrics& GetHorizontalMetrics() { + return mMetrics; + } + // override to prefer CoreText shaping with fonts that depend on AAT virtual bool ShapeText(gfxContext *aContext, const char16_t *aText, From af4fdc577c799f3801b7e8405acfe1b1dec1e5a3 Mon Sep 17 00:00:00 2001 From: Jonathan Kew Date: Tue, 30 Sep 2014 07:38:06 +0100 Subject: [PATCH 69/82] Bug 1065002 pt 1.2 - Add CreateVerticalMetrics to gfxFont, to read/synthesize metrics for vertical layout. r=jdaggett --- gfx/thebes/gfxFont.cpp | 149 +++++++++++++++++++++++++++++++++++++++++ gfx/thebes/gfxFont.h | 5 ++ 2 files changed, 154 insertions(+) diff --git a/gfx/thebes/gfxFont.cpp b/gfx/thebes/gfxFont.cpp index b83be18fc6b1..9f3cc504992c 100644 --- a/gfx/thebes/gfxFont.cpp +++ b/gfx/thebes/gfxFont.cpp @@ -3080,6 +3080,9 @@ gfxFont::InitMetricsFromSfntTables(Metrics& aMetrics) } } +#undef SET_SIGNED +#undef SET_UNSIGNED + mIsValid = true; return true; @@ -3205,6 +3208,152 @@ gfxFont::SanitizeMetrics(gfxFont::Metrics *aMetrics, bool aIsBadUnderlineFont) } } +// Create a Metrics record to be used for vertical layout. This should never +// fail, as we've already decided this is a valid font. We do not have the +// option of marking it invalid (as can happen if we're unable to read +// horizontal metrics), because that could break a font that we're already +// using for horizontal text. +// So we will synthesize *something* usable here even if there aren't any of the +// usual font tables (which can happen in the case of a legacy bitmap or Type1 +// font for which the platform-specific backend used platform APIs instead of +// sfnt tables to create the horizontal metrics). +const gfxFont::Metrics* +gfxFont::CreateVerticalMetrics() +{ + const uint32_t kHheaTableTag = TRUETYPE_TAG('h','h','e','a'); + const uint32_t kVheaTableTag = TRUETYPE_TAG('v','h','e','a'); + const uint32_t kPostTableTag = TRUETYPE_TAG('p','o','s','t'); + const uint32_t kOS_2TableTag = TRUETYPE_TAG('O','S','/','2'); + uint32_t len; + + Metrics* metrics = new Metrics; + ::memset(metrics, 0, sizeof(Metrics)); + + // Some basic defaults, in case the font lacks any real metrics tables. + // TODO: consider what rounding (if any) we should apply to these. + metrics->emHeight = GetAdjustedSize(); + metrics->emAscent = metrics->emHeight / 2; + metrics->emDescent = metrics->emHeight - metrics->emAscent; + + metrics->maxAscent = metrics->emAscent; + metrics->maxDescent = metrics->emDescent; + + const float UNINITIALIZED_LEADING = -10000.0f; + metrics->externalLeading = UNINITIALIZED_LEADING; + +#define SET_UNSIGNED(field,src) metrics->field = uint16_t(src) * mFUnitsConvFactor +#define SET_SIGNED(field,src) metrics->field = int16_t(src) * mFUnitsConvFactor + + gfxFontEntry::AutoTable os2Table(mFontEntry, kOS_2TableTag); + if (os2Table) { + const OS2Table *os2 = + reinterpret_cast(hb_blob_get_data(os2Table, &len)); + // These fields should always be present in any valid OS/2 table + if (len >= offsetof(OS2Table, sTypoLineGap) + sizeof(int16_t)) { + SET_SIGNED(strikeoutSize, os2->yStrikeoutSize); + SET_SIGNED(aveCharWidth, int16_t(os2->sTypoAscender) - + int16_t(os2->sTypoDescender)); + metrics->maxAscent = + std::max(metrics->maxAscent, int16_t(os2->xAvgCharWidth) * + gfxFloat(mFUnitsConvFactor)); + metrics->maxDescent = + std::max(metrics->maxDescent, int16_t(os2->xAvgCharWidth) * + gfxFloat(mFUnitsConvFactor)); + } + } + + // If we didn't set aveCharWidth from OS/2, try to read 'hhea' metrics + // and use the line height from its ascent/descent. + if (!metrics->aveCharWidth) { + gfxFontEntry::AutoTable hheaTable(mFontEntry, kHheaTableTag); + if (hheaTable) { + const HheaTable* hhea = + reinterpret_cast(hb_blob_get_data(hheaTable, + &len)); + if (len >= sizeof(HheaTable)) { + SET_SIGNED(aveCharWidth, int16_t(hhea->ascender) - + int16_t(hhea->descender)); + metrics->maxAscent = metrics->aveCharWidth / 2; + metrics->maxDescent = + metrics->aveCharWidth - metrics->maxAscent; + } + } + } + + // Read real vertical metrics if available. + gfxFontEntry::AutoTable vheaTable(mFontEntry, kVheaTableTag); + if (vheaTable) { + const HheaTable* vhea = + reinterpret_cast(hb_blob_get_data(vheaTable, + &len)); + if (len >= sizeof(HheaTable)) { + SET_UNSIGNED(maxAdvance, vhea->advanceWidthMax); + SET_SIGNED(maxAscent, vhea->ascender); + SET_SIGNED(maxDescent, -int16_t(vhea->descender)); + SET_SIGNED(externalLeading, vhea->lineGap); + } + } + + // If we didn't set aveCharWidth above, we must be dealing with a non-sfnt + // font of some kind (Type1, bitmap, vector, ...), so fall back to using + // whatever the platform backend figured out for horizontal layout. + // And if we haven't set externalLeading yet, then copy that from the + // horizontal metrics as well, to help consistency of CSS line-height. + if (!metrics->aveCharWidth || + metrics->externalLeading == UNINITIALIZED_LEADING) { + const Metrics& horizMetrics = GetHorizontalMetrics(); + if (!metrics->aveCharWidth) { + metrics->aveCharWidth = horizMetrics.maxAscent + horizMetrics.maxDescent; + } + if (metrics->externalLeading == UNINITIALIZED_LEADING) { + metrics->externalLeading = horizMetrics.externalLeading; + } + } + + // Get underline thickness from the 'post' table if available. + gfxFontEntry::AutoTable postTable(mFontEntry, kPostTableTag); + if (postTable) { + const PostTable *post = + reinterpret_cast(hb_blob_get_data(postTable, + &len)); + if (len >= offsetof(PostTable, underlineThickness) + + sizeof(uint16_t)) { + SET_UNSIGNED(underlineSize, post->underlineThickness); + // Also use for strikeout if we didn't find that in OS/2 above. + if (!metrics->strikeoutSize) { + metrics->strikeoutSize = metrics->underlineSize; + } + } + } + +#undef SET_UNSIGNED +#undef SET_SIGNED + + // If we didn't read this from a vhea table, it will still be zero. + // In any case, let's make sure it is not less than the value we've + // come up with for aveCharWidth. + metrics->maxAdvance = std::max(metrics->maxAdvance, metrics->aveCharWidth); + + // Thickness of underline and strikeout may have been read from tables, + // but in case they were not present, ensure a minimum of 1 pixel. + // We synthesize our own positions, as font metrics don't provide these + // for vertical layout. + metrics->underlineSize = std::max(1.0, metrics->underlineSize); + metrics->underlineOffset = 0; // XXX to be adjusted + + metrics->strikeoutSize = std::max(1.0, metrics->strikeoutSize); + metrics->strikeoutOffset = + metrics->maxDescent - 0.5 * metrics->strikeoutSize; + + // Somewhat arbitrary values for now, subject to future refinement... + metrics->spaceWidth = metrics->aveCharWidth; + metrics->zeroOrAveCharWidth = metrics->aveCharWidth; + metrics->maxHeight = metrics->maxAscent + metrics->maxDescent; + metrics->xHeight = metrics->emHeight / 2; + + return metrics; +} + gfxFloat gfxFont::SynthesizeSpaceWidth(uint32_t aCh) { diff --git a/gfx/thebes/gfxFont.h b/gfx/thebes/gfxFont.h index 4ea1e71da30b..55f193bbe833 100644 --- a/gfx/thebes/gfxFont.h +++ b/gfx/thebes/gfxFont.h @@ -1741,6 +1741,8 @@ public: protected: virtual const Metrics& GetHorizontalMetrics() = 0; + const Metrics* CreateVerticalMetrics(); + // Output a single glyph at *aPt, which is updated by the glyph's advance. // Normal glyphs are simply accumulated in aBuffer until it is full and // gets flushed, but SVG or color-font glyphs will instead be rendered @@ -1978,6 +1980,9 @@ protected: mozilla::RefPtr mAzureScaledFont; + // For vertical metrics, created on demand. + nsAutoPtr mVerticalMetrics; + // Helper for subclasses that want to initialize standard metrics from the // tables of sfnt (TrueType/OpenType) fonts. // This will use mFUnitsConvFactor if it is already set, else compute it From c5e1a54d597852fe84b95b69b08fa58d0ad8ae55 Mon Sep 17 00:00:00 2001 From: Jonathan Kew Date: Tue, 30 Sep 2014 07:38:26 +0100 Subject: [PATCH 70/82] Bug 1065002 pt 1.3 - Add an Orientation parameter to gfxFont::GetMetrics and dispatch to horizontal or vertical as needed. r=jdaggett --- dom/canvas/CanvasRenderingContext2D.cpp | 2 +- gfx/src/nsFontMetrics.cpp | 2 +- gfx/thebes/gfxFont.cpp | 21 +++++++++++++-------- gfx/thebes/gfxFont.h | 18 +++++++++++++++--- gfx/thebes/gfxGlyphExtents.cpp | 4 +++- gfx/thebes/gfxGlyphExtents.h | 7 ++++--- gfx/thebes/gfxPangoFonts.cpp | 3 ++- gfx/thebes/gfxTextRun.cpp | 23 +++++++++++++++++------ layout/generic/nsTextFrame.cpp | 8 +++++--- layout/style/nsRuleNode.cpp | 5 +++-- 10 files changed, 64 insertions(+), 29 deletions(-) diff --git a/dom/canvas/CanvasRenderingContext2D.cpp b/dom/canvas/CanvasRenderingContext2D.cpp index 0c1a96d8324b..d2ff09b235f6 100644 --- a/dom/canvas/CanvasRenderingContext2D.cpp +++ b/dom/canvas/CanvasRenderingContext2D.cpp @@ -3443,7 +3443,7 @@ CanvasRenderingContext2D::DrawOrMeasureText(const nsAString& aRawText, // offset pt.y based on text baseline processor.mFontgrp->UpdateUserFonts(); // ensure user font generation is current const gfxFont::Metrics& fontMetrics = - processor.mFontgrp->GetFirstValidFont()->GetMetrics(); + processor.mFontgrp->GetFirstValidFont()->GetMetrics(gfxFont::eHorizontal); // XXX vertical? gfxFloat anchorY; diff --git a/gfx/src/nsFontMetrics.cpp b/gfx/src/nsFontMetrics.cpp index 1e1a902b4acf..61873a5267f2 100644 --- a/gfx/src/nsFontMetrics.cpp +++ b/gfx/src/nsFontMetrics.cpp @@ -147,7 +147,7 @@ nsFontMetrics::Destroy() const gfxFont::Metrics& nsFontMetrics::GetMetrics() const { - return mFontGroup->GetFirstValidFont()->GetMetrics(); + return mFontGroup->GetFirstValidFont()->GetMetrics(gfxFont::eHorizontal); // XXX vertical? } nscoord diff --git a/gfx/thebes/gfxFont.cpp b/gfx/thebes/gfxFont.cpp index 9f3cc504992c..dbd0fedb3f19 100644 --- a/gfx/thebes/gfxFont.cpp +++ b/gfx/thebes/gfxFont.cpp @@ -658,7 +658,7 @@ gfxShapedText::SetMissingGlyph(uint32_t aIndex, uint32_t aChar, gfxFont *aFont) details->mAdvance = 0; } else { gfxFloat width = - std::max(aFont->GetMetrics().aveCharWidth, + std::max(aFont->GetMetrics(gfxFont::eHorizontal).aveCharWidth, gfxFontMissingGlyphs::GetDesiredMinWidth(aChar, mAppUnitsPerDevUnit)); details->mAdvance = uint32_t(width * mAppUnitsPerDevUnit); @@ -788,7 +788,7 @@ gfxFont::GetGlyphHAdvance(gfxContext *aCtx, uint16_t aGID) return GetGlyphWidth(aCtx, aGID) / 65536.0; } if (mFUnitsConvFactor == 0.0f) { - GetMetrics(); + GetMetrics(eHorizontal); } NS_ASSERTION(mFUnitsConvFactor > 0.0f, "missing font unit conversion factor"); @@ -1780,7 +1780,7 @@ gfxFont::DrawGlyphs(gfxShapedText *aShapedText, ToDeviceUnits(aPt->y, aRunParams.devPerApp)); gfxFloat advanceDevUnits = ToDeviceUnits(advance, aRunParams.devPerApp); - gfxFloat height = GetMetrics().maxAscent; + gfxFloat height = GetMetrics(eHorizontal).maxAscent; gfxRect glyphRect(pt.x, pt.y - height, advanceDevUnits, height); @@ -2059,7 +2059,9 @@ gfxFont::Measure(gfxTextRun *aTextRun, const int32_t appUnitsPerDevUnit = aTextRun->GetAppUnitsPerDevUnit(); // Current position in appunits - const gfxFont::Metrics& fontMetrics = GetMetrics(); + gfxFont::Orientation orientation = + aTextRun->IsVertical() ? gfxFont::eVertical : gfxFont::eHorizontal; + const gfxFont::Metrics& fontMetrics = GetMetrics(orientation); RunMetrics metrics; metrics.mAscent = fontMetrics.maxAscent*appUnitsPerDevUnit; @@ -2103,6 +2105,7 @@ gfxFont::Measure(gfxTextRun *aTextRun, } else { gfxRect glyphRect; if (!extents->GetTightGlyphExtentsAppUnits(this, + orientation, aRefContext, glyphIndex, &glyphRect)) { glyphRect = gfxRect(0, metrics.mBoundingBox.Y(), advance, metrics.mBoundingBox.Height()); @@ -2130,6 +2133,7 @@ gfxFont::Measure(gfxTextRun *aTextRun, gfxRect glyphRect; if (glyphData->IsMissing() || !extents || !extents->GetTightGlyphExtentsAppUnits(this, + orientation, aRefContext, glyphIndex, &glyphRect)) { // We might have failed to get glyph extents due to // OOM or something @@ -2937,7 +2941,8 @@ gfxFont::GetOrCreateGlyphExtents(int32_t aAppUnitsPerDevUnit) { } void -gfxFont::SetupGlyphExtents(gfxContext *aContext, uint32_t aGlyphID, bool aNeedTight, +gfxFont::SetupGlyphExtents(gfxContext *aContext, Orientation aOrientation, + uint32_t aGlyphID, bool aNeedTight, gfxGlyphExtents *aExtents) { gfxContextMatrixAutoSaveRestore matrixRestore(aContext); @@ -2962,7 +2967,7 @@ gfxFont::SetupGlyphExtents(gfxContext *aContext, uint32_t aGlyphID, bool aNeedTi cairo_text_extents_t extents; cairo_glyph_extents(aContext->GetCairo(), &glyph, 1, &extents); - const Metrics& fontMetrics = GetMetrics(); + const Metrics& fontMetrics = GetMetrics(aOrientation); int32_t appUnitsPerDevUnit = aExtents->GetAppUnitsPerDevUnit(); if (!aNeedTight && extents.x_bearing >= 0 && extents.y_bearing >= -fontMetrics.maxAscent && @@ -3368,8 +3373,8 @@ gfxFont::SynthesizeSpaceWidth(uint32_t aCh) case 0x2004: return GetAdjustedSize() / 3; // three-per-em space case 0x2005: return GetAdjustedSize() / 4; // four-per-em space case 0x2006: return GetAdjustedSize() / 6; // six-per-em space - case 0x2007: return GetMetrics().zeroOrAveCharWidth; // figure space - case 0x2008: return GetMetrics().spaceWidth; // punctuation space + case 0x2007: return GetMetrics(eHorizontal).zeroOrAveCharWidth; // figure space + case 0x2008: return GetMetrics(eHorizontal).spaceWidth; // punctuation space case 0x2009: return GetAdjustedSize() / 5; // thin space case 0x200a: return GetAdjustedSize() / 10; // hair space case 0x202f: return GetAdjustedSize() / 5; // narrow no-break space diff --git a/gfx/thebes/gfxFont.h b/gfx/thebes/gfxFont.h index 55f193bbe833..f92e4f48ecf2 100644 --- a/gfx/thebes/gfxFont.h +++ b/gfx/thebes/gfxFont.h @@ -1442,9 +1442,20 @@ public: // equal to .aveCharWidth }; - const Metrics& GetMetrics() + enum Orientation { + eHorizontal, + eVertical + }; + + const Metrics& GetMetrics(Orientation aOrientation) { - return GetHorizontalMetrics(); + if (aOrientation == eHorizontal) { + return GetHorizontalMetrics(); + } + if (!mVerticalMetrics) { + mVerticalMetrics = CreateVerticalMetrics(); + } + return *mVerticalMetrics; } /** @@ -1562,7 +1573,8 @@ public: gfxGlyphExtents *GetOrCreateGlyphExtents(int32_t aAppUnitsPerDevUnit); // You need to call SetupCairoFont on the aCR just before calling this - virtual void SetupGlyphExtents(gfxContext *aContext, uint32_t aGlyphID, + virtual void SetupGlyphExtents(gfxContext *aContext, + Orientation aOrientation, uint32_t aGlyphID, bool aNeedTight, gfxGlyphExtents *aExtents); // This is called by the default Draw() implementation above. diff --git a/gfx/thebes/gfxGlyphExtents.cpp b/gfx/thebes/gfxGlyphExtents.cpp index 0f5bd16e7d0d..2a946456ce03 100644 --- a/gfx/thebes/gfxGlyphExtents.cpp +++ b/gfx/thebes/gfxGlyphExtents.cpp @@ -36,6 +36,7 @@ gfxGlyphExtents::~gfxGlyphExtents() bool gfxGlyphExtents::GetTightGlyphExtentsAppUnits(gfxFont *aFont, + gfxFont::Orientation aOrientation, gfxContext *aContext, uint32_t aGlyphID, gfxRect *aExtents) { HashEntry *entry = mTightGlyphExtents.GetEntry(aGlyphID); @@ -49,7 +50,8 @@ gfxGlyphExtents::GetTightGlyphExtentsAppUnits(gfxFont *aFont, #ifdef DEBUG_TEXT_RUN_STORAGE_METRICS ++gGlyphExtentsSetupLazyTight; #endif - aFont->SetupGlyphExtents(aContext, aGlyphID, true, this); + aFont->SetupGlyphExtents(aContext, aOrientation, aGlyphID, true, + this); entry = mTightGlyphExtents.GetEntry(aGlyphID); } if (!entry) { diff --git a/gfx/thebes/gfxGlyphExtents.h b/gfx/thebes/gfxGlyphExtents.h index 59ec4225d55f..28952a8c7955 100644 --- a/gfx/thebes/gfxGlyphExtents.h +++ b/gfx/thebes/gfxGlyphExtents.h @@ -6,13 +6,13 @@ #ifndef GFX_GLYPHEXTENTS_H #define GFX_GLYPHEXTENTS_H +#include "gfxFont.h" #include "nsTHashtable.h" #include "nsHashKeys.h" #include "nsTArray.h" #include "mozilla/MemoryReporting.h" class gfxContext; -class gfxFont; struct gfxRect; /** @@ -62,8 +62,9 @@ public: // Get glyph extents; a rectangle relative to the left baseline origin // Returns true on success. Can fail on OOM or when aContext is null // and extents were not (successfully) prefetched. - bool GetTightGlyphExtentsAppUnits(gfxFont *aFont, gfxContext *aContext, - uint32_t aGlyphID, gfxRect *aExtents); + bool GetTightGlyphExtentsAppUnits(gfxFont *aFont, + gfxFont::Orientation aOrientation, + gfxContext *aContext, uint32_t aGlyphID, gfxRect *aExtents); void SetContainedGlyphWidthAppUnits(uint32_t aGlyphID, uint16_t aWidth) { mContainedGlyphWidths.Set(aGlyphID, aWidth); diff --git a/gfx/thebes/gfxPangoFonts.cpp b/gfx/thebes/gfxPangoFonts.cpp index 46d5180f0c92..84bdab8fd085 100644 --- a/gfx/thebes/gfxPangoFonts.cpp +++ b/gfx/thebes/gfxPangoFonts.cpp @@ -1861,7 +1861,8 @@ gfxPangoFontGroup::GetBaseFontSet() if (size != 0.0 && mStyle.sizeAdjust != 0.0) { gfxFcFont *font = fontSet->GetFontAt(0, GetStyle()); if (font) { - const gfxFont::Metrics& metrics = font->GetMetrics(); + const gfxFont::Metrics& metrics = + font->GetMetrics(gfxFont::eHorizontal); // XXX vertical? // The factor of 0.1 ensures that xHeight is sane so fonts don't // become huge. Strictly ">" ensures that xHeight and emHeight are diff --git a/gfx/thebes/gfxTextRun.cpp b/gfx/thebes/gfxTextRun.cpp index db04ba516970..bbe232f99cc7 100644 --- a/gfx/thebes/gfxTextRun.cpp +++ b/gfx/thebes/gfxTextRun.cpp @@ -1264,8 +1264,12 @@ gfxTextRun::SetSpaceGlyphIfSimple(gfxFont *aFont, gfxContext *aContext, return false; } + gfxFont::Orientation fontOrientation = + (aOrientation & gfxTextRunFactory::TEXT_ORIENT_VERTICAL_UPRIGHT) ? + gfxFont::eVertical : gfxFont::eHorizontal; uint32_t spaceWidthAppUnits = - NS_lroundf(aFont->GetMetrics().spaceWidth * mAppUnitsPerDevUnit); + NS_lroundf(aFont->GetMetrics(fontOrientation).spaceWidth * + mAppUnitsPerDevUnit); if (!CompressedGlyph::IsSimpleAdvance(spaceWidthAppUnits)) { return false; } @@ -1306,6 +1310,8 @@ gfxTextRun::FetchGlyphExtents(gfxContext *aRefContext) for (j = start; j < end; ++j) { const gfxTextRun::CompressedGlyph *glyphData = &charGlyphs[j]; + gfxFont::Orientation orientation = + IsVertical() ? gfxFont::eVertical : gfxFont::eHorizontal; if (glyphData->IsSimpleGlyph()) { // If we're in speed mode, don't set up glyph extents here; we'll // just return "optimistic" glyph bounds later @@ -1322,7 +1328,8 @@ gfxTextRun::FetchGlyphExtents(gfxContext *aRefContext) #ifdef DEBUG_TEXT_RUN_STORAGE_METRICS ++gGlyphExtentsSetupEagerSimple; #endif - font->SetupGlyphExtents(aRefContext, glyphIndex, false, extents); + font->SetupGlyphExtents(aRefContext, orientation, + glyphIndex, false, extents); } } } else if (!glyphData->IsMissing()) { @@ -1347,7 +1354,8 @@ gfxTextRun::FetchGlyphExtents(gfxContext *aRefContext) #ifdef DEBUG_TEXT_RUN_STORAGE_METRICS ++gGlyphExtentsSetupEagerTight; #endif - font->SetupGlyphExtents(aRefContext, glyphIndex, true, extents); + font->SetupGlyphExtents(aRefContext, orientation, + glyphIndex, true, extents); } } } @@ -2474,16 +2482,19 @@ gfxFontGroup::GetUnderlineOffset() if (!font) { continue; } - gfxFloat bad = font->GetMetrics().underlineOffset; + gfxFloat bad = font->GetMetrics(gfxFont::eHorizontal). + underlineOffset; gfxFloat first = - GetFirstValidFont()->GetMetrics().underlineOffset; + GetFirstValidFont()->GetMetrics(gfxFont::eHorizontal). + underlineOffset; mUnderlineOffset = std::min(first, bad); return mUnderlineOffset; } } // no bad underline fonts, use the first valid font's metric - mUnderlineOffset = GetFirstValidFont()->GetMetrics().underlineOffset; + mUnderlineOffset = GetFirstValidFont()-> + GetMetrics(gfxFont::eHorizontal).underlineOffset; } return mUnderlineOffset; diff --git a/layout/generic/nsTextFrame.cpp b/layout/generic/nsTextFrame.cpp index a2da9611212d..bb1bd5683aee 100644 --- a/layout/generic/nsTextFrame.cpp +++ b/layout/generic/nsTextFrame.cpp @@ -1798,7 +1798,7 @@ GetFirstFontMetrics(gfxFontGroup* aFontGroup) gfxFont* font = aFontGroup->GetFirstValidFont(); if (!font) return gfxFont::Metrics(); - return font->GetMetrics(); + return font->GetMetrics(gfxFont::eHorizontal); // XXX vertical } PR_STATIC_ASSERT(NS_STYLE_WHITESPACE_NORMAL == 0); @@ -5672,7 +5672,8 @@ nsTextFrame::PaintTextSelectionDecorations(gfxContext* aCtx, gfxFont* firstFont = aProvider.GetFontGroup()->GetFirstValidFont(); if (!firstFont) return; // OOM - gfxFont::Metrics decorationMetrics(firstFont->GetMetrics()); + gfxFont::Metrics + decorationMetrics(firstFont->GetMetrics(gfxFont::eHorizontal)); // XXX vertical? decorationMetrics.underlineOffset = aProvider.GetFontGroup()->GetUnderlineOffset(); @@ -6353,7 +6354,8 @@ nsTextFrame::CombineSelectionUnderlineRect(nsPresContext* aPresContext, gfxFont* firstFont = fontGroup->GetFirstValidFont(); if (!firstFont) return false; // OOM - const gfxFont::Metrics& metrics = firstFont->GetMetrics(); + const gfxFont::Metrics& metrics = + firstFont->GetMetrics(gfxFont::eHorizontal); // XXX vertical? gfxFloat underlineOffset = fontGroup->GetUnderlineOffset(); gfxFloat ascent = aPresContext->AppUnitsToGfxUnits(mAscent); gfxFloat descentLimit = diff --git a/layout/style/nsRuleNode.cpp b/layout/style/nsRuleNode.cpp index 675bb19b1ccb..22f6ff87c2a2 100644 --- a/layout/style/nsRuleNode.cpp +++ b/layout/style/nsRuleNode.cpp @@ -500,8 +500,9 @@ static nscoord CalcLengthWith(const nsCSSValue& aValue, nsRefPtr fm = GetMetricsFor(aPresContext, aStyleContext, styleFont, aFontSize, aUseUserFontSet); - gfxFloat zeroWidth = (fm->GetThebesFontGroup()->GetFirstValidFont() - ->GetMetrics().zeroOrAveCharWidth); + gfxFloat zeroWidth = + fm->GetThebesFontGroup()->GetFirstValidFont()-> + GetMetrics(gfxFont::eHorizontal).zeroOrAveCharWidth; // XXX vertical? return ScaleCoordRound(aValue, ceil(aPresContext->AppUnitsPerDevPixel() * zeroWidth)); From 4a06c41b0a3d846824747251dc752d3756f874e3 Mon Sep 17 00:00:00 2001 From: Jonathan Kew Date: Tue, 30 Sep 2014 07:38:35 +0100 Subject: [PATCH 71/82] Bug 1065002 pt 2 - Add an orientation field to nsFontMetrics. r=jdaggett --- dom/canvas/CanvasRenderingContext2D.cpp | 3 ++- gfx/src/nsDeviceContext.cpp | 15 ++++++++++----- gfx/src/nsDeviceContext.h | 2 ++ gfx/src/nsFontMetrics.cpp | 4 +++- gfx/src/nsFontMetrics.h | 7 +++++++ layout/base/nsLayoutUtils.cpp | 2 ++ layout/base/nsPresShell.cpp | 1 + layout/generic/MathMLTextRunFactory.cpp | 1 + layout/generic/nsPageFrame.cpp | 1 + layout/mathml/nsMathMLChar.cpp | 2 ++ layout/style/nsRuleNode.cpp | 10 +++++++++- 11 files changed, 40 insertions(+), 8 deletions(-) diff --git a/dom/canvas/CanvasRenderingContext2D.cpp b/dom/canvas/CanvasRenderingContext2D.cpp index d2ff09b235f6..ab3ada588fc2 100644 --- a/dom/canvas/CanvasRenderingContext2D.cpp +++ b/dom/canvas/CanvasRenderingContext2D.cpp @@ -2169,7 +2169,8 @@ public: gfxTextPerfMetrics* tp = mPresContext->GetTextPerfMetrics(); nsRefPtr fontMetrics; nsDeviceContext* dc = mPresContext->DeviceContext(); - dc->GetMetricsFor(mFont, mFontLanguage, nullptr, tp, + dc->GetMetricsFor(mFont, mFontLanguage, gfxFont::eHorizontal, + nullptr, tp, *getter_AddRefs(fontMetrics)); return NSAppUnitsToFloatPixels(fontMetrics->XHeight(), nsPresContext::AppUnitsPerCSSPixel()); diff --git a/gfx/src/nsDeviceContext.cpp b/gfx/src/nsDeviceContext.cpp index 631bcc4d342a..105eb81f6201 100644 --- a/gfx/src/nsDeviceContext.cpp +++ b/gfx/src/nsDeviceContext.cpp @@ -62,6 +62,7 @@ public: void Destroy(); nsresult GetMetricsFor(const nsFont& aFont, nsIAtom* aLanguage, + gfxFont::Orientation aOrientation, gfxUserFontSet* aUserFontSet, gfxTextPerfMetrics* aTextPerf, nsFontMetrics*& aMetrics); @@ -122,6 +123,7 @@ nsFontCache::Observe(nsISupports*, const char* aTopic, const char16_t*) nsresult nsFontCache::GetMetricsFor(const nsFont& aFont, nsIAtom* aLanguage, + gfxFont::Orientation aOrientation, gfxUserFontSet* aUserFontSet, gfxTextPerfMetrics* aTextPerf, nsFontMetrics*& aMetrics) @@ -137,7 +139,7 @@ nsFontCache::GetMetricsFor(const nsFont& aFont, nsIAtom* aLanguage, for (int32_t i = n; i >= 0; --i) { fm = mFontMetrics[i]; if (fm->Font().Equals(aFont) && fm->GetUserFontSet() == aUserFontSet && - fm->Language() == aLanguage) { + fm->Language() == aLanguage && fm->Orientation() == aOrientation) { if (i != n) { // promote it to the end of the cache mFontMetrics.RemoveElementAt(i); @@ -153,7 +155,8 @@ nsFontCache::GetMetricsFor(const nsFont& aFont, nsIAtom* aLanguage, fm = new nsFontMetrics(); NS_ADDREF(fm); - nsresult rv = fm->Init(aFont, aLanguage, mContext, aUserFontSet, aTextPerf); + nsresult rv = fm->Init(aFont, aLanguage, aOrientation, mContext, + aUserFontSet, aTextPerf); if (NS_SUCCEEDED(rv)) { // the mFontMetrics list has the "head" at the end, because append // is cheaper than insert @@ -172,7 +175,8 @@ nsFontCache::GetMetricsFor(const nsFont& aFont, nsIAtom* aLanguage, Compact(); fm = new nsFontMetrics(); NS_ADDREF(fm); - rv = fm->Init(aFont, aLanguage, mContext, aUserFontSet, aTextPerf); + rv = fm->Init(aFont, aLanguage, aOrientation, mContext, aUserFontSet, + aTextPerf); if (NS_SUCCEEDED(rv)) { mFontMetrics.AppendElement(fm); aMetrics = fm; @@ -260,6 +264,7 @@ nsDeviceContext::~nsDeviceContext() nsresult nsDeviceContext::GetMetricsFor(const nsFont& aFont, nsIAtom* aLanguage, + gfxFont::Orientation aOrientation, gfxUserFontSet* aUserFontSet, gfxTextPerfMetrics* aTextPerf, nsFontMetrics*& aMetrics) @@ -270,8 +275,8 @@ nsDeviceContext::GetMetricsFor(const nsFont& aFont, mFontCache->Init(this); } - return mFontCache->GetMetricsFor(aFont, aLanguage, aUserFontSet, - aTextPerf, aMetrics); + return mFontCache->GetMetricsFor(aFont, aLanguage, aOrientation, + aUserFontSet, aTextPerf, aMetrics); } nsresult diff --git a/gfx/src/nsDeviceContext.h b/gfx/src/nsDeviceContext.h index db005f29fe11..342dcc503069 100644 --- a/gfx/src/nsDeviceContext.h +++ b/gfx/src/nsDeviceContext.h @@ -9,6 +9,7 @@ #include // for uint32_t #include // for int32_t #include "gfxTypes.h" // for gfxFloat +#include "gfxFont.h" // for gfxFont::Orientation #include "mozilla/Assertions.h" // for MOZ_ASSERT_HELPER2 #include "nsAutoPtr.h" // for nsRefPtr #include "nsCOMPtr.h" // for nsCOMPtr @@ -118,6 +119,7 @@ public: * @return error status */ nsresult GetMetricsFor(const nsFont& aFont, nsIAtom* aLanguage, + gfxFont::Orientation aOrientation, gfxUserFontSet* aUserFontSet, gfxTextPerfMetrics* aTextPerf, nsFontMetrics*& aMetrics); diff --git a/gfx/src/nsFontMetrics.cpp b/gfx/src/nsFontMetrics.cpp index 61873a5267f2..95f2c4c5a30c 100644 --- a/gfx/src/nsFontMetrics.cpp +++ b/gfx/src/nsFontMetrics.cpp @@ -104,6 +104,7 @@ nsFontMetrics::~nsFontMetrics() nsresult nsFontMetrics::Init(const nsFont& aFont, nsIAtom* aLanguage, + gfxFont::Orientation aOrientation, nsDeviceContext *aContext, gfxUserFontSet *aUserFontSet, gfxTextPerfMetrics *aTextPerf) @@ -112,6 +113,7 @@ nsFontMetrics::Init(const nsFont& aFont, nsIAtom* aLanguage, mFont = aFont; mLanguage = aLanguage; + mOrientation = aOrientation; mDeviceContext = aContext; mP2A = mDeviceContext->AppUnitsPerDevPixel(); @@ -147,7 +149,7 @@ nsFontMetrics::Destroy() const gfxFont::Metrics& nsFontMetrics::GetMetrics() const { - return mFontGroup->GetFirstValidFont()->GetMetrics(gfxFont::eHorizontal); // XXX vertical? + return mFontGroup->GetFirstValidFont()->GetMetrics(mOrientation); } nscoord diff --git a/gfx/src/nsFontMetrics.h b/gfx/src/nsFontMetrics.h index 0642c4889253..7d1d6c8d60c0 100644 --- a/gfx/src/nsFontMetrics.h +++ b/gfx/src/nsFontMetrics.h @@ -57,6 +57,7 @@ public: * @see nsDeviceContext#GetMetricsFor() */ nsresult Init(const nsFont& aFont, nsIAtom* aLanguage, + gfxFont::Orientation aOrientation, nsDeviceContext *aContext, gfxUserFontSet *aUserFontSet, gfxTextPerfMetrics *aTextPerf); @@ -174,6 +175,11 @@ public: */ nsIAtom* Language() { return mLanguage; } + /** + * Returns the orientation (horizontal/vertical) of these metrics. + */ + gfxFont::Orientation Orientation() { return mOrientation; } + int32_t GetMaxStringLength(); // Get the width for this string. aWidth will be updated with the @@ -223,6 +229,7 @@ private: nsDeviceContext *mDeviceContext; int32_t mP2A; bool mTextRunRTL; + gfxFont::Orientation mOrientation; }; #endif /* NSFONTMETRICS__H__ */ diff --git a/layout/base/nsLayoutUtils.cpp b/layout/base/nsLayoutUtils.cpp index 37076b0b4c2d..d115fed15738 100644 --- a/layout/base/nsLayoutUtils.cpp +++ b/layout/base/nsLayoutUtils.cpp @@ -3434,8 +3434,10 @@ nsLayoutUtils::GetFontMetricsForStyleContext(nsStyleContext* aStyleContext, if (aInflation != 1.0f) { font.size = NSToCoordRound(font.size * aInflation); } + WritingMode wm(aStyleContext->StyleVisibility()); return pc->DeviceContext()->GetMetricsFor( font, aStyleContext->StyleFont()->mLanguage, + wm.IsVertical() ? gfxFont::eVertical : gfxFont::eHorizontal, fs, tp, *aFontMetrics); } diff --git a/layout/base/nsPresShell.cpp b/layout/base/nsPresShell.cpp index 60d777d29431..b780580cb9f8 100644 --- a/layout/base/nsPresShell.cpp +++ b/layout/base/nsPresShell.cpp @@ -10060,6 +10060,7 @@ void ReflowCountMgr::PaintCount(const char* aName, // We have one frame, therefore we must have a root... aPresContext->GetPresShell()->GetRootFrame()-> StyleFont()->mLanguage, + gfxFont::eHorizontal, aPresContext->GetUserFontSet(), aPresContext->GetTextPerfMetrics(), *getter_AddRefs(fm)); diff --git a/layout/generic/MathMLTextRunFactory.cpp b/layout/generic/MathMLTextRunFactory.cpp index f13cec0c4d74..9cf55666baeb 100644 --- a/layout/generic/MathMLTextRunFactory.cpp +++ b/layout/generic/MathMLTextRunFactory.cpp @@ -740,6 +740,7 @@ MathMLTextRunFactory::RebuildTextRun(nsTransformedTextRun* aTextRun, nsRefPtr metrics; pc->DeviceContext()->GetMetricsFor(font, styles[0]->StyleFont()->mLanguage, + gfxFont::eHorizontal, pc->GetUserFontSet(), pc->GetTextPerfMetrics(), *getter_AddRefs(metrics)); diff --git a/layout/generic/nsPageFrame.cpp b/layout/generic/nsPageFrame.cpp index d0805cd1fd77..9a3c289fc753 100644 --- a/layout/generic/nsPageFrame.cpp +++ b/layout/generic/nsPageFrame.cpp @@ -583,6 +583,7 @@ nsPageFrame::PaintHeaderFooter(nsRenderingContext& aRenderingContext, // Get the FontMetrics to determine width.height of strings nsRefPtr fontMet; pc->DeviceContext()->GetMetricsFor(mPD->mHeadFootFont, nullptr, + gfxFont::eHorizontal, pc->GetUserFontSet(), pc->GetTextPerfMetrics(), *getter_AddRefs(fontMet)); diff --git a/layout/mathml/nsMathMLChar.cpp b/layout/mathml/nsMathMLChar.cpp index 34821b64c163..828f101230bc 100644 --- a/layout/mathml/nsMathMLChar.cpp +++ b/layout/mathml/nsMathMLChar.cpp @@ -989,6 +989,7 @@ nsMathMLChar::SetFontFamily(nsPresContext* aPresContext, aPresContext->DeviceContext()-> GetMetricsFor(font, mStyleContext->StyleFont()->mLanguage, + gfxFont::eHorizontal, aPresContext->GetUserFontSet(), aPresContext->GetTextPerfMetrics(), *getter_AddRefs(fm)); @@ -1529,6 +1530,7 @@ nsMathMLChar::StretchInternal(nsPresContext* aPresContext, aPresContext->DeviceContext()-> GetMetricsFor(font, mStyleContext->StyleFont()->mLanguage, + gfxFont::eHorizontal, aPresContext->GetUserFontSet(), aPresContext->GetTextPerfMetrics(), *getter_AddRefs(fm)); diff --git a/layout/style/nsRuleNode.cpp b/layout/style/nsRuleNode.cpp index 22f6ff87c2a2..dab8e883aea5 100644 --- a/layout/style/nsRuleNode.cpp +++ b/layout/style/nsRuleNode.cpp @@ -285,9 +285,17 @@ GetMetricsFor(nsPresContext* aPresContext, fs = aPresContext->GetUserFontSet(); } gfxTextPerfMetrics *tp = aPresContext->GetTextPerfMetrics(); + gfxFont::Orientation orientation = gfxFont::eHorizontal; + if (aStyleContext) { + WritingMode wm(aStyleContext->StyleVisibility()); + if (wm.IsVertical()) { + orientation = gfxFont::eVertical; + } + } nsRefPtr fm; aPresContext->DeviceContext()->GetMetricsFor(font, aStyleFont->mLanguage, + orientation, fs, tp, *getter_AddRefs(fm)); return fm.forget(); } @@ -502,7 +510,7 @@ static nscoord CalcLengthWith(const nsCSSValue& aValue, aFontSize, aUseUserFontSet); gfxFloat zeroWidth = fm->GetThebesFontGroup()->GetFirstValidFont()-> - GetMetrics(gfxFont::eHorizontal).zeroOrAveCharWidth; // XXX vertical? + GetMetrics(fm->Orientation()).zeroOrAveCharWidth; return ScaleCoordRound(aValue, ceil(aPresContext->AppUnitsPerDevPixel() * zeroWidth)); From 8b1437a232c0dd52c6fca88e77685fdc062419b9 Mon Sep 17 00:00:00 2001 From: JW Wang Date: Mon, 29 Sep 2014 19:23:00 +0200 Subject: [PATCH 72/82] Bug 962874 - Enable content/media mochitests on b2g desktop builds. r=cpearce --- content/media/test/mochitest.ini | 3 ++- content/media/test/test_playback_rate.html | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/content/media/test/mochitest.ini b/content/media/test/mochitest.ini index 371551641287..fc32db73419a 100644 --- a/content/media/test/mochitest.ini +++ b/content/media/test/mochitest.ini @@ -22,7 +22,7 @@ # do ok(true, "Type not supported") and stop the test. [DEFAULT] -skip-if = buildapp == 'mulet' || (buildapp == 'b2g' && toolkit != 'gonk') # b2g-desktop(bug 918299) +skip-if = buildapp == 'mulet' support-files = 320x240.ogv 320x240.ogv^headers^ @@ -299,6 +299,7 @@ support-files = wavedata_u8.wav^headers^ [test_access_control.html] +skip-if = buildapp == 'b2g' && toolkit != 'gonk' # crash on b2g-desktop [test_aspectratio_mp4.html] [test_audio1.html] [test_audio2.html] diff --git a/content/media/test/test_playback_rate.html b/content/media/test/test_playback_rate.html index 62dd3444757e..4410658cf578 100644 --- a/content/media/test/test_playback_rate.html +++ b/content/media/test/test_playback_rate.html @@ -36,7 +36,8 @@ let VERY_SLOW_RATE = 0.1, function ontimeupdate(e) { var t = e.target; - if (t.gotEnded) { + // Skip short files for SoundTouch doesn't work well on small number of samples. + if (t.gotEnded || t.duration < 2) { return; } t.testedForSlowdown = true; From 36cb24b7567a215f8a11854498283b927560a31c Mon Sep 17 00:00:00 2001 From: Valentin Gosu Date: Fri, 19 Sep 2014 02:28:43 +0300 Subject: [PATCH 73/82] Bug 1002855 - Turn on Resource Timing. r=bz --- modules/libpref/init/all.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/libpref/init/all.js b/modules/libpref/init/all.js index bb1470872e97..a0ab69f632e4 100644 --- a/modules/libpref/init/all.js +++ b/modules/libpref/init/all.js @@ -135,7 +135,7 @@ pref("dom.serviceWorkers.enabled", false); pref("dom.enable_performance", true); // Whether resource timing will be gathered and returned by performance.GetEntries* -pref("dom.enable_resource_timing", false); +pref("dom.enable_resource_timing", true); // Whether the Gamepad API is enabled pref("dom.gamepad.enabled", true); From b87b1bef23a179e53f95f5169979dafcf6001042 Mon Sep 17 00:00:00 2001 From: JW Wang Date: Mon, 29 Sep 2014 01:31:00 +0200 Subject: [PATCH 74/82] Bug 1051658 - Part 2 - notify decode error when AudioSink init failed. r=kinetik --- content/media/AudioSink.cpp | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/content/media/AudioSink.cpp b/content/media/AudioSink.cpp index 9d85dadbcb2b..2bb20c61b9e2 100644 --- a/content/media/AudioSink.cpp +++ b/content/media/AudioSink.cpp @@ -55,10 +55,18 @@ AudioSink::Init() nullptr, MEDIA_THREAD_STACK_SIZE); if (NS_FAILED(rv)) { + mStateMachine->OnAudioSinkError(); return rv; } + nsCOMPtr event = NS_NewRunnableMethod(this, &AudioSink::AudioLoop); - return mThread->Dispatch(event, NS_DISPATCH_NORMAL); + rv = mThread->Dispatch(event, NS_DISPATCH_NORMAL); + if (NS_FAILED(rv)) { + mStateMachine->OnAudioSinkError(); + return rv; + } + + return NS_OK; } int64_t @@ -138,6 +146,8 @@ AudioSink::AudioLoop() if (NS_FAILED(InitializeAudioStream())) { NS_WARNING("Initializing AudioStream failed."); + ReentrantMonitorAutoEnter mon(GetReentrantMonitor()); + mStateMachine->OnAudioSinkError(); return; } @@ -197,10 +207,13 @@ AudioSink::InitializeAudioStream() // circumstances, so we take care to drop the decoder monitor while // initializing. RefPtr audioStream(new AudioStream()); - audioStream->Init(mInfo.mChannels, mInfo.mRate, - mChannel, AudioStream::HighLatency); - // TODO: Check Init's return value and bail on error. Unfortunately this - // causes some tests to fail due to playback failing. + nsresult rv = audioStream->Init(mInfo.mChannels, mInfo.mRate, + mChannel, AudioStream::HighLatency); + if (NS_FAILED(rv)) { + audioStream->Shutdown(); + return rv; + } + ReentrantMonitorAutoEnter mon(GetReentrantMonitor()); mAudioStream = audioStream; UpdateStreamSettings(); From ab65de651f4fb6c148ab59039374d31afc3e37ac Mon Sep 17 00:00:00 2001 From: JW Wang Date: Mon, 29 Sep 2014 17:42:00 +0200 Subject: [PATCH 75/82] Bug 1051658 - Part 1 - handle AudioSink init failure in MediaDecoderStateMachine. r=kinetik. --- content/media/MediaDecoderStateMachine.cpp | 38 +++++++++++++++------- content/media/MediaDecoderStateMachine.h | 3 ++ 2 files changed, 30 insertions(+), 11 deletions(-) diff --git a/content/media/MediaDecoderStateMachine.cpp b/content/media/MediaDecoderStateMachine.cpp index e959a051a3a4..fd837e1f52d3 100644 --- a/content/media/MediaDecoderStateMachine.cpp +++ b/content/media/MediaDecoderStateMachine.cpp @@ -1154,9 +1154,9 @@ void MediaDecoderStateMachine::StartPlayback() SetPlayStartTime(TimeStamp::Now()); NS_ASSERTION(IsPlaying(), "Should report playing by end of StartPlayback()"); - if (NS_FAILED(StartAudioThread())) { - DECODER_WARN("Failed to create audio thread"); - } + nsresult rv = StartAudioThread(); + NS_ENSURE_SUCCESS_VOID(rv); + mDecoder->GetReentrantMonitor().NotifyAll(); mDecoder->UpdateStreamBlockingForStateMachinePlaying(); DispatchDecodeTasksIfNeeded(); @@ -1791,15 +1791,12 @@ MediaDecoderStateMachine::StartAudioThread() mStopAudioThread = false; if (HasAudio() && !mAudioSink) { mAudioCompleted = false; - mAudioSink = new AudioSink(this, - mAudioStartTime, mInfo.mAudio, mDecoder->GetAudioChannel()); + mAudioSink = new AudioSink(this, mAudioStartTime, + mInfo.mAudio, mDecoder->GetAudioChannel()); + // OnAudioSinkError() will be called before Init() returns if an error + // occurs during initialization. nsresult rv = mAudioSink->Init(); - if (NS_FAILED(rv)) { - DECODER_WARN("Changed state to SHUTDOWN because audio sink initialization failed"); - SetState(DECODER_STATE_SHUTDOWN); - mScheduler->ScheduleAndShutdown(); - return rv; - } + NS_ENSURE_SUCCESS(rv, rv); mAudioSink->SetVolume(mVolume); mAudioSink->SetPlaybackRate(mPlaybackRate); @@ -3124,6 +3121,25 @@ void MediaDecoderStateMachine::OnAudioSinkComplete() mDecoder->GetReentrantMonitor().NotifyAll(); } +void MediaDecoderStateMachine::OnAudioSinkError() +{ + AssertCurrentThreadInMonitor(); + // AudioSink not used with captured streams, so ignore errors in this case. + if (mAudioCaptured) { + return; + } + + mAudioCompleted = true; + + // Notify media decoder/element about this error. + RefPtr task( + NS_NewRunnableMethod(this, &MediaDecoderStateMachine::OnDecodeError)); + nsresult rv = mDecodeTaskQueue->Dispatch(task); + if (NS_FAILED(rv)) { + DECODER_WARN("Failed to dispatch OnDecodeError"); + } +} + } // namespace mozilla // avoid redefined macro in unified build diff --git a/content/media/MediaDecoderStateMachine.h b/content/media/MediaDecoderStateMachine.h index cdf8be438a91..42b91e005f52 100644 --- a/content/media/MediaDecoderStateMachine.h +++ b/content/media/MediaDecoderStateMachine.h @@ -636,6 +636,9 @@ protected: // and the sink is shutting down. void OnAudioSinkComplete(); + // Called by the AudioSink to signal errors. + void OnAudioSinkError(); + // The decoder object that created this state machine. The state machine // holds a strong reference to the decoder to ensure that the decoder stays // alive once media element has started the decoder shutdown process, and has From 06db6f98bd9a2e51e1fd9773e3ae93e29ba659b0 Mon Sep 17 00:00:00 2001 From: Michael Pruett Date: Sun, 28 Sep 2014 21:52:25 -0500 Subject: [PATCH 76/82] Bug 1056871 - Remove workarounds for bug 687194 in tests. r=mconley --- browser/devtools/styleinspector/test/head.js | 11 +---------- .../browser/browser_test_new_window_from_content.js | 12 +----------- 2 files changed, 2 insertions(+), 21 deletions(-) diff --git a/browser/devtools/styleinspector/test/head.js b/browser/devtools/styleinspector/test/head.js index 2eeb0fd8b548..b840c7304768 100644 --- a/browser/devtools/styleinspector/test/head.js +++ b/browser/devtools/styleinspector/test/head.js @@ -109,16 +109,7 @@ function addTab(url) { let browser = tab.linkedBrowser; info("Loading the helper frame script " + FRAME_SCRIPT_URL); - // Bug 687194 - Mochitest registers its chrome URLs after browser - // initialization, so the content processes don't pick them up. That - // means we can't load our frame script from its chrome URI, because - // the content process won't be able to find it. - // Instead, we resolve the chrome URI for the script to a file URI, which - // we can then pass to the content process, which it is able to find. - let registry = Cc['@mozilla.org/chrome/chrome-registry;1'] - .getService(Ci.nsIChromeRegistry); - let fileURI = registry.convertChromeURL(Services.io.newURI(FRAME_SCRIPT_URL, null, null)).spec; - browser.messageManager.loadFrameScript(fileURI, false); + browser.messageManager.loadFrameScript(FRAME_SCRIPT_URL, false); browser.addEventListener("load", function onload() { browser.removeEventListener("load", onload, true); diff --git a/dom/tests/browser/browser_test_new_window_from_content.js b/dom/tests/browser/browser_test_new_window_from_content.js index b8c1b2d8d74b..230f7d50c403 100644 --- a/dom/tests/browser/browser_test_new_window_from_content.js +++ b/dom/tests/browser/browser_test_new_window_from_content.js @@ -156,17 +156,7 @@ function loadAndSelectTestTab() { gBrowser.selectedTab = tab; let browser = gBrowser.getBrowserForTab(tab); - - // Bug 687194 - Mochitest registers its chrome URLs after browser - // initialization, so the content processes don't pick them up. That - // means we can't load our frame script from its chrome URI, because - // the content process won't be able to find it. - // - // Instead, we resolve the chrome URI for the script to a file URI, which - // we can then pass to the content process, which it is able to find. - let registry = Cc['@mozilla.org/chrome/chrome-registry;1'].getService(Ci.nsIChromeRegistry); - let fileURI = registry.convertChromeURL(Services.io.newURI(kContentScript, null, null)).spec; - browser.messageManager.loadFrameScript(fileURI, false); + browser.messageManager.loadFrameScript(kContentScript, false); let deferred = Promise.defer(); browser.addEventListener("DOMContentLoaded", function onBrowserLoad(aEvent) { From c311c93a343c63451cb0bc6c77213285dfdc180b Mon Sep 17 00:00:00 2001 From: Chris Jones Date: Thu, 25 Sep 2014 19:18:21 -0700 Subject: [PATCH 77/82] Bug 1069573 - Use the bKGD PNG background color (if present) to fill the background of animation frames. r=mwu --- media/libpng/pnglibconf.h | 2 + widget/gonk/libdisplay/BootAnimation.cpp | 66 +++++++++++++++++++++++- 2 files changed, 66 insertions(+), 2 deletions(-) diff --git a/media/libpng/pnglibconf.h b/media/libpng/pnglibconf.h index 94c0e41176e4..7fe1fda448b7 100644 --- a/media/libpng/pnglibconf.h +++ b/media/libpng/pnglibconf.h @@ -86,10 +86,12 @@ /* necessary for boot animation code (Gonk) */ #ifdef MOZ_WIDGET_GONK +#define PNG_bKGD_SUPPORTED #define PNG_UNKNOWN_CHUNKS_SUPPORTED #define PNG_SET_UNKNOWN_CHUNKS_SUPPORTED #define PNG_HANDLE_AS_UNKNOWN_SUPPORTED #define PNG_EASY_ACCESS_SUPPORTED +#define PNG_READ_bKGD_SUPPORTED #define PNG_READ_BGR_SUPPORTED #define PNG_READ_GRAY_TO_RGB_SUPPORTED #define PNG_READ_STRIP_ALPHA_SUPPORTED diff --git a/widget/gonk/libdisplay/BootAnimation.cpp b/widget/gonk/libdisplay/BootAnimation.cpp index 82dc83c6529b..c57e441f316d 100644 --- a/widget/gonk/libdisplay/BootAnimation.cpp +++ b/widget/gonk/libdisplay/BootAnimation.cpp @@ -224,11 +224,13 @@ public: struct AnimationFrame { char path[256]; + png_color_16 bgcolor; char *buf; const local_file_header *file; uint32_t width; uint32_t height; uint16_t bytepp; + bool has_bgcolor; AnimationFrame() : buf(nullptr) {} AnimationFrame(const AnimationFrame &frame) : buf(nullptr) { @@ -290,8 +292,7 @@ AnimationFrame::ReadPngFrame(int outputFormat) { #ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED static const png_byte unused_chunks[] = - { 98, 75, 71, 68, '\0', /* bKGD */ - 99, 72, 82, 77, '\0', /* cHRM */ + { 99, 72, 82, 77, '\0', /* cHRM */ 104, 73, 83, 84, '\0', /* hIST */ 105, 67, 67, 80, '\0', /* iCCP */ 105, 84, 88, 116, '\0', /* iTXt */ @@ -348,9 +349,16 @@ AnimationFrame::ReadPngFrame(int outputFormat) png_read_info(pngread, pnginfo); + png_color_16p colorp; + has_bgcolor = (PNG_INFO_bKGD == png_get_bKGD(pngread, pnginfo, &colorp)); + bgcolor = has_bgcolor ? *colorp : png_color_16(); width = png_get_image_width(pngread, pnginfo); height = png_get_image_height(pngread, pnginfo); + LOG("Decoded %s: %d x %d frame with bgcolor? %s (%#x, %#x, %#x; gray:%#x)", + path, width, height, has_bgcolor ? "yes" : "no", + bgcolor.red, bgcolor.green, bgcolor.blue, bgcolor.gray); + switch (outputFormat) { case HAL_PIXEL_FORMAT_BGRA_8888: png_set_bgr(pngread); @@ -391,6 +399,54 @@ AnimationFrame::ReadPngFrame(int outputFormat) png_destroy_read_struct(&pngread, &pnginfo, nullptr); } +/** + * Return a wchar_t that when used to |wmemset()| an image buffer will + * fill it with the color defined by |color16|. The packed wchar_t + * may comprise one or two pixels depending on |outputFormat|. + */ +static wchar_t +AsBackgroundFill(const png_color_16& color16, int outputFormat) +{ + static_assert(sizeof(wchar_t) == sizeof(uint32_t), + "TODO: support 2-byte wchar_t"); + union { + uint32_t r8g8b8; + struct { + uint8_t b8; + uint8_t g8; + uint8_t r8; + uint8_t x8; + }; + } color; + color.b8 = color16.blue; + color.g8 = color16.green; + color.r8 = color16.red; + color.x8 = 0xFF; + + switch (outputFormat) { + case HAL_PIXEL_FORMAT_RGBA_8888: + case HAL_PIXEL_FORMAT_RGBX_8888: + return color.r8g8b8; + + case HAL_PIXEL_FORMAT_BGRA_8888: + swap(color.r8, color.b8); + return color.r8g8b8; + + case HAL_PIXEL_FORMAT_RGB_565: { + // NB: we could do a higher-quality downsample here, but we + // want the results to be a pixel-perfect match with the fast + // downsample in TransformTo565(). + uint16_t color565 = ((color.r8 & 0xF8) << 8) | + ((color.g8 & 0xFC) << 3) | + ((color.b8 ) >> 3); + return (color565 << 16) | color565; + } + default: + LOGW("Unhandled pixel format %d; falling back on black", outputFormat); + return 0; + } +} + static void * AnimationThread(void *) { @@ -524,6 +580,12 @@ AnimationThread(void *) break; } + if (frame.has_bgcolor) { + wchar_t bgfill = AsBackgroundFill(frame.bgcolor, format); + wmemset((wchar_t*)vaddr, bgfill, + (buf->height * buf->stride * frame.bytepp) / sizeof(wchar_t)); + } + if (buf->height == frame.height && buf->stride == frame.width) { memcpy(vaddr, frame.buf, frame.width * frame.height * frame.bytepp); From 6b3ee519b9af36cab08e65c21db34e2d8a7aa499 Mon Sep 17 00:00:00 2001 From: Douglas Crosher Date: Tue, 30 Sep 2014 01:02:11 +1000 Subject: [PATCH 78/82] Bug 1070985 - IonMonkey x64: add some move instructions with a constant address operand, and fix the formatters to emit REX prefixes. r=nbp --- js/src/jit/shared/BaseAssembler-x86-shared.h | 144 +++++++++++-------- 1 file changed, 86 insertions(+), 58 deletions(-) diff --git a/js/src/jit/shared/BaseAssembler-x86-shared.h b/js/src/jit/shared/BaseAssembler-x86-shared.h index 3374d9a2ab71..9e996dcd47cf 100644 --- a/js/src/jit/shared/BaseAssembler-x86-shared.h +++ b/js/src/jit/shared/BaseAssembler-x86-shared.h @@ -1877,15 +1877,13 @@ public: m_formatter.oneByteOp(OP_MOV_EvGv, src, base, index, scale, offset); } -#ifdef JS_CODEGEN_X86 void movw_rm(RegisterID src, const void* addr) { spew("movw %s, %p", nameIReg(2, src), addr); m_formatter.prefix(PRE_OPERAND_SIZE); - m_formatter.oneByteOp(OP_MOV_EvGv, src, addr); + m_formatter.oneByteOp_disp32(OP_MOV_EvGv, src, addr); } -#endif void movl_rm(RegisterID src, int offset, RegisterID base) { @@ -1985,6 +1983,13 @@ public: m_formatter.immediate8(imm); } + void movb_i8m(int imm, const void* addr) + { + spew("movb %d, %p", imm, addr); + m_formatter.oneByteOp_disp32(OP_GROUP11_EvIb, GROUP11_MOV, addr); + m_formatter.immediate8(imm); + } + void movw_i16m(int imm, int offset, RegisterID base) { spew("movw $0x%x, %s0x%x(%s)", @@ -1994,6 +1999,14 @@ public: m_formatter.immediate16(imm); } + void movw_i16m(int imm, const void* addr) + { + spew("movw %d, %p", imm, addr); + m_formatter.prefix(PRE_OPERAND_SIZE); + m_formatter.oneByteOp_disp32(OP_GROUP11_EvIz, GROUP11_MOV, addr); + m_formatter.immediate16(imm); + } + void movl_i32m(int imm, int offset, RegisterID base) { spew("movl $0x%x, %s0x%x(%s)", @@ -2237,14 +2250,12 @@ public: m_formatter.oneByteOp8(OP_MOV_EbGv, src, base, index, scale, offset); } -#ifdef JS_CODEGEN_X86 void movb_rm(RegisterID src, const void* addr) { spew("movb %s, %p", nameIReg(1, src), addr); - m_formatter.oneByteOp(OP_MOV_EbGv, src, addr); + m_formatter.oneByteOp8(OP_MOV_EbGv, src, addr); } -#endif void movzbl_mr(int offset, RegisterID base, RegisterID dst) { @@ -2267,14 +2278,12 @@ public: m_formatter.twoByteOp(OP2_MOVZX_GvEb, dst, base, index, scale, offset); } -#ifdef JS_CODEGEN_X86 void movzbl_mr(const void* addr, RegisterID dst) { spew("movzbl %p, %s", addr, nameIReg(dst)); m_formatter.twoByteOp(OP2_MOVZX_GvEb, dst, addr); } -#endif void movsbl_mr(int offset, RegisterID base, RegisterID dst) { @@ -2297,14 +2306,12 @@ public: m_formatter.twoByteOp(OP2_MOVSX_GvEb, dst, base, index, scale, offset); } -#ifdef JS_CODEGEN_X86 void movsbl_mr(const void* addr, RegisterID dst) { spew("movsbl %p, %s", addr, nameIReg(4, dst)); m_formatter.twoByteOp(OP2_MOVSX_GvEb, dst, addr); } -#endif void movzwl_rr(RegisterID src, RegisterID dst) { @@ -2334,14 +2341,12 @@ public: m_formatter.twoByteOp(OP2_MOVZX_GvEw, dst, base, index, scale, offset); } -#ifdef JS_CODEGEN_X86 void movzwl_mr(const void* addr, RegisterID dst) { spew("movzwl %p, %s", addr, nameIReg(4, dst)); m_formatter.twoByteOp(OP2_MOVZX_GvEw, dst, addr); } -#endif void movswl_mr(int offset, RegisterID base, RegisterID dst) { @@ -2364,14 +2369,12 @@ public: m_formatter.twoByteOp(OP2_MOVSX_GvEw, dst, base, index, scale, offset); } -#ifdef JS_CODEGEN_X86 void movswl_mr(const void* addr, RegisterID dst) { spew("movswl %p, %s", addr, nameIReg(4, dst)); m_formatter.twoByteOp(OP2_MOVSX_GvEw, dst, addr); } -#endif void movzbl_rr(RegisterID src, RegisterID dst) { @@ -3086,7 +3089,6 @@ public: m_formatter.twoByteOp(OP2_MOVSD_VsdWsd, (RegisterID)dst, (RegisterID)src); } -#ifdef JS_CODEGEN_X86 void movsd_mr(const void* address, XMMRegisterID dst) { spew("movsd %p, %s", @@ -3133,7 +3135,7 @@ public: nameFPReg(src), address); m_formatter.twoByteOp(OP2_MOVPS_WpsVps, (RegisterID)src, address); } -#else +#ifdef JS_CODEGEN_X64 JmpSrc movsd_ripr(XMMRegisterID dst) { spew("movsd ?(%%rip), %s", @@ -3815,15 +3817,16 @@ public: // Linking & patching: // - // 'link' and 'patch' methods are for use on unprotected code - such as the code - // within the AssemblerBuffer, and code being patched by the patch buffer. Once - // code has been finalized it is (platform support permitting) within a non- - // writable region of memory; to modify the code in an execute-only execuable - // pool the 'repatch' and 'relink' methods should be used. + // 'link' and 'patch' methods are for use on unprotected code - such as the + // code within the AssemblerBuffer, and code being patched by the patch + // buffer. Once code has been finalized it is (platform support permitting) + // within a non- writable region of memory; to modify the code in an + // execute-only execuable pool the 'repatch' and 'relink' methods should be + // used. // Like Lua's emitter, we thread jump lists through the unpatched target - // field, which will get fixed up when the label (which has a pointer to - // the head of the jump list) is bound. + // field, which will get fixed up when the label (which has a pointer to the + // head of the jump list) is bound. bool nextJump(const JmpSrc& from, JmpSrc* next) { // Sanity check - if the assembler has OOM'd, it will start overwriting @@ -3930,8 +3933,8 @@ public: where); #ifdef JS_CODEGEN_X64 - // On x86-64 pointer memory accesses require a 64-bit operand, and as such a REX prefix. - // Skip over the prefix byte. + // On x86-64 pointer memory accesses require a 64-bit operand, and as + // such a REX prefix. Skip over the prefix byte. where = reinterpret_cast(where) + 1; #endif *reinterpret_cast(where) = static_cast(OP_LEA); @@ -3942,8 +3945,8 @@ public: staticSpew("##repatchLEAToLoadPtr ((where=%p))", where); #ifdef JS_CODEGEN_X64 - // On x86-64 pointer memory accesses require a 64-bit operand, and as such a REX prefix. - // Skip over the prefix byte. + // On x86-64 pointer memory accesses require a 64-bit operand, and as + // such a REX prefix. Skip over the prefix byte. where = reinterpret_cast(where) + 1; #endif *reinterpret_cast(where) = static_cast(OP_MOV_GvEv); @@ -4031,17 +4034,17 @@ public: } // Test whether the given address will fit in an address immediate field. - // This is always true on x86, but on x64 it's only true for addreses - // which fit in the 32-bit immediate field. + // This is always true on x86, but on x64 it's only true for addreses which + // fit in the 32-bit immediate field. static bool isAddressImmediate(const void *address) { intptr_t value = reinterpret_cast(address); int32_t immediate = static_cast(value); return value == immediate; } - // Convert the given address to a 32-bit immediate field value. This is - // a no-op on x86, but on x64 it asserts that the address is actually - // a valid address immediate. + // Convert the given address to a 32-bit immediate field value. This is a + // no-op on x86, but on x64 it asserts that the address is actually a valid + // address immediate. static int32_t addressImmediate(const void *address) { #ifdef JS_CODEGEN_X64 // x64's 64-bit addresses don't all fit in the 32-bit immediate. @@ -4086,10 +4089,12 @@ private: // * Three argument ModRM - a register, and a register and an offset describing a memory operand. // * Five argument ModRM - a register, and a base register, an index, scale, and offset describing a memory operand. // - // For 32-bit x86 targets, the address operand may also be provided as a void*. - // On 64-bit targets REX prefixes will be planted as necessary, where high numbered registers are used. + // For 32-bit x86 targets, the address operand may also be provided as a + // void*. On 64-bit targets REX prefixes will be planted as necessary, + // where high numbered registers are used. // - // The twoByteOp methods plant two-byte Intel instructions sequences (first opcode byte 0x0F). + // The twoByteOp methods plant two-byte Intel instructions sequences + // (first opcode byte 0x0F). void oneByteOp(OneByteOpcodeID opcode) { @@ -4147,13 +4152,15 @@ private: void oneByteOp(OneByteOpcodeID opcode, int reg, const void* address) { m_buffer.ensureSpace(maxInstructionSize); + emitRexIfNeeded(reg, 0, 0); m_buffer.putByteUnchecked(opcode); - memoryModRM(reg, address); + memoryModRM_disp32(reg, address); } void oneByteOp_disp32(OneByteOpcodeID opcode, int reg, const void* address) { m_buffer.ensureSpace(maxInstructionSize); + emitRexIfNeeded(reg, 0, 0); m_buffer.putByteUnchecked(opcode); memoryModRM_disp32(reg, address); } @@ -4233,6 +4240,7 @@ private: void twoByteOp(TwoByteOpcodeID opcode, int reg, const void* address) { m_buffer.ensureSpace(maxInstructionSize); + emitRexIfNeeded(reg, 0, 0); m_buffer.putByteUnchecked(OP_2BYTE_ESCAPE); m_buffer.putByteUnchecked(opcode); memoryModRM(reg, address); @@ -4261,9 +4269,9 @@ private: #ifdef JS_CODEGEN_X64 // Quad-word-sized operands: // - // Used to format 64-bit operantions, planting a REX.w prefix. - // When planting d64 or f64 instructions, not requiring a REX.w prefix, - // the normal (non-'64'-postfixed) formatters should be used. + // Used to format 64-bit operantions, planting a REX.w prefix. When + // planting d64 or f64 instructions, not requiring a REX.w prefix, the + // normal (non-'64'-postfixed) formatters should be used. void oneByteOp64(OneByteOpcodeID opcode) { @@ -4331,16 +4339,19 @@ private: // Byte-operands: // - // These methods format byte operations. Byte operations differ from the normal - // formatters in the circumstances under which they will decide to emit REX prefixes. - // These should be used where any register operand signifies a byte register. + // These methods format byte operations. Byte operations differ from + // the normal formatters in the circumstances under which they will + // decide to emit REX prefixes. These should be used where any register + // operand signifies a byte register. // - // The disctinction is due to the handling of register numbers in the range 4..7 on - // x86-64. These register numbers may either represent the second byte of the first - // four registers (ah..bh) or the first byte of the second four registers (spl..dil). + // The disctinction is due to the handling of register numbers in the + // range 4..7 on x86-64. These register numbers may either represent + // the second byte of the first four registers (ah..bh) or the first + // byte of the second four registers (spl..dil). // - // Address operands should still be checked using regRequiresRex(), while byteRegRequiresRex() - // is provided to check byte register operands. + // Address operands should still be checked using regRequiresRex(), + // while byteRegRequiresRex() is provided to check byte register + // operands. void oneByteOp8(OneByteOpcodeID opcode, GroupOpcodeID groupOp, RegisterID rm) { @@ -4395,6 +4406,17 @@ private: memoryModRM(reg, base, index, scale, offset); } + void oneByteOp8(OneByteOpcodeID opcode, int reg, const void* address) + { +#ifdef JS_CODEGEN_X86 + MOZ_ASSERT(!byteRegRequiresRex(reg)); +#endif + m_buffer.ensureSpace(maxInstructionSize); + emitRexIf(byteRegRequiresRex(reg), reg, 0, 0); + m_buffer.putByteUnchecked(opcode); + memoryModRM_disp32(reg, address); + } + void twoByteOp8(TwoByteOpcodeID opcode, RegisterID reg, RegisterID rm) { m_buffer.ensureSpace(maxInstructionSize); @@ -4428,8 +4450,9 @@ private: // Immediates: // - // An immedaite should be appended where appropriate after an op has been emitted. - // The writes are unchecked since the opcode formatters above will have ensured space. + // An immedaite should be appended where appropriate after an op has + // been emitted. The writes are unchecked since the opcode formatters + // above will have ensured space. void immediate8(int imm) { @@ -4532,7 +4555,8 @@ private: // Internals; ModRm and REX formatters. - // Byte operand register spl & above require a REX prefix (to prevent the 'H' registers be accessed). + // Byte operand register spl & above require a REX prefix (to prevent + // the 'H' registers be accessed). inline bool byteRegRequiresRex(int reg) { return (reg >= X86Registers::esp); @@ -4563,19 +4587,21 @@ private: emitRex(true, r, x, b); } - // Used for operations with byte operands - use byteRegRequiresRex() to check register operands, - // regRequiresRex() to check other registers (i.e. address base & index). + // Used for operations with byte operands - use byteRegRequiresRex() to + // check register operands, regRequiresRex() to check other registers + // (i.e. address base & index). // - // NB: WebKit's use of emitRexIf() is limited such that the reqRequiresRex() checks are - // not needed. SpiderMonkey extends oneByteOp8 functionality such that r, x, and b can - // all be used. + // NB: WebKit's use of emitRexIf() is limited such that the + // reqRequiresRex() checks are not needed. SpiderMonkey extends + // oneByteOp8 functionality such that r, x, and b can all be used. inline void emitRexIf(bool condition, int r, int x, int b) { if (condition || regRequiresRex(r) || regRequiresRex(x) || regRequiresRex(b)) emitRex(false, r, x, b); } - // Used for word sized operations, will plant a REX prefix if necessary (if any register is r8 or above). + // Used for word sized operations, will plant a REX prefix if necessary + // (if any register is r8 or above). inline void emitRexIfNeeded(int r, int x, int b) { emitRexIf(regRequiresRex(r) || regRequiresRex(x) || regRequiresRex(b), r, x, b); @@ -4614,7 +4640,8 @@ private: void memoryModRM(int reg, RegisterID base, int offset) { - // A base of esp or r12 would be interpreted as a sib, so force a sib with no index & put the base in there. + // A base of esp or r12 would be interpreted as a sib, so force a + // sib with no index & put the base in there. #ifdef JS_CODEGEN_X64 if ((base == hasSib) || (base == hasSib2)) #else @@ -4649,7 +4676,8 @@ private: void memoryModRM_disp32(int reg, RegisterID base, int offset) { - // A base of esp or r12 would be interpreted as a sib, so force a sib with no index & put the base in there. + // A base of esp or r12 would be interpreted as a sib, so force a + // sib with no index & put the base in there. #ifdef JS_CODEGEN_X64 if ((base == hasSib) || (base == hasSib2)) #else From 333bf1d10b77cf88e4a06490c22339dd4b2cf189 Mon Sep 17 00:00:00 2001 From: Douglas Crosher Date: Sat, 27 Sep 2014 00:31:13 +1000 Subject: [PATCH 79/82] Bug 1071456 - IonMonkey: optimize DoubleToFloat32 to useAtStart the argument. r=sunfish --- js/src/jit/Lowering.cpp | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/js/src/jit/Lowering.cpp b/js/src/jit/Lowering.cpp index 683cc0e0be41..c462f28dc014 100644 --- a/js/src/jit/Lowering.cpp +++ b/js/src/jit/Lowering.cpp @@ -1772,7 +1772,7 @@ LIRGenerator::visitToDouble(MToDouble *convert) case MIRType_Int32: { - LInt32ToDouble *lir = new(alloc()) LInt32ToDouble(useRegister(opd)); + LInt32ToDouble *lir = new(alloc()) LInt32ToDouble(useRegisterAtStart(opd)); return define(lir, convert); } @@ -1834,13 +1834,22 @@ LIRGenerator::visitToFloat32(MToFloat32 *convert) case MIRType_Int32: { - LInt32ToFloat32 *lir = new(alloc()) LInt32ToFloat32(useRegister(opd)); + LInt32ToFloat32 *lir = new(alloc()) LInt32ToFloat32(useRegisterAtStart(opd)); return define(lir, convert); } case MIRType_Double: { - LDoubleToFloat32 *lir = new(alloc()) LDoubleToFloat32(useRegister(opd)); + LDoubleToFloat32 *lir; +#if defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_MIPS) + // Bug 1039993: workaround LSRA issues. + if (gen->optimizationInfo().registerAllocator() == RegisterAllocator_LSRA) + lir = new(alloc()) LDoubleToFloat32(useRegister(opd)); + else + lir = new(alloc()) LDoubleToFloat32(useRegisterAtStart(opd)); +#else + lir = new(alloc()) LDoubleToFloat32(useRegisterAtStart(opd)); +#endif return define(lir, convert); } From 2e6d8524b0ce7992f9de454c623bbea0e485e766 Mon Sep 17 00:00:00 2001 From: Jan Beich Date: Fri, 26 Sep 2014 15:15:00 +0200 Subject: [PATCH 80/82] Bug 1073709 - Disable C++11 when using GCC with libc++. r=waldo --- mfbt/Atomics.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/mfbt/Atomics.h b/mfbt/Atomics.h index 495a9d3f595e..c7f76014276f 100644 --- a/mfbt/Atomics.h +++ b/mfbt/Atomics.h @@ -34,10 +34,12 @@ * loose typing of the atomic builtins. GCC 4.5 and 4.6 lacks inline * definitions for unspecialized std::atomic and causes linking errors. * Therefore, we require at least 4.7.0 for using libstdc++. + * + * libc++ is only functional with clang. */ # if MOZ_USING_LIBSTDCXX && MOZ_LIBSTDCXX_VERSION_AT_LEAST(4, 7, 0) # define MOZ_HAVE_CXX11_ATOMICS -# elif MOZ_USING_LIBCXX +# elif MOZ_USING_LIBCXX && defined(__clang__) # define MOZ_HAVE_CXX11_ATOMICS # endif /* From 3ac8cb4ccb96ab6ab398961728fc82cb3856195f Mon Sep 17 00:00:00 2001 From: Cykesiopka Date: Sat, 27 Sep 2014 14:02:00 +0200 Subject: [PATCH 81/82] Bug 1073865 - Add missing SSL_ERROR l10n strings v1. r=dkeeler --- .../locales/en-US/chrome/pipnss/nsserrors.properties | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/security/manager/locales/en-US/chrome/pipnss/nsserrors.properties b/security/manager/locales/en-US/chrome/pipnss/nsserrors.properties index d9f3f0742e4d..f2abd0a80632 100644 --- a/security/manager/locales/en-US/chrome/pipnss/nsserrors.properties +++ b/security/manager/locales/en-US/chrome/pipnss/nsserrors.properties @@ -122,6 +122,18 @@ SSL_ERROR_NEXT_PROTOCOL_DATA_INVALID=SSL received invalid NPN extension data. SSL_ERROR_FEATURE_NOT_SUPPORTED_FOR_SSL2=SSL feature not supported for SSL 2.0 connections. SSL_ERROR_FEATURE_NOT_SUPPORTED_FOR_SERVERS=SSL feature not supported for servers. SSL_ERROR_FEATURE_NOT_SUPPORTED_FOR_CLIENTS=SSL feature not supported for clients. +SSL_ERROR_INVALID_VERSION_RANGE=SSL version range is not valid. +SSL_ERROR_CIPHER_DISALLOWED_FOR_VERSION=SSL peer selected a cipher suite disallowed for the selected protocol version. +SSL_ERROR_RX_MALFORMED_HELLO_VERIFY_REQUEST=SSL received a malformed Hello Verify Request handshake message. +SSL_ERROR_RX_UNEXPECTED_HELLO_VERIFY_REQUEST=SSL received an unexpected Hello Verify Request handshake message. +SSL_ERROR_FEATURE_NOT_SUPPORTED_FOR_VERSION=SSL feature not supported for the protocol version. +SSL_ERROR_RX_UNEXPECTED_CERT_STATUS=SSL received an unexpected Certificate Status handshake message. +SSL_ERROR_UNSUPPORTED_HASH_ALGORITHM=Unsupported hash algorithm used by TLS peer. +SSL_ERROR_DIGEST_FAILURE=Digest function failed. +SSL_ERROR_INCORRECT_SIGNATURE_ALGORITHM=Incorrect signature algorithm specified in a digitally-signed element. +SSL_ERROR_NEXT_PROTOCOL_NO_CALLBACK=The next protocol negotiation extension was enabled, but the callback was cleared prior to being needed. +SSL_ERROR_NEXT_PROTOCOL_NO_PROTOCOL=The server supports no protocols that the client advertises in the ALPN extension. +SSL_ERROR_INAPPROPRIATE_FALLBACK_ALERT=The server rejected the handshake because the client downgraded to a lower TLS version than the server supports. SEC_ERROR_IO=An I/O error occurred during security authorization. SEC_ERROR_LIBRARY_FAILURE=security library failure. SEC_ERROR_BAD_DATA=security library: received bad data. From 293f52a08f3c7bf3264756996ce4c035f575e6c5 Mon Sep 17 00:00:00 2001 From: "Carsten \"Tomcat\" Book" Date: Tue, 30 Sep 2014 10:04:11 +0200 Subject: [PATCH 82/82] Backed out changeset d42938fd0288 (bug 1002855) for web-platform-4 test failures --- modules/libpref/init/all.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/libpref/init/all.js b/modules/libpref/init/all.js index a0ab69f632e4..bb1470872e97 100644 --- a/modules/libpref/init/all.js +++ b/modules/libpref/init/all.js @@ -135,7 +135,7 @@ pref("dom.serviceWorkers.enabled", false); pref("dom.enable_performance", true); // Whether resource timing will be gathered and returned by performance.GetEntries* -pref("dom.enable_resource_timing", true); +pref("dom.enable_resource_timing", false); // Whether the Gamepad API is enabled pref("dom.gamepad.enabled", true);