author | Carsten "Tomcat" Book <cbook@mozilla.com> |
Thu, 21 Jan 2016 11:49:16 +0100 | |
changeset 280912 | 977d78a8dd78afbc0153d37fd9887c3a200dce6a |
parent 280911 | aa416d2a76faf1346c1c9e8a2b6fa44a38cd68fc (current diff) |
parent 280826 | b578a9def3278b66b4ff63d167e5d9c1eff05a7b (diff) |
child 280913 | b39718d9ea3cc74af292a3d162fe44907134a3ac |
child 280950 | 66e07ef46853709e3fa91e7c9ad9fe6abf0d5f06 |
child 281063 | bb351c1c6ca218d9364a89a4370b2b6f5ab8efa9 |
child 281117 | bd50a0b3f94d016c2aae15ea8fcf52bcca37c617 |
push id | 70615 |
push user | cbook@mozilla.com |
push date | Thu, 21 Jan 2016 11:29:33 +0000 |
treeherder | mozilla-inbound@b39718d9ea3c [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | merge |
milestone | 46.0a1 |
first release with | nightly linux32
977d78a8dd78
/
46.0a1
/
20160121030208
/
files
nightly linux64
977d78a8dd78
/
46.0a1
/
20160121030208
/
files
nightly mac
977d78a8dd78
/
46.0a1
/
20160121030208
/
files
nightly win32
977d78a8dd78
/
46.0a1
/
20160121030208
/
files
nightly win64
977d78a8dd78
/
46.0a1
/
20160121030208
/
files
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
releases | nightly linux32
46.0a1
/
20160121030208
/
pushlog to previous
nightly linux64
46.0a1
/
20160121030208
/
pushlog to previous
nightly mac
46.0a1
/
20160121030208
/
pushlog to previous
nightly win32
46.0a1
/
20160121030208
/
pushlog to previous
nightly win64
46.0a1
/
20160121030208
/
pushlog to previous
|
--- a/accessible/base/NotificationController.cpp +++ b/accessible/base/NotificationController.cpp @@ -136,16 +136,17 @@ NotificationController::IsUpdatePending( } //////////////////////////////////////////////////////////////////////////////// // NotificationCollector: private void NotificationController::WillRefresh(mozilla::TimeStamp aTime) { + PROFILER_LABEL_FUNC(js::ProfileEntry::Category::OTHER); Telemetry::AutoTimer<Telemetry::A11Y_UPDATE_TIME> updateTimer; // If the document accessible that notification collector was created for is // now shut down, don't process notifications anymore. NS_ASSERTION(mDocument, "The document was shut down while refresh observer is attached!"); if (!mDocument) return;
new file mode 100644 --- /dev/null +++ b/accessible/base/TextRange-inl.h @@ -0,0 +1,29 @@ +/* -*- 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_TextRange_inl_h__ +#define mozilla_a11y_TextRange_inl_h__ + +#include "TextRange.h" +#include "HyperTextAccessible.h" + +namespace mozilla { +namespace a11y { + +inline Accessible* +TextRange::Container() const +{ + uint32_t pos1 = 0, pos2 = 0; + nsAutoTArray<Accessible*, 30> parents1, parents2; + return CommonParent(mStartContainer, mEndContainer, + &parents1, &pos1, &parents2, &pos2); +} + + +} // namespace a11y +} // namespace mozilla + +#endif
--- a/accessible/base/TextRange.cpp +++ b/accessible/base/TextRange.cpp @@ -1,18 +1,17 @@ /* -*- 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 "TextRange.h" +#include "TextRange-inl.h" #include "Accessible-inl.h" -#include "HyperTextAccessible.h" #include "nsAccUtils.h" namespace mozilla { namespace a11y { //////////////////////////////////////////////////////////////////////////////// // TextPoint @@ -54,90 +53,37 @@ TextPoint::operator <(const TextPoint& a TextRange::TextRange(HyperTextAccessible* aRoot, HyperTextAccessible* aStartContainer, int32_t aStartOffset, HyperTextAccessible* aEndContainer, int32_t aEndOffset) : mRoot(aRoot), mStartContainer(aStartContainer), mEndContainer(aEndContainer), mStartOffset(aStartOffset), mEndOffset(aEndOffset) { } -Accessible* -TextRange::Container() const -{ - if (mStartContainer == mEndContainer) - return mStartContainer; - - // Build the chain of parents - Accessible* p1 = mStartContainer; - Accessible* p2 = mEndContainer; - nsAutoTArray<Accessible*, 30> parents1, parents2; - do { - parents1.AppendElement(p1); - p1 = p1->Parent(); - } while (p1); - do { - parents2.AppendElement(p2); - p2 = p2->Parent(); - } while (p2); - - // Find where the parent chain differs - uint32_t pos1 = parents1.Length(); - uint32_t pos2 = parents2.Length(); - Accessible* parent = nullptr; - uint32_t len = 0; - for (len = std::min(pos1, pos2); len > 0; --len) { - Accessible* child1 = parents1.ElementAt(--pos1); - Accessible* child2 = parents2.ElementAt(--pos2); - if (child1 != child2) - break; - - parent = child1; - } - - return parent; -} - void TextRange::EmbeddedChildren(nsTArray<Accessible*>* aChildren) const { if (mStartContainer == mEndContainer) { int32_t startIdx = mStartContainer->GetChildIndexAtOffset(mStartOffset); int32_t endIdx = mStartContainer->GetChildIndexAtOffset(mEndOffset); for (int32_t idx = startIdx; idx <= endIdx; idx++) { Accessible* child = mStartContainer->GetChildAt(idx); if (nsAccUtils::IsEmbeddedObject(child)) aChildren->AppendElement(child); } return; } Accessible* p1 = mStartContainer->GetChildAtOffset(mStartOffset); Accessible* p2 = mEndContainer->GetChildAtOffset(mEndOffset); + + uint32_t pos1 = 0, pos2 = 0; nsAutoTArray<Accessible*, 30> parents1, parents2; - do { - parents1.AppendElement(p1); - p1 = p1->Parent(); - } while (p1); - do { - parents2.AppendElement(p2); - p2 = p2->Parent(); - } while (p2); - - // Find deepest common container. - uint32_t pos1 = parents1.Length(); - uint32_t pos2 = parents2.Length(); - Accessible* container = nullptr; - for (uint32_t len = std::min(pos1, pos2); len > 0; --len) { - Accessible* child1 = parents1.ElementAt(--pos1); - Accessible* child2 = parents2.ElementAt(--pos2); - if (child1 != child2) - break; - - container = child1; - } + Accessible* container = + CommonParent(p1, p2, &parents1, &pos1, &parents2, &pos2); // Traverse the tree up to the container and collect embedded objects. for (uint32_t idx = 0; idx < pos1 - 1; idx++) { Accessible* parent = parents1[idx + 1]; Accessible* child = parents1[idx]; uint32_t childCount = parent->ChildCount(); for (uint32_t childIdx = child->IndexInParent(); childIdx < childCount; childIdx++) { Accessible* next = parent->GetChildAt(childIdx); @@ -191,16 +137,105 @@ TextRange::Bounds(nsTArray<nsIntRect> aR } void TextRange::Normalize(ETextUnit aUnit) { } +bool +TextRange::Crop(Accessible* aContainer) +{ + uint32_t boundaryPos = 0, containerPos = 0; + nsAutoTArray<Accessible*, 30> boundaryParents, containerParents; + + // Crop the start boundary. + Accessible* container = nullptr; + Accessible* boundary = mStartContainer->GetChildAtOffset(mStartOffset); + if (boundary != aContainer) { + CommonParent(boundary, aContainer, &boundaryParents, &boundaryPos, + &containerParents, &containerPos); + + if (boundaryPos == 0) { + if (containerPos != 0) { + // The container is contained by the start boundary, reduce the range to + // the point starting at the container. + aContainer->ToTextPoint(mStartContainer.StartAssignment(), &mStartOffset); + static_cast<Accessible*>(mStartContainer)->AddRef(); + } + else { + // The start boundary and the container are siblings. + container = aContainer; + } + } + else if (containerPos != 0) { + // The container does not contain the start boundary. + boundary = boundaryParents[boundaryPos]; + container = containerParents[containerPos]; + } + + if (container) { + // If the range start is after the container, then make the range invalid. + if (boundary->IndexInParent() > container->IndexInParent()) { + return !!(mRoot = nullptr); + } + + // If the range starts before the container, then reduce the range to + // the point starting at the container. + if (boundary->IndexInParent() < container->IndexInParent()) { + container->ToTextPoint(mStartContainer.StartAssignment(), &mStartOffset); + mStartContainer.get()->AddRef(); + } + } + + boundaryParents.SetLengthAndRetainStorage(0); + containerParents.SetLengthAndRetainStorage(0); + } + + boundary = mEndContainer->GetChildAtOffset(mEndOffset); + if (boundary == aContainer) { + return true; + } + + // Crop the end boundary. + container = nullptr; + CommonParent(boundary, aContainer, &boundaryParents, &boundaryPos, + &containerParents, &containerPos); + + if (boundaryPos == 0) { + if (containerPos != 0) { + aContainer->ToTextPoint(mEndContainer.StartAssignment(), &mEndOffset, false); + static_cast<Accessible*>(mEndContainer)->AddRef(); + } + else { + container = aContainer; + } + } + else if (containerPos != 0) { + boundary = boundaryParents[boundaryPos]; + container = containerParents[containerPos]; + } + + if (!container) { + return true; + } + + if (boundary->IndexInParent() < container->IndexInParent()) { + return !!(mRoot = nullptr); + } + + if (boundary->IndexInParent() > container->IndexInParent()) { + container->ToTextPoint(mEndContainer.StartAssignment(), &mEndOffset, false); + static_cast<Accessible*>(mEndContainer)->AddRef(); + } + + return true; +} + void TextRange::FindText(const nsAString& aText, EDirection aDirection, nsCaseTreatment aCaseSensitive, TextRange* aFoundRange) const { } void @@ -291,10 +326,51 @@ TextRange::TextInternal(nsAString& aText void TextRange::MoveInternal(ETextUnit aUnit, int32_t aCount, HyperTextAccessible& aContainer, int32_t aOffset, HyperTextAccessible* aStopContainer, int32_t aStopOffset) { } +Accessible* +TextRange::CommonParent(Accessible* aAcc1, Accessible* aAcc2, + nsTArray<Accessible*>* aParents1, uint32_t* aPos1, + nsTArray<Accessible*>* aParents2, uint32_t* aPos2) const +{ + if (aAcc1 == aAcc2) { + return aAcc1; + } + + MOZ_ASSERT(aParents1->Length() == 0 || aParents2->Length() == 0, + "Wrong arguments"); + + // Build the chain of parents. + Accessible* p1 = aAcc1; + Accessible* p2 = aAcc2; + do { + aParents1->AppendElement(p1); + p1 = p1->Parent(); + } while (p1); + do { + aParents2->AppendElement(p2); + p2 = p2->Parent(); + } while (p2); + + // Find where the parent chain differs + *aPos1 = aParents1->Length(); + *aPos2 = aParents2->Length(); + Accessible* parent = nullptr; + uint32_t len = 0; + for (len = std::min(*aPos1, *aPos2); len > 0; --len) { + Accessible* child1 = aParents1->ElementAt(--(*aPos1)); + Accessible* child2 = aParents2->ElementAt(--(*aPos2)); + if (child1 != child2) + break; + + parent = child1; + } + + return parent; +} + } // namespace a11y } // namespace mozilla
--- a/accessible/base/TextRange.h +++ b/accessible/base/TextRange.h @@ -126,16 +126,22 @@ public: void MoveEnd(ETextUnit aUnit, int32_t aCount) { MoveInternal(aUnit, aCount, *mEndContainer, mEndOffset); } /** * Move the range points to the closest unit boundaries. */ void Normalize(ETextUnit aUnit); + /** + * Crops the range if it overlaps the given accessible element boundaries, + * returns true if the range was cropped successfully. + */ + bool Crop(Accessible* aContainer); + enum EDirection { eBackward, eForward }; /** * Return range enclosing the found text. */ @@ -238,16 +244,24 @@ private: bool TextInternal(nsAString& aText, Accessible* aCurrent, uint32_t aStartIntlOffset) const; void MoveInternal(ETextUnit aUnit, int32_t aCount, HyperTextAccessible& aContainer, int32_t aOffset, HyperTextAccessible* aStopContainer = nullptr, int32_t aStopOffset = 0); + /** + * A helper method returning a common parent for two given accessible + * elements. + */ + Accessible* CommonParent(Accessible* aAcc1, Accessible* aAcc2, + nsTArray<Accessible*>* aParents1, uint32_t* aPos1, + nsTArray<Accessible*>* aParents2, uint32_t* aPos2) const; + RefPtr<HyperTextAccessible> mRoot; RefPtr<HyperTextAccessible> mStartContainer; RefPtr<HyperTextAccessible> mEndContainer; int32_t mStartOffset; int32_t mEndOffset; };
--- a/accessible/generic/Accessible.cpp +++ b/accessible/generic/Accessible.cpp @@ -2245,16 +2245,40 @@ Accessible::AnchorAt(uint32_t aAnchorInd already_AddRefed<nsIURI> Accessible::AnchorURIAt(uint32_t aAnchorIndex) { NS_PRECONDITION(IsLink(), "AnchorURIAt is called on not hyper link!"); return nullptr; } +void +Accessible::ToTextPoint(HyperTextAccessible** aContainer, int32_t* aOffset, + bool aIsBefore) const +{ + if (IsHyperText()) { + *aContainer = const_cast<Accessible*>(this)->AsHyperText(); + *aOffset = aIsBefore ? 0 : (*aContainer)->CharacterCount(); + return; + } + + const Accessible* child = nullptr; + const Accessible* parent = this; + do { + child = parent; + parent = parent->Parent(); + } while (parent && !parent->IsHyperText()); + + if (parent) { + *aContainer = const_cast<Accessible*>(parent)->AsHyperText(); + *aOffset = (*aContainer)->GetChildOffset( + child->IndexInParent() + static_cast<int32_t>(!aIsBefore)); + } +} + //////////////////////////////////////////////////////////////////////////////// // SelectAccessible void Accessible::SelectedItems(nsTArray<Accessible*>* aItems) { AccIterator iter(this, filters::GetSelected);
--- a/accessible/generic/Accessible.h +++ b/accessible/generic/Accessible.h @@ -753,16 +753,22 @@ public: */ virtual Accessible* AnchorAt(uint32_t aAnchorIndex); /** * Returns an anchor URI at the given index. */ virtual already_AddRefed<nsIURI> AnchorURIAt(uint32_t aAnchorIndex); + /** + * Returns a text point for the accessible element. + */ + void ToTextPoint(HyperTextAccessible** aContainer, int32_t* aOffset, + bool aIsBefore = true) const; + ////////////////////////////////////////////////////////////////////////////// // SelectAccessible /** * Return an array of selected items. */ virtual void SelectedItems(nsTArray<Accessible*>* aItems);
--- a/accessible/generic/HyperTextAccessible.cpp +++ b/accessible/generic/HyperTextAccessible.cpp @@ -1764,17 +1764,17 @@ HyperTextAccessible::EnclosingRange(a11y } else { aRange.Set(mDoc, mDoc, 0, mDoc, mDoc->CharacterCount()); } } void HyperTextAccessible::SelectionRanges(nsTArray<a11y::TextRange>* aRanges) const { - NS_ASSERTION(aRanges->Length() != 0, "TextRange array supposed to be empty"); + MOZ_ASSERT(aRanges->Length() == 0, "TextRange array supposed to be empty"); dom::Selection* sel = DOMSelection(); if (!sel) return; aRanges->SetCapacity(sel->RangeCount()); for (uint32_t idx = 0; idx < sel->RangeCount(); idx++) {
--- a/accessible/generic/HyperTextAccessible.h +++ b/accessible/generic/HyperTextAccessible.h @@ -253,17 +253,17 @@ public: /** * Return text offset of the given child accessible within hypertext * accessible. * * @param aChild [in] accessible child to get text offset for * @param aInvalidateAfter [in, optional] indicates whether invalidate * cached offsets for next siblings of the child */ - int32_t GetChildOffset(Accessible* aChild, + int32_t GetChildOffset(const Accessible* aChild, bool aInvalidateAfter = false) const { int32_t index = GetIndexOf(aChild); return index == -1 ? -1 : GetChildOffset(index, aInvalidateAfter); } /** * Return text offset for the child accessible index.
--- a/accessible/interfaces/ia2/Makefile.in +++ b/accessible/interfaces/ia2/Makefile.in @@ -6,16 +6,17 @@ IA2DIR = $(topsrcdir)/other-licen GARBAGE += $(MIDL_GENERATED_FILES) # Please keep this list in sync with the moz.build file until the rest of this # Makefile is ported over. MIDL_INTERFACES = \ Accessible2.idl \ Accessible2_2.idl \ + Accessible2_3.idl \ AccessibleAction.idl \ AccessibleApplication.idl \ AccessibleComponent.idl \ AccessibleDocument.idl \ AccessibleEditableText.idl \ AccessibleHyperlink.idl \ AccessibleHypertext.idl \ AccessibleHypertext2.idl \
--- a/accessible/interfaces/nsIAccessibleTextRange.idl +++ b/accessible/interfaces/nsIAccessibleTextRange.idl @@ -8,17 +8,17 @@ interface nsIAccessible; interface nsIAccessibleText; interface nsIArray; interface nsIVariant; /** * A range representing a piece of text in the document. */ -[scriptable, uuid(525b3401-8a67-4822-b35d-661065767cd8)] +[scriptable, uuid(c4515623-55f9-4543-a3d5-c1e9afa588f4)] interface nsIAccessibleTextRange : nsISupports { readonly attribute nsIAccessibleText startContainer; readonly attribute long startOffset; readonly attribute nsIAccessibleText endContainer; readonly attribute long endOffset; /** @@ -77,16 +77,21 @@ interface nsIAccessibleTextRange : nsISu void moveEnd(in unsigned long aUnit, in long aCount); /** * Normalize the range to the closest unit of the given type. */ void normalize(in unsigned long aUnit); /** + * Crops the range by the given accessible element. + */ + boolean crop(in nsIAccessible aContainer); + + /** * Return range enclosing the found text. */ nsIAccessibleTextRange findText(in AString aText, in boolean aIsBackward, in boolean aIsIgnoreCase); /** * Text attributes. Used in conjunction with findAttrs(). */
--- a/accessible/tests/mochitest/text.js +++ b/accessible/tests/mochitest/text.js @@ -468,16 +468,20 @@ function testTextRange(aRange, aRangeDes "Wrong start container of " + aRangeDescr); is(aRange.startOffset, aStartOffset, "Wrong start offset of " + aRangeDescr); isObject(aRange.endContainer, getAccessible(aEndContainer), "Wrong end container of " + aRangeDescr); is(aRange.endOffset, aEndOffset, "Wrong end offset of " + aRangeDescr); + if (aText === undefined) { + return; + } + is(aRange.text, aText, "Wrong text of " + aRangeDescr); var children = aRange.embeddedChildren; is(children ? children.length : 0, aChildren ? aChildren.length : 0, "Wrong embedded children count of " + aRangeDescr); isObject(aRange.container, getAccessible(aCommonContainer), "Wrong container of " + aRangeDescr);
--- a/accessible/tests/mochitest/textrange/a11y.ini +++ b/accessible/tests/mochitest/textrange/a11y.ini @@ -1,3 +1,4 @@ [DEFAULT] [test_general.html] +[test_selection.html]
new file mode 100644 --- /dev/null +++ b/accessible/tests/mochitest/textrange/test_selection.html @@ -0,0 +1,120 @@ +<!DOCTYPE html> +<html> +<head> + <title>Text Range selection tests</title> + <link rel="stylesheet" type="text/css" + href="chrome://mochikit/content/tests/SimpleTest/test.css" /> + + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + <script type="application/javascript" + src="../common.js"></script> + <script type="application/javascript" + src="../text.js"></script> + <script type="application/javascript" + src="../layout.js"></script> + <script type="application/javascript"> + + function doTest() + { + var sel = window.getSelection(); + var p = getNode("p1"); + var a = getNode("p2_a"); + + var range = document.createRange(); + sel.addRange(range); + + // the accessible is contained by the range + range.selectNode(p); + + var a11yranges = getAccessible(document, [nsIAccessibleText]).selectionRanges; + var a11yrange = a11yranges.queryElementAt(0, nsIAccessibleTextRange); + + testTextRange(a11yrange, "selection range #1", document, 3, document, 4); + + ok(a11yrange.crop(getAccessible(a)), "Range failed to crop #1."); + testTextRange(a11yrange, "cropped range #1", a, 0, a, 5); + + // the range is contained by the accessible + range.selectNode(a); + var a11yranges = getAccessible(document, [nsIAccessibleText]).selectionRanges; + var a11yrange = a11yranges.queryElementAt(0, nsIAccessibleTextRange); + + testTextRange(a11yrange, "selection range #2", p, 5, p, 6); + + ok(a11yrange.crop(getAccessible(p)), "Range failed to crop #2."); + testTextRange(a11yrange, "cropped range #2", p, 5, p, 6); + + // the range starts before the accessible and ends inside it + range.setStart(p, 0); + range.setEndAfter(a.firstChild, 4); + var a11yranges = getAccessible(document, [nsIAccessibleText]).selectionRanges; + var a11yrange = a11yranges.queryElementAt(0, nsIAccessibleTextRange); + + testTextRange(a11yrange, "selection range #3", p, 0, a, 4); + + ok(a11yrange.crop(getAccessible(a)), "Range failed to crop #3."); + testTextRange(a11yrange, "cropped range #3", a, 0, a, 4); + + // the range starts inside the accessible and ends after it + range.setStart(a.firstChild, 1); + range.setEndAfter(p); + var a11yranges = getAccessible(document, [nsIAccessibleText]).selectionRanges; + var a11yrange = a11yranges.queryElementAt(0, nsIAccessibleTextRange); + + testTextRange(a11yrange, "selection range #4", a, 1, document, 4); + + ok(a11yrange.crop(getAccessible(a)), "Range failed to crop #4."); + testTextRange(a11yrange, "cropped range #4", a, 1, a, 5); + + // the range ends before the accessible + range.setStart(p.firstChild, 0); + range.setEnd(p.firstChild, 4); + var a11yranges = getAccessible(document, [nsIAccessibleText]).selectionRanges; + var a11yrange = a11yranges.queryElementAt(0, nsIAccessibleTextRange); + + testTextRange(a11yrange, "selection range #5", p, 0, p, 4); + ok(!a11yrange.crop(getAccessible(a)), "Crop #5 succeeded while it shouldn't"); + + // the range starts after the accessible + range.setStart(p.lastChild, 0); + range.setEnd(p.lastChild, 4); + var a11yranges = getAccessible(document, [nsIAccessibleText]).selectionRanges; + var a11yrange = a11yranges.queryElementAt(0, nsIAccessibleTextRange); + + testTextRange(a11yrange, "selection range #6", p, 6, p, 10); + + ok(!a11yrange.crop(getAccessible(a)), "Crop #6 succeeded while it shouldn't"); + + // crop a range by a table + range.selectNode(getNode("c2")); + var a11yranges = getAccessible(document, [nsIAccessibleText]).selectionRanges; + var a11yrange = a11yranges.queryElementAt(0, nsIAccessibleTextRange); + + testTextRange(a11yrange, "selection range #7", document, 4, document, 5); + + ok(a11yrange.crop(getAccessible("table")), "Range failed to crop #7."); + testTextRange(a11yrange, "cropped range #7", "c2", 5, "c2", 6); + + SimpleTest.finish(); + } + + SimpleTest.waitForExplicitFinish(); + addA11yLoadEvent(doTest); + </script> +</head> +<body> + + <a target="_blank" + title="Implement IAccessible2_3::selectionRanges" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=1233118">Bug 1233118</a> + <p id="display"></p> + <div id="content" style="display: none"></div> + <pre id="test"> + </pre> + + <p id="p1">text <a id="p2_a" href="www">link<img id="p2_img", src="../moz.png"></a> text</p> + + <div id="c2">start<table id="table"><tr><td>cell</td></tr></table>end</div> +</body> +</html>
--- a/accessible/windows/ia2/ia2Accessible.cpp +++ b/accessible/windows/ia2/ia2Accessible.cpp @@ -3,26 +3,28 @@ /* 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 "AccessibleWrap.h" #include "Accessible2_i.c" #include "Accessible2_2_i.c" +#include "Accessible2_3_i.c" #include "AccessibleRole.h" #include "AccessibleStates.h" #include "Compatibility.h" #include "ia2AccessibleRelation.h" #include "IUnknownImpl.h" #include "nsCoreUtils.h" #include "nsIAccessibleTypes.h" #include "mozilla/a11y/PDocAccessible.h" #include "Relation.h" +#include "TextRange-inl.h" #include "nsAccessibilityService.h" #include "nsIPersistentProperties2.h" #include "nsISimpleEnumerator.h" using namespace mozilla; using namespace mozilla::a11y; @@ -35,17 +37,19 @@ template<typename String> static void Es STDMETHODIMP ia2Accessible::QueryInterface(REFIID iid, void** ppv) { if (!ppv) return E_INVALIDARG; *ppv = nullptr; - if (IID_IAccessible2_2 == iid) + if (IID_IAccessible2_3 == iid) + *ppv = static_cast<IAccessible2_3*>(this); + else if (IID_IAccessible2_2 == iid) *ppv = static_cast<IAccessible2_2*>(this); else if (IID_IAccessible2 == iid && !Compatibility::IsIA2Off()) *ppv = static_cast<IAccessible2*>(this); if (*ppv) { (reinterpret_cast<IUnknown*>(*ppv))->AddRef(); return S_OK; } @@ -744,16 +748,68 @@ ia2Accessible::get_relationTargetsOfType (*aTargets)[i]->AddRef(); } return S_OK; A11Y_TRYBLOCK_END } +STDMETHODIMP +ia2Accessible::get_selectionRanges(IA2Range** aRanges, + long *aNRanges) +{ + A11Y_TRYBLOCK_BEGIN + + if (!aRanges || !aNRanges || aNRanges <= 0) + return E_INVALIDARG; + + *aNRanges = 0; + + AccessibleWrap* acc = static_cast<AccessibleWrap*>(this); + if (acc->IsDefunct()) + return CO_E_OBJNOTCONNECTED; + + nsAutoTArray<TextRange, 1> ranges; + acc->Document()->SelectionRanges(&ranges); + uint32_t len = ranges.Length(); + for (uint32_t idx = 0; idx < len; idx++) { + if (!ranges[idx].Crop(acc)) { + ranges.RemoveElementAt(idx); + } + } + + *aNRanges = ranges.Length(); + *aRanges = static_cast<IA2Range*>( + ::CoTaskMemAlloc(sizeof(IA2Range) * *aNRanges)); + if (!*aRanges) + return E_OUTOFMEMORY; + + for (uint32_t idx = 0; idx < static_cast<uint32_t>(*aNRanges); idx++) { + AccessibleWrap* anchor = + static_cast<AccessibleWrap*>(ranges[idx].StartContainer()); + (*aRanges)[idx].anchor = static_cast<IAccessible2*>(anchor); + anchor->AddRef(); + + (*aRanges)[idx].anchorOffset = ranges[idx].StartOffset(); + + AccessibleWrap* active = + static_cast<AccessibleWrap*>(ranges[idx].EndContainer()); + (*aRanges)[idx].active = static_cast<IAccessible2*>(active); + active->AddRef(); + + (*aRanges)[idx].activeOffset = ranges[idx].EndOffset(); + } + + return S_OK; + + A11Y_TRYBLOCK_END +} + + //////////////////////////////////////////////////////////////////////////////// // Helpers template<typename String> static inline void EscapeAttributeChars(String& aStr) { int32_t offset = 0;
--- a/accessible/windows/ia2/ia2Accessible.h +++ b/accessible/windows/ia2/ia2Accessible.h @@ -4,23 +4,23 @@ * 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_ia2Accessible_h_ #define mozilla_a11y_ia2Accessible_h_ #include "nsISupports.h" -#include "Accessible2_2.h" +#include "Accessible2_3.h" namespace mozilla { namespace a11y { class Attribute; -class ia2Accessible : public IAccessible2_2 +class ia2Accessible : public IAccessible2_3 { public: // IUnknown STDMETHODIMP QueryInterface(REFIID, void**); // IAccessible2 virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_nRelations( @@ -99,16 +99,21 @@ public: virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_relationTargetsOfType( /* [in] */ BSTR type, /* [in] */ long maxTargets, /* [out, size_is(,*nTargets)] */ IUnknown*** targets, /* [out, retval] */ long* nTargets ); + // IAccessible2_3 + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_selectionRanges( + /* [out, size_is(,*nRanges)] */ IA2Range** ranges, + /* [out, retval] */ long *nRanges); + // Helper method static HRESULT ConvertToIA2Attributes(nsIPersistentProperties* aAttributes, BSTR* aIA2Attributes); static HRESULT ConvertToIA2Attributes(nsTArray<Attribute>* aAttributes, BSTR* aIA2Attributes); }; } // namespace a11y
--- a/accessible/xpcom/xpcAccessibleTextRange.cpp +++ b/accessible/xpcom/xpcAccessibleTextRange.cpp @@ -1,17 +1,17 @@ /* -*- 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 "xpcAccessibleTextRange.h" -#include "TextRange.h" +#include "TextRange-inl.h" #include "xpcAccessibleDocument.h" #include "nsIMutableArray.h" #include "nsComponentManagerUtils.h" #include "nsQueryObject.h" using namespace mozilla; using namespace mozilla::a11y; @@ -166,16 +166,26 @@ xpcAccessibleTextRange::MoveEnd(uint32_t NS_IMETHODIMP xpcAccessibleTextRange::Normalize(uint32_t aUnit) { return NS_OK; } NS_IMETHODIMP +xpcAccessibleTextRange::Crop(nsIAccessible* aContainer, bool* aSuccess) +{ + Accessible* container = aContainer->ToInternalAccessible(); + NS_ENSURE_TRUE(container, NS_ERROR_INVALID_ARG); + + *aSuccess = mRange.Crop(container); + return NS_OK; +} + +NS_IMETHODIMP xpcAccessibleTextRange::FindText(const nsAString& aText, bool aIsBackward, bool aIsIgnoreCase, nsIAccessibleTextRange** aRange) { return NS_OK; } NS_IMETHODIMP
--- a/accessible/xpcom/xpcAccessibleTextRange.h +++ b/accessible/xpcom/xpcAccessibleTextRange.h @@ -44,16 +44,17 @@ public: uint32_t aOtherRangeEndPoint, int32_t* aResult) final override; NS_IMETHOD GetText(nsAString& aText) final override; NS_IMETHOD GetBounds(nsIArray** aRectList) final override; NS_IMETHOD Move(uint32_t aUnit, int32_t aCount) final override; NS_IMETHOD MoveStart(uint32_t aUnit, int32_t aCount) final override; NS_IMETHOD MoveEnd(uint32_t aUnit, int32_t aCount) final override; NS_IMETHOD Normalize(uint32_t aUnit) final override; + NS_IMETHOD Crop(nsIAccessible* aContainer, bool* aSuccess) final override; NS_IMETHOD FindText(const nsAString& aText, bool aIsBackward, bool aIsIgnoreCase, nsIAccessibleTextRange** aRange) final override; NS_IMETHOD FindAttr(uint32_t aAttr, nsIVariant* aVal, bool aIsBackward, nsIAccessibleTextRange** aRange) final override; NS_IMETHOD AddToSelection() final override; NS_IMETHOD RemoveFromSelection() final override; NS_IMETHOD Select() final override; NS_IMETHOD ScrollIntoView(uint32_t aHow) final override;
--- a/browser/app/profile/firefox.js +++ b/browser/app/profile/firefox.js @@ -1389,26 +1389,29 @@ pref("social.sidebar.unload_timeout_ms", pref("social.share.activationPanelEnabled", true); pref("social.shareDirectory", "https://activations.cdn.mozilla.net/sharePanel.html"); pref("dom.identity.enabled", false); // Block insecure active content on https pages pref("security.mixed_content.block_active_content", true); -// Show degraded UI for http pages with password fields -#ifdef NIGHTLY_BUILD +// Show degraded UI for http pages with password fields. +// Only for Nightly and Dev Edition for not, not for beta or release. +#ifndef RELEASE_BUILD pref("security.insecure_password.ui.enabled", true); #else pref("security.insecure_password.ui.enabled", false); #endif // 1 = allow MITM for certificate pinning checks. pref("security.cert_pinning.enforcement_level", 1); +// NB: Changes to this pref affect CERT_CHAIN_SHA1_POLICY_STATUS telemetry. +// See the comment in CertVerifier.cpp. // 0 = allow SHA-1 pref("security.pki.sha1_enforcement_level", 0); // Required blocklist freshness for OneCRL OCSP bypass // (default is 1.25x extensions.blocklist.interval, or 30 hours) pref("security.onecrl.maximum_staleness_in_seconds", 108000); // Override the Gecko-default value of false for Firefox.
--- a/browser/base/content/test/general/browser_sanitizeDialog.js +++ b/browser/base/content/test/general/browser_sanitizeDialog.js @@ -549,17 +549,17 @@ add_task(function* test_offline_cache() // Give www.example.com privileges to store offline data Services.perms.addFromPrincipal(principal, "offline-app", Ci.nsIPermissionManager.ALLOW_ACTION); Services.perms.addFromPrincipal(principal, "offline-app", Ci.nsIOfflineCacheUpdateService.ALLOW_NO_WARN); // Store something to the offline cache var appcacheserv = Cc["@mozilla.org/network/application-cache-service;1"] .getService(Ci.nsIApplicationCacheService); - var appcachegroupid = appcacheserv.buildGroupID(makeURI(URL + "/manifest"), LoadContextInfo.default); + var appcachegroupid = appcacheserv.buildGroupIDForInfo(makeURI(URL + "/manifest"), LoadContextInfo.default); var appcache = appcacheserv.createApplicationCache(appcachegroupid); var storage = Services.cache2.appCacheStorage(LoadContextInfo.default, appcache); // Open the dialog let wh = new WindowHelper(); wh.onload = function () { this.selectDuration(Sanitizer.TIMESPAN_EVERYTHING); // Show details
--- a/browser/components/search/test/browser.ini +++ b/browser/components/search/test/browser.ini @@ -9,42 +9,33 @@ support-files = test.html testEngine.xml testEngine_diacritics.xml testEngine_dupe.xml testEngine_mozsearch.xml webapi.html [browser_426329.js] -skip-if = e10s # Bug ?????? - Test uses load event and checks event.target. [browser_483086.js] [browser_addEngine.js] [browser_amazon.js] [browser_amazon_behavior.js] -skip-if = e10s # Bug ?????? - some issue with progress listeners [JavaScript Error: "req.originalURI is null" {file: "chrome://mochitests/content/browser/browser/components/search/test/browser_bing_behavior.js" line: 127}] [browser_bing.js] [browser_bing_behavior.js] -skip-if = e10s # Bug ?????? - some issue with progress listeners [JavaScript Error: "req.originalURI is null" {file: "chrome://mochitests/content/browser/browser/components/search/test/browser_bing_behavior.js" line: 127}] [browser_contextmenu.js] -skip-if = e10s # Bug ?????? - Test touches content (content.window.getSelection().QueryInterface(Ci.nsISelectionPrivate)....) [browser_eBay.js] [browser_eBay_behavior.js] -skip-if = e10s # Bug ?????? - some issue with progress listeners [JavaScript Error: "req.originalURI is null" {file: "chrome://mochitests/content/browser/browser/components/search/test/browser_bing_behavior.js" line: 127}] [browser_google.js] [browser_google_behavior.js] -skip-if = e10s # Bug ?????? - some issue with progress listeners [JavaScript Error: "req.originalURI is null" {file: "chrome://mochitests/content/browser/browser/components/search/test/browser_bing_behavior.js" line: 127}] [browser_healthreport.js] [browser_hiddenOneOffs_cleanup.js] [browser_hiddenOneOffs_diacritics.js] [browser_oneOffHeader.js] -skip-if = e10s # bug ?????? - Test alters the searchbar textbox value which causes issues with other tests in e10s. [browser_private_search_perwindowpb.js] -skip-if = e10s # Bug ?????? - Test uses load event and checks event.target. [browser_yahoo.js] [browser_yahoo_behavior.js] -skip-if = e10s # Bug ?????? - some issue with progress listeners [JavaScript Error: "req.originalURI is null" {file: "chrome://mochitests/content/browser/browser/components/search/test/browser_bing_behavior.js" line: 127}] [browser_abouthome_behavior.js] -skip-if = e10s || true # Bug ??????, Bug 1100301 - leaks windows until shutdown when --run-by-dir +skip-if = true # Bug ??????, Bug 1100301 - leaks windows until shutdown when --run-by-dir [browser_searchbar_openpopup.js] -skip-if = os == "linux" || e10s # Linux has different focus behaviours and e10s seems to have timing issues. +skip-if = os == "linux" # Linux has different focus behaviours. [browser_searchbar_keyboard_navigation.js] [browser_searchbar_smallpanel_keyboard_navigation.js] [browser_webapi.js]
--- a/browser/components/search/test/browser_426329.js +++ b/browser/components/search/test/browser_426329.js @@ -1,19 +1,10 @@ -// Instead of loading ChromeUtils.js into the test scope in browser-test.js for all tests, -// we only need ChromeUtils.js for a few files which is why we are using loadSubScript. -var ChromeUtils = {}; -this._scriptLoader = Cc["@mozilla.org/moz/jssubscript-loader;1"]. - getService(Ci.mozIJSSubScriptLoader); -this._scriptLoader.loadSubScript("chrome://mochikit/content/tests/SimpleTest/ChromeUtils.js", ChromeUtils); - XPCOMUtils.defineLazyModuleGetter(this, "FormHistory", "resource://gre/modules/FormHistory.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "Promise", - "resource://gre/modules/Promise.jsm"); function expectedURL(aSearchTerms) { const ENGINE_HTML_BASE = "http://mochi.test:8888/browser/browser/components/search/test/test.html"; var textToSubURI = Cc["@mozilla.org/intl/texttosuburi;1"]. getService(Ci.nsITextToSubURI); var searchArg = textToSubURI.ConvertAndEscape("utf-8", aSearchTerms); return ENGINE_HTML_BASE + "?test=" + searchArg; } @@ -48,216 +39,198 @@ function getMenuEntries() { var column = autocompleteMenu.tree.columns[0]; var numRows = autocompleteMenu.tree.view.rowCount; for (var i = 0; i < numRows; i++) { entries.push(autocompleteMenu.tree.view.getValueAt(i, column)); } return entries; } -function* countEntries(name, value) { - let deferred = Promise.defer(); - let count = 0; - let obj = name && value ? {fieldname: name, value: value} : {}; - FormHistory.count(obj, - { handleResult: function(result) { count = result; }, - handleError: function(error) { throw error; }, - handleCompletion: function(reason) { - if (!reason) { - deferred.resolve(count); +function countEntries(name, value) { + return new Promise(resolve => { + let count = 0; + let obj = name && value ? {fieldname: name, value: value} : {}; + FormHistory.count(obj, + { handleResult: function(result) { count = result; }, + handleError: function(error) { throw error; }, + handleCompletion: function(reason) { + if (!reason) { + resolve(count); + } } - } - }); - return deferred.promise; + }); + }); } var searchBar; var searchButton; var searchEntries = ["test"]; -function* promiseSetEngine() { - let deferred = Promise.defer(); - var ss = Services.search; +function promiseSetEngine() { + return new Promise(resolve => { + var ss = Services.search; - function observer(aSub, aTopic, aData) { - switch (aData) { - case "engine-added": - var engine = ss.getEngineByName("Bug 426329"); - ok(engine, "Engine was added."); - ss.currentEngine = engine; - break; - case "engine-current": - ok(ss.currentEngine.name == "Bug 426329", "currentEngine set"); - searchBar = BrowserSearch.searchBar; - searchButton = document.getAnonymousElementByAttribute(searchBar, - "anonid", "search-go-button"); - ok(searchButton, "got search-go-button"); - searchBar.value = "test"; + function observer(aSub, aTopic, aData) { + switch (aData) { + case "engine-added": + var engine = ss.getEngineByName("Bug 426329"); + ok(engine, "Engine was added."); + ss.currentEngine = engine; + break; + case "engine-current": + ok(ss.currentEngine.name == "Bug 426329", "currentEngine set"); + searchBar = BrowserSearch.searchBar; + searchButton = document.getAnonymousElementByAttribute(searchBar, + "anonid", "search-go-button"); + ok(searchButton, "got search-go-button"); + searchBar.value = "test"; - Services.obs.removeObserver(observer, "browser-search-engine-modified"); - deferred.resolve(); - break; - } - }; + Services.obs.removeObserver(observer, "browser-search-engine-modified"); + resolve(); + break; + } + }; - Services.obs.addObserver(observer, "browser-search-engine-modified", false); - ss.addEngine("http://mochi.test:8888/browser/browser/components/search/test/426329.xml", - null, "data:image/x-icon,%00", false); - - return deferred.promise; + Services.obs.addObserver(observer, "browser-search-engine-modified", false); + ss.addEngine("http://mochi.test:8888/browser/browser/components/search/test/426329.xml", + null, "data:image/x-icon,%00", false); + }); } -function* promiseRemoveEngine() { - let deferred = Promise.defer(); - var ss = Services.search; +function promiseRemoveEngine() { + return new Promise(resolve => { + var ss = Services.search; - function observer(aSub, aTopic, aData) { - if (aData == "engine-removed") { - Services.obs.removeObserver(observer, "browser-search-engine-modified"); - deferred.resolve(); - } - }; + function observer(aSub, aTopic, aData) { + if (aData == "engine-removed") { + Services.obs.removeObserver(observer, "browser-search-engine-modified"); + resolve(); + } + }; - Services.obs.addObserver(observer, "browser-search-engine-modified", false); - var engine = ss.getEngineByName("Bug 426329"); - ss.removeEngine(engine); - - return deferred.promise; + Services.obs.addObserver(observer, "browser-search-engine-modified", false); + var engine = ss.getEngineByName("Bug 426329"); + ss.removeEngine(engine); + }); } var preSelectedBrowser; var preTabNo; function* prepareTest() { preSelectedBrowser = gBrowser.selectedBrowser; preTabNo = gBrowser.tabs.length; searchBar = BrowserSearch.searchBar; - let windowFocused = Promise.defer(); - SimpleTest.waitForFocus(windowFocused.resolve, window); - yield windowFocused.promise; + yield SimpleTest.promiseFocus(); + + if (document.activeElement == searchBar) + return; - let deferred = Promise.defer(); - if (document.activeElement != searchBar) { - searchBar.addEventListener("focus", function onFocus() { - searchBar.removeEventListener("focus", onFocus); - deferred.resolve(); - }); - gURLBar.focus(); - searchBar.focus(); - } else { - deferred.resolve(); - } - return deferred.promise; + let focusPromise = BrowserTestUtils.waitForEvent(searchBar, "focus"); + gURLBar.focus(); + searchBar.focus(); + yield focusPromise; } add_task(function* testSetupEngine() { yield promiseSetEngine(); }); add_task(function* testReturn() { - yield prepareTest(); + yield* prepareTest(); EventUtils.synthesizeKey("VK_RETURN", {}); - let event = yield promiseOnLoad(); + yield BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser); is(gBrowser.tabs.length, preTabNo, "Return key did not open new tab"); - is(event.originalTarget, preSelectedBrowser.contentDocument, - "Return key loaded results in current tab"); - is(event.originalTarget.URL, expectedURL(searchBar.value), "testReturn opened correct search page"); + is(gBrowser.currentURI.spec, expectedURL(searchBar.value), "testReturn opened correct search page"); }); add_task(function* testAltReturn() { - yield prepareTest(); - EventUtils.synthesizeKey("VK_RETURN", { altKey: true }); - let event = yield promiseOnLoad(); + yield* prepareTest(); + yield BrowserTestUtils.openNewForegroundTab(gBrowser, () => { + EventUtils.synthesizeKey("VK_RETURN", { altKey: true }); + }); is(gBrowser.tabs.length, preTabNo + 1, "Alt+Return key added new tab"); - isnot(event.originalTarget, preSelectedBrowser.contentDocument, - "Alt+Return key loaded results in new tab"); - is(event.originalTarget, gBrowser.contentDocument, - "Alt+Return key loaded results in foreground tab"); - is(event.originalTarget.URL, expectedURL(searchBar.value), "testAltReturn opened correct search page"); + is(gBrowser.currentURI.spec, expectedURL(searchBar.value), "testAltReturn opened correct search page"); }); //Shift key has no effect for now, so skip it add_task(function* testShiftAltReturn() { return; - yield prepareTest(); + yield* prepareTest(); + + let url = expectedURL(searchBar.value); + + let newTabPromise = BrowserTestUtils.waitForNewTab(gBrowser, url); EventUtils.synthesizeKey("VK_RETURN", { shiftKey: true, altKey: true }); - let event = yield promiseOnLoad(); + let newTab = yield newTabPromise; is(gBrowser.tabs.length, preTabNo + 1, "Shift+Alt+Return key added new tab"); - isnot(event.originalTarget, preSelectedBrowser.contentDocument, - "Shift+Alt+Return key loaded results in new tab"); - isnot(event.originalTarget, gBrowser.contentDocument, - "Shift+Alt+Return key loaded results in background tab"); - is(event.originalTarget.URL, expectedURL(searchBar.value), "testShiftAltReturn opened correct search page"); + is(gBrowser.currentURI.spec, url, "testShiftAltReturn opened correct search page"); }); add_task(function* testLeftClick() { - yield prepareTest(); + yield* prepareTest(); simulateClick({ button: 0 }, searchButton); - let event = yield promiseOnLoad(); + yield BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser); is(gBrowser.tabs.length, preTabNo, "LeftClick did not open new tab"); - is(event.originalTarget, preSelectedBrowser.contentDocument, - "LeftClick loaded results in current tab"); - is(event.originalTarget.URL, expectedURL(searchBar.value), "testLeftClick opened correct search page"); + is(gBrowser.currentURI.spec, expectedURL(searchBar.value), "testLeftClick opened correct search page"); }); add_task(function* testMiddleClick() { - yield prepareTest(); - simulateClick({ button: 1 }, searchButton); - let event = yield promiseOnLoad(); + yield* prepareTest(); + yield BrowserTestUtils.openNewForegroundTab(gBrowser, () => { + simulateClick({ button: 1 }, searchButton); + }); is(gBrowser.tabs.length, preTabNo + 1, "MiddleClick added new tab"); - isnot(event.originalTarget, preSelectedBrowser.contentDocument, - "MiddleClick loaded results in new tab"); - is(event.originalTarget, gBrowser.contentDocument, - "MiddleClick loaded results in foreground tab"); - is(event.originalTarget.URL, expectedURL(searchBar.value), "testMiddleClick opened correct search page"); + is(gBrowser.currentURI.spec, expectedURL(searchBar.value), "testMiddleClick opened correct search page"); }); add_task(function* testShiftMiddleClick() { - yield prepareTest(); + yield* prepareTest(); + + let url = expectedURL(searchBar.value); + + let newTabPromise = BrowserTestUtils.waitForNewTab(gBrowser, url); simulateClick({ button: 1, shiftKey: true }, searchButton); - let event = yield promiseOnLoad(); + let newTab = yield newTabPromise; + is(gBrowser.tabs.length, preTabNo + 1, "Shift+MiddleClick added new tab"); - isnot(event.originalTarget, preSelectedBrowser.contentDocument, - "Shift+MiddleClick loaded results in new tab"); - isnot(event.originalTarget, gBrowser.contentDocument, - "Shift+MiddleClick loaded results in background tab"); - is(event.originalTarget.URL, expectedURL(searchBar.value), "testShiftMiddleClick opened correct search page"); + is(newTab.linkedBrowser.currentURI.spec, url, "testShiftMiddleClick opened correct search page"); }); add_task(function* testRightClick() { preTabNo = gBrowser.tabs.length; - content.location.href = "about:blank"; - simulateClick({ button: 2 }, searchButton); - let deferred = Promise.defer(); - setTimeout(function() { - is(gBrowser.tabs.length, preTabNo, "RightClick did not open new tab"); - is(gBrowser.currentURI.spec, "about:blank", "RightClick did nothing"); - deferred.resolve(); - }, 5000); - yield deferred.promise; + gBrowser.selectedBrowser.loadURI("about:blank"); + yield new Promise(resolve => { + setTimeout(function() { + is(gBrowser.tabs.length, preTabNo, "RightClick did not open new tab"); + is(gBrowser.currentURI.spec, "about:blank", "RightClick did nothing"); + resolve(); + }, 5000); + simulateClick({ button: 2 }, searchButton); + }); // The click in the searchbox focuses it, which opens the suggestion // panel. Clean up after ourselves. searchBar.textbox.popup.hidePopup(); }); add_task(function* testSearchHistory() { var textbox = searchBar._textbox; for (var i = 0; i < searchEntries.length; i++) { let count = yield countEntries(textbox.getAttribute("autocompletesearchparam"), searchEntries[i]); ok(count > 0, "form history entry '" + searchEntries[i] + "' should exist"); } }); add_task(function* testAutocomplete() { var popup = searchBar.textbox.popup; - let popupShownPromise = promiseEvent(popup, "popupshown"); + let popupShownPromise = BrowserTestUtils.waitForEvent(popup, "popupshown"); searchBar.textbox.showHistoryPopup(); yield popupShownPromise; checkMenuEntries(searchEntries); }); add_task(function* testClearHistory() { let controller = searchBar.textbox.controllers.getControllerForCommand("cmd_clearhistory") ok(controller.isCommandEnabled("cmd_clearhistory"), "Clear history command enabled"); @@ -266,11 +239,11 @@ add_task(function* testClearHistory() { ok(count == 0, "History cleared"); }); add_task(function* asyncCleanup() { searchBar.value = ""; while (gBrowser.tabs.length != 1) { gBrowser.removeTab(gBrowser.tabs[0], {animate: false}); } - content.location.href = "about:blank"; + gBrowser.selectedBrowser.loadURI("about:blank"); yield promiseRemoveEngine(); });
--- a/browser/components/search/test/browser_abouthome_behavior.js +++ b/browser/components/search/test/browser_abouthome_behavior.js @@ -114,16 +114,19 @@ function test() { onStateChange: function onStateChange(webProgress, req, flags, status) { info("onStateChange"); // Only care about top-level document starts let docStart = Ci.nsIWebProgressListener.STATE_IS_DOCUMENT | Ci.nsIWebProgressListener.STATE_START; if (!(flags & docStart) || !webProgress.isTopLevel) return; + if (req.originalURI.spec == "about:blank") + return; + info("received document start"); ok(req instanceof Ci.nsIChannel, "req is a channel"); is(req.originalURI.spec, gCurrTest.searchURL, "search URL was loaded"); info("Actual URI: " + req.URI.spec); req.cancel(Components.results.NS_ERROR_FAILURE);
--- a/browser/components/search/test/browser_amazon_behavior.js +++ b/browser/components/search/test/browser_amazon_behavior.js @@ -131,16 +131,19 @@ function test() { onStateChange: function onStateChange(webProgress, req, flags, status) { info("onStateChange"); // Only care about top-level document starts let docStart = Ci.nsIWebProgressListener.STATE_IS_DOCUMENT | Ci.nsIWebProgressListener.STATE_START; if (!(flags & docStart) || !webProgress.isTopLevel) return; + if (req.originalURI.spec == "about:blank") + return; + info("received document start"); ok(req instanceof Ci.nsIChannel, "req is a channel"); is(req.originalURI.spec, gCurrTest.searchURL, "search URL was loaded"); info("Actual URI: " + req.URI.spec); req.cancel(Components.results.NS_ERROR_FAILURE);
--- a/browser/components/search/test/browser_bing_behavior.js +++ b/browser/components/search/test/browser_bing_behavior.js @@ -131,16 +131,19 @@ function test() { onStateChange: function onStateChange(webProgress, req, flags, status) { info("onStateChange"); // Only care about top-level document starts let docStart = Ci.nsIWebProgressListener.STATE_IS_DOCUMENT | Ci.nsIWebProgressListener.STATE_START; if (!(flags & docStart) || !webProgress.isTopLevel) return; + if (req.originalURI.spec == "about:blank") + return; + info("received document start"); ok(req instanceof Ci.nsIChannel, "req is a channel"); is(req.originalURI.spec, gCurrTest.searchURL, "search URL was loaded"); info("Actual URI: " + req.URI.spec); req.cancel(Components.results.NS_ERROR_FAILURE);
--- a/browser/components/search/test/browser_contextmenu.js +++ b/browser/components/search/test/browser_contextmenu.js @@ -1,111 +1,98 @@ /* Any copyright is dedicated to the Public Domain. * * http://creativecommons.org/publicdomain/zero/1.0/ */ /* * Test searching for the selected text using the context menu */ -function test() { - waitForExplicitFinish(); - +add_task(function* () { const ss = Services.search; const ENGINE_NAME = "Foo"; var contextMenu; let envService = Cc["@mozilla.org/process/environment;1"].getService(Ci.nsIEnvironment); let originalValue = envService.get("XPCSHELL_TEST_PROFILE_DIR"); envService.set("XPCSHELL_TEST_PROFILE_DIR", "1"); let url = "chrome://mochitests/content/browser/browser/components/search/test/"; let resProt = Services.io.getProtocolHandler("resource") .QueryInterface(Ci.nsIResProtocolHandler); let originalSubstitution = resProt.getSubstitution("search-plugins"); resProt.setSubstitution("search-plugins", Services.io.newURI(url, null, null)); - function observer(aSub, aTopic, aData) { - switch (aData) { - case "engine-added": - var engine = ss.getEngineByName(ENGINE_NAME); - ok(engine, "Engine was added."); - ss.currentEngine = engine; - envService.set("XPCSHELL_TEST_PROFILE_DIR", originalValue); - resProt.setSubstitution("search-plugins", originalSubstitution); - break; - case "engine-current": - is(ss.currentEngine.name, ENGINE_NAME, "currentEngine set"); - startTest(); - break; - case "engine-removed": - Services.obs.removeObserver(observer, "browser-search-engine-modified"); - finish(); - break; - } - } - - Services.obs.addObserver(observer, "browser-search-engine-modified", false); - ss.addEngine("resource://search-plugins/testEngine_mozsearch.xml", - null, "data:image/x-icon,%00", false); - - function startTest() { - contextMenu = document.getElementById("contentAreaContextMenu"); - ok(contextMenu, "Got context menu XUL"); - - doOnloadOnce(testContextMenu); - let tab = gBrowser.selectedTab = gBrowser.addTab("data:text/plain;charset=utf8,test%20search"); - registerCleanupFunction(function () { - gBrowser.removeTab(tab); - }); - } - - function testContextMenu() { - function rightClickOnDocument() { - info("rightClickOnDocument: " + content.window.location); - waitForBrowserContextMenu(checkContextMenu); - var clickTarget = content.document.body; - var eventDetails = { type: "contextmenu", button: 2 }; - EventUtils.synthesizeMouseAtCenter(clickTarget, eventDetails, content); + let searchDonePromise; + yield new Promise(resolve => { + function observer(aSub, aTopic, aData) { + switch (aData) { + case "engine-added": + var engine = ss.getEngineByName(ENGINE_NAME); + ok(engine, "Engine was added."); + ss.currentEngine = engine; + envService.set("XPCSHELL_TEST_PROFILE_DIR", originalValue); + resProt.setSubstitution("search-plugins", originalSubstitution); + break; + case "engine-current": + is(ss.currentEngine.name, ENGINE_NAME, "currentEngine set"); + resolve(); + break; + case "engine-removed": + Services.obs.removeObserver(observer, "browser-search-engine-modified"); + if (searchDonePromise) { + searchDonePromise(); + } + break; + } } - // check the search menu item and then perform a search - function checkContextMenu() { - info("checkContextMenu"); - var searchItem = contextMenu.getElementsByAttribute("id", "context-searchselect")[0]; - ok(searchItem, "Got search context menu item"); - is(searchItem.label, 'Search ' + ENGINE_NAME + ' for "test search"', "Check context menu label"); - is(searchItem.disabled, false, "Check that search context menu item is enabled"); - doOnloadOnce(checkSearchURL); - searchItem.click(); - contextMenu.hidePopup(); - } + Services.obs.addObserver(observer, "browser-search-engine-modified", false); + ss.addEngine("resource://search-plugins/testEngine_mozsearch.xml", + null, "data:image/x-icon,%00", false); + }); + + contextMenu = document.getElementById("contentAreaContextMenu"); + ok(contextMenu, "Got context menu XUL"); + + let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, "data:text/plain;charset=utf8,test%20search"); - function checkSearchURL(event) { - is(event.originalTarget.URL, - "http://mochi.test:8888/browser/browser/components/search/test/?test=test+search&ie=utf-8&channel=contextsearch", - "Checking context menu search URL"); - // Remove the tab opened by the search - gBrowser.removeCurrentTab(); - ss.removeEngine(ss.currentEngine); - } + yield ContentTask.spawn(tab.linkedBrowser, "", function*() { + return new Promise(resolve => { + content.document.addEventListener("selectionchange", function selectionChanged() { + content.document.removeEventListener("selectionchange", selectionChanged); + resolve(); + }); + content.document.getSelection().selectAllChildren(content.document.body); + }); + }); + + var eventDetails = { type: "contextmenu", button: 2 }; + + let popupPromise = BrowserTestUtils.waitForEvent(contextMenu, "popupshown"); + BrowserTestUtils.synthesizeMouseAtCenter("body", eventDetails, gBrowser.selectedBrowser); + yield popupPromise; - var selectionListener = { - notifySelectionChanged: function(doc, sel, reason) { - if (reason != Ci.nsISelectionListener.SELECTALL_REASON || sel.toString() != "test search") - return; - info("notifySelectionChanged: Text selected"); - content.window.getSelection().QueryInterface(Ci.nsISelectionPrivate). - removeSelectionListener(selectionListener); - SimpleTest.executeSoon(rightClickOnDocument); - } - }; + info("checkContextMenu"); + var searchItem = contextMenu.getElementsByAttribute("id", "context-searchselect")[0]; + ok(searchItem, "Got search context menu item"); + is(searchItem.label, 'Search ' + ENGINE_NAME + ' for "test search"', "Check context menu label"); + is(searchItem.disabled, false, "Check that search context menu item is enabled"); + + yield BrowserTestUtils.openNewForegroundTab(gBrowser, () => { + searchItem.click(); + }); - // Delay the select all to avoid intermittent selection failures. - setTimeout(function delaySelectAll() { - info("delaySelectAll: " + content.window.location.toString()); - // add a listener to know when the selection takes effect - content.window.getSelection().QueryInterface(Ci.nsISelectionPrivate). - addSelectionListener(selectionListener); - // select the text on the page - goDoCommand('cmd_selectAll'); - }, 500); - } -} + is(gBrowser.currentURI.spec, + "http://mochi.test:8888/browser/browser/components/search/test/?test=test+search&ie=utf-8&channel=contextsearch", + "Checking context menu search URL"); + + contextMenu.hidePopup(); + + // Remove the tab opened by the search + gBrowser.removeCurrentTab(); + + yield new Promise(resolve => { + searchDonePromise = resolve; + ss.removeEngine(ss.currentEngine); + }); + + gBrowser.removeCurrentTab(); +});
--- a/browser/components/search/test/browser_eBay_behavior.js +++ b/browser/components/search/test/browser_eBay_behavior.js @@ -131,16 +131,19 @@ function test() { onStateChange: function onStateChange(webProgress, req, flags, status) { info("onStateChange"); // Only care about top-level document starts let docStart = Ci.nsIWebProgressListener.STATE_IS_DOCUMENT | Ci.nsIWebProgressListener.STATE_START; if (!(flags & docStart) || !webProgress.isTopLevel) return; + if (req.originalURI.spec == "about:blank") + return; + info("received document start"); ok(req instanceof Ci.nsIChannel, "req is a channel"); is(req.originalURI.spec, gCurrTest.searchURL, "search URL was loaded"); info("Actual URI: " + req.URI.spec); req.cancel(Components.results.NS_ERROR_FAILURE);
--- a/browser/components/search/test/browser_google_behavior.js +++ b/browser/components/search/test/browser_google_behavior.js @@ -129,16 +129,19 @@ function test() { onStateChange: function onStateChange(webProgress, req, flags, status) { info("onStateChange"); // Only care about top-level document starts let docStart = Ci.nsIWebProgressListener.STATE_IS_DOCUMENT | Ci.nsIWebProgressListener.STATE_START; if (!(flags & docStart) || !webProgress.isTopLevel) return; + if (req.originalURI.spec == "about:blank") + return; + info("received document start"); ok(req instanceof Ci.nsIChannel, "req is a channel"); is(req.originalURI.spec, gCurrTest.searchURL, "search URL was loaded"); info("Actual URI: " + req.URI.spec); req.cancel(Components.results.NS_ERROR_FAILURE);
--- a/browser/components/search/test/browser_private_search_perwindowpb.js +++ b/browser/components/search/test/browser_private_search_perwindowpb.js @@ -1,109 +1,73 @@ // This test performs a search in a public window, then a different // search in a private window, and then checks in the public window // whether there is an autocomplete entry for the private search. -function test() { +add_task(function* () { // Don't use about:home as the homepage for new windows Services.prefs.setIntPref("browser.startup.page", 0); registerCleanupFunction(() => Services.prefs.clearUserPref("browser.startup.page")); - waitForExplicitFinish(); - let engineURL = "http://mochi.test:8888/browser/browser/components/search/test/"; let windowsToClose = []; - registerCleanupFunction(function() { - let engine = Services.search.getEngineByName("Bug 426329"); - Services.search.removeEngine(engine); - windowsToClose.forEach(function(win) { - win.close(); - }); - }); - function onPageLoad(aWin, aCallback) { - aWin.gBrowser.addEventListener("DOMContentLoaded", function load(aEvent) { - let doc = aEvent.originalTarget; - info(doc.location.href); - if (doc.location.href.indexOf(engineURL) != -1) { - aWin.gBrowser.removeEventListener("DOMContentLoaded", load, false); - aCallback(); - } - }, false); - } - - function performSearch(aWin, aIsPrivate, aCallback) { + function performSearch(aWin, aIsPrivate) { let searchBar = aWin.BrowserSearch.searchBar; ok(searchBar, "got search bar"); - onPageLoad(aWin, aCallback); + + let loadPromise = BrowserTestUtils.browserLoaded(aWin.gBrowser.selectedBrowser); searchBar.value = aIsPrivate ? "private test" : "public test"; searchBar.focus(); EventUtils.synthesizeKey("VK_RETURN", {}, aWin); + + return loadPromise; } - function addEngine(aCallback) { - let installCallback = { - onSuccess: function (engine) { - Services.search.currentEngine = engine; - aCallback(); - }, - onError: function (errorCode) { - ok(false, "failed to install engine: " + errorCode); - } - }; - Services.search.addEngine(engineURL + "426329.xml", null, - "data:image/x-icon,%00", false, installCallback); + function* testOnWindow(aIsPrivate) { + let win = yield BrowserTestUtils.openNewBrowserWindow({ private: aIsPrivate }); + yield SimpleTest.promiseFocus(win); + windowsToClose.push(win); + return win; } - function testOnWindow(aIsPrivate, aCallback) { - let win = whenNewWindowLoaded({ private: aIsPrivate }, function() { - waitForFocus(aCallback, win); - }); - windowsToClose.push(win); - } + yield promiseNewEngine("426329.xml", { iconURL: "data:image/x-icon,%00" }); + + let newWindow = yield* testOnWindow(false); + yield performSearch(newWindow, false); - addEngine(function() { - testOnWindow(false, function(win) { - performSearch(win, false, function() { - testOnWindow(true, function(win) { - performSearch(win, true, function() { - testOnWindow(false, function(win) { - checkSearchPopup(win, finish); - }); - }); - }); - }); - }); - }); -} + newWindow = yield* testOnWindow(true); + yield performSearch(newWindow, true); -function checkSearchPopup(aWin, aCallback) { - let searchBar = aWin.BrowserSearch.searchBar; + newWindow = yield* testOnWindow(false); + + let searchBar = newWindow.BrowserSearch.searchBar; searchBar.value = "p"; searchBar.focus(); let popup = searchBar.textbox.popup; - popup.addEventListener("popupshowing", function showing() { - popup.removeEventListener("popupshowing", showing, false); + let popupPromise = BrowserTestUtils.waitForEvent(popup, "popupshown"); + searchBar.textbox.showHistoryPopup(); + yield popupPromise; - let entries = getMenuEntries(searchBar); - for (let i = 0; i < entries.length; i++) { - isnot(entries[i], "private test", - "shouldn't see private autocomplete entries"); - } + let entries = getMenuEntries(searchBar); + for (let i = 0; i < entries.length; i++) { + isnot(entries[i], "private test", + "shouldn't see private autocomplete entries"); + } - searchBar.textbox.toggleHistoryPopup(); - searchBar.value = ""; - aCallback(); - }, false); + searchBar.textbox.toggleHistoryPopup(); + searchBar.value = ""; - searchBar.textbox.showHistoryPopup(); -} + windowsToClose.forEach(function(win) { + win.close(); + }); +}); function getMenuEntries(searchBar) { let entries = []; let autocompleteMenu = searchBar.textbox.popup; // Could perhaps pull values directly from the controller, but it seems // more reliable to test the values that are actually in the tree? let column = autocompleteMenu.tree.columns[0]; let numRows = autocompleteMenu.tree.view.rowCount;
--- a/browser/components/search/test/browser_searchbar_keyboard_navigation.js +++ b/browser/components/search/test/browser_searchbar_keyboard_navigation.js @@ -376,30 +376,18 @@ add_task(function* test_tab_and_arrows() // Finally close the panel. let promise = promiseEvent(searchPopup, "popuphidden"); searchPopup.hidePopup(); yield promise; }); add_task(function* test_open_search() { - let tab = gBrowser.addTab(); - gBrowser.selectedTab = tab; - - let deferred = Promise.defer(); - let browser = gBrowser.selectedBrowser; - browser.addEventListener("load", function onload() { - browser.removeEventListener("load", onload, true); - deferred.resolve(); - }, true); - let rootDir = getRootDirectory(gTestPath); - content.location = rootDir + "opensearch.html"; - - yield deferred.promise; + let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, rootDir + "opensearch.html"); let promise = promiseEvent(searchPopup, "popupshown"); info("Opening search panel"); searchbar.focus(); yield promise; let engines = getOpenSearchItems(); is(engines.length, 2, "the opensearch.html page exposes 2 engines")
--- a/browser/components/search/test/browser_searchbar_openpopup.js +++ b/browser/components/search/test/browser_searchbar_openpopup.js @@ -408,17 +408,17 @@ add_task(function* refocus_window_doesnt // Clicking the search go button shouldn't open the popup add_no_popup_task(function* search_go_doesnt_open_popup() { gBrowser.selectedTab = gBrowser.addTab(); gURLBar.focus(); textbox.value = "foo"; searchbar.updateGoButtonVisibility(); - let promise = promiseOnLoad(); + let promise = BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser); EventUtils.synthesizeMouseAtCenter(goButton, {}); yield promise; textbox.value = ""; gBrowser.removeCurrentTab(); }); // Clicks outside the search popup should close the popup but not consume the click.
--- a/browser/components/search/test/browser_searchbar_smallpanel_keyboard_navigation.js +++ b/browser/components/search/test/browser_searchbar_smallpanel_keyboard_navigation.js @@ -304,30 +304,18 @@ add_task(function* test_tab_and_arrows() // Finally close the panel. let promise = promiseEvent(searchPopup, "popuphidden"); searchPopup.hidePopup(); yield promise; }); add_task(function* test_open_search() { - let tab = gBrowser.addTab(); - gBrowser.selectedTab = tab; - - let deferred = Promise.defer(); - let browser = gBrowser.selectedBrowser; - browser.addEventListener("load", function onload() { - browser.removeEventListener("load", onload, true); - deferred.resolve(); - }, true); - let rootDir = getRootDirectory(gTestPath); - content.location = rootDir + "opensearch.html"; - - yield deferred.promise; + let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, rootDir + "opensearch.html"); let promise = promiseEvent(searchPopup, "popupshown"); info("Opening search panel"); EventUtils.synthesizeMouseAtCenter(searchIcon, {}); yield promise; is(searchPopup.getAttribute("showonlysettings"), "true", "Should show the small popup"); let engines = getOpenSearchItems();
--- a/browser/components/search/test/browser_yahoo_behavior.js +++ b/browser/components/search/test/browser_yahoo_behavior.js @@ -131,16 +131,19 @@ function test() { onStateChange: function onStateChange(webProgress, req, flags, status) { info("onStateChange"); // Only care about top-level document starts let docStart = Ci.nsIWebProgressListener.STATE_IS_DOCUMENT | Ci.nsIWebProgressListener.STATE_START; if (!(flags & docStart) || !webProgress.isTopLevel) return; + if (req.originalURI.spec == "about:blank") + return; + info("received document start"); ok(req instanceof Ci.nsIChannel, "req is a channel"); is(req.originalURI.spec, gCurrTest.searchURL, "search URL was loaded"); info("Actual URI: " + req.URI.spec); req.cancel(Components.results.NS_ERROR_FAILURE);
--- a/browser/components/search/test/head.js +++ b/browser/components/search/test/head.js @@ -1,25 +1,11 @@ /* Any copyright is dedicated to the Public Domain. * http://creativecommons.org/publicdomain/zero/1.0/ */ -XPCOMUtils.defineLazyModuleGetter(this, "Promise", - "resource://gre/modules/Promise.jsm"); - -function whenNewWindowLoaded(aOptions, aCallback) { - let win = OpenBrowserWindow(aOptions); - let focused = SimpleTest.promiseFocus(win); - let startupFinished = TestUtils.topicObserved("browser-delayed-startup-finished", - subject => subject == win).then(() => win); - Promise.all([focused, startupFinished]) - .then(results => executeSoon(() => aCallback(results[1]))); - - return win; -} - /** * Recursively compare two objects and check that every property of expectedObj has the same value * on actualObj. */ function isSubObjectOf(expectedObj, actualObj, name) { for (let prop in expectedObj) { if (typeof expectedObj[prop] == 'function') continue; @@ -48,87 +34,39 @@ function getLocalizedPref(aPrefName, aDe return Services.prefs.getComplexValue(aPrefName, Ci.nsIPrefLocalizedString).data; } catch (ex) { return aDefault; } return aDefault; } -function waitForPopupShown(aPopupId, aCallback) { - let popup = document.getElementById(aPopupId); - info("waitForPopupShown: got popup: " + popup.id); - function onPopupShown() { - info("onPopupShown"); - removePopupShownListener(); - SimpleTest.executeSoon(aCallback); - } - function removePopupShownListener() { - popup.removeEventListener("popupshown", onPopupShown); - } - popup.addEventListener("popupshown", onPopupShown); - registerCleanupFunction(removePopupShownListener); -} - function promiseEvent(aTarget, aEventName, aPreventDefault) { - let deferred = Promise.defer(); - aTarget.addEventListener(aEventName, function onEvent(aEvent) { - aTarget.removeEventListener(aEventName, onEvent, true); + function cancelEvent(event) { if (aPreventDefault) { - aEvent.preventDefault(); + event.preventDefault(); } - deferred.resolve(); - }, true); - return deferred.promise; -} -function waitForBrowserContextMenu(aCallback) { - waitForPopupShown(gBrowser.selectedBrowser.contextMenu, aCallback); -} - -function doOnloadOnce(aCallback) { - function doOnloadOnceListener(aEvent) { - info("doOnloadOnce: " + aEvent.originalTarget.location); - removeDoOnloadOnceListener(); - SimpleTest.executeSoon(function doOnloadOnceCallback() { - aCallback(aEvent); - }); - } - function removeDoOnloadOnceListener() { - gBrowser.removeEventListener("load", doOnloadOnceListener, true); + return true; } - gBrowser.addEventListener("load", doOnloadOnceListener, true); - registerCleanupFunction(removeDoOnloadOnceListener); -} -function* promiseOnLoad() { - return new Promise(resolve => { - gBrowser.addEventListener("load", function onLoadListener(aEvent) { - let cw = aEvent.target.defaultView; - let tab = gBrowser._getTabForContentWindow(cw); - if (tab) { - info("onLoadListener: " + aEvent.originalTarget.location); - gBrowser.removeEventListener("load", onLoadListener, true); - resolve(aEvent); - } - }, true); - }); + return BrowserTestUtils.waitForEvent(aTarget, aEventName, false, cancelEvent); } function promiseNewEngine(basename, options = {}) { return new Promise((resolve, reject) => { //Default the setAsCurrent option to true. let setAsCurrent = options.setAsCurrent == undefined ? true : options.setAsCurrent; info("Waiting for engine to be added: " + basename); Services.search.init({ onInitComplete: function() { let url = getRootDirectory(gTestPath) + basename; let current = Services.search.currentEngine; - Services.search.addEngine(url, null, "", false, { + Services.search.addEngine(url, null, options.iconURL || "", false, { onSuccess: function (engine) { info("Search engine added: " + basename); if (setAsCurrent) { Services.search.currentEngine = engine; } registerCleanupFunction(() => { if (setAsCurrent) { Services.search.currentEngine = current;
--- a/browser/modules/DirectoryLinksProvider.jsm +++ b/browser/modules/DirectoryLinksProvider.jsm @@ -91,19 +91,16 @@ const DEFAULT_TOTAL_FREQUENCY_CAP = 10; const DEFAULT_PRUNE_TIME_DELTA = 10*24*60*60*1000; // The min number of visible (not blocked) history tiles to have before showing suggested tiles const MIN_VISIBLE_HISTORY_TILES = 8; // The max number of visible (not blocked) history tiles to test for inadjacency const MAX_VISIBLE_HISTORY_TILES = 15; -// Divide frecency by this amount for pings -const PING_SCORE_DIVISOR = 10000; - // Allowed ping actions remotely stored as columns: case-insensitive [a-z0-9_] const PING_ACTIONS = ["block", "click", "pin", "sponsored", "sponsored_link", "unpin", "view"]; // Location of inadjacent sites json const INADJACENCY_SOURCE = "chrome://browser/content/newtab/newTab.inadjacent.json"; // Fake URL to keep track of last block of a suggested tile in the frequency cap object const FAKE_SUGGESTED_BLOCK_URL = "ignore://suggested_block"; @@ -561,60 +558,23 @@ var DirectoryLinksProvider = { let newtabEnhanced = false; let pingEndPoint = ""; try { newtabEnhanced = Services.prefs.getBoolPref(PREF_NEWTAB_ENHANCED); pingEndPoint = Services.prefs.getCharPref(PREF_DIRECTORY_PING); } catch (ex) {} - // Only send pings when enhancing tiles with an endpoint and valid action + // Bug 1240245 - We no longer send pings, but frequency capping and fetching + // tests depend on the following actions, so references to PING remain. let invalidAction = PING_ACTIONS.indexOf(action) == -1; if (!newtabEnhanced || pingEndPoint == "" || invalidAction) { return Promise.resolve(); } - let actionIndex; - let data = { - locale: this.locale, - tiles: sites.reduce((tiles, site, pos) => { - // Only add data for non-empty tiles - if (site) { - // Remember which tiles data triggered the action - let {link} = site; - let tilesIndex = tiles.length; - if (triggeringSiteIndex == pos) { - actionIndex = tilesIndex; - } - - // Make the payload in a way so keys can be excluded when stringified - let id = link.directoryId; - tiles.push({ - id: id || site.enhancedId, - pin: site.isPinned() ? 1 : undefined, - pos: pos != tilesIndex ? pos : undefined, - past_impressions: pos == triggeringSiteIndex ? pastImpressions : undefined, - score: Math.round(link.frecency / PING_SCORE_DIVISOR) || undefined, - url: site.enhancedId && "", - }); - } - return tiles; - }, []), - }; - - // Provide a direct index to the tile triggering the action - if (actionIndex !== undefined) { - data[action] = actionIndex; - } - - // Package the data to be sent with the ping - let ping = this._newXHR(); - ping.open("POST", pingEndPoint + (action == "view" ? "view" : "click")); - ping.send(JSON.stringify(data)); - return Task.spawn(function* () { // since we updated views/clicks we need write _frequencyCaps to disk yield this._writeFrequencyCapFile(); // Use this as an opportunity to potentially fetch new links yield this._fetchAndCacheLinksIfNecessary(); }.bind(this)); },
--- a/browser/modules/WindowsPreviewPerTab.jsm +++ b/browser/modules/WindowsPreviewPerTab.jsm @@ -366,18 +366,16 @@ XPCOMUtils.defineLazyGetter(PreviewContr * * @param win * The nsIDOMWindow browser window */ function TabWindow(win) { this.win = win; this.tabbrowser = win.gBrowser; - this.cacheDims(); - this.previews = new Map(); for (let i = 0; i < this.tabEvents.length; i++) this.tabbrowser.tabContainer.addEventListener(this.tabEvents[i], this, false); for (let i = 0; i < this.winEvents.length; i++) this.win.addEventListener(this.winEvents[i], this, false); @@ -389,28 +387,30 @@ function TabWindow(win) { this.newTab(tabs[i]); this.updateTabOrdering(); AeroPeek.checkPreviewCount(); } TabWindow.prototype = { _enabled: false, + _cachedWidth: 0, + _cachedHeight: 0, tabEvents: ["TabOpen", "TabClose", "TabSelect", "TabMove"], winEvents: ["resize"], destroy: function () { this._destroying = true; let tabs = this.tabbrowser.tabs; this.tabbrowser.removeTabsProgressListener(this); - for (let i = 0; i < this.winEvents.length; i++) - this.win.removeEventListener(this.winEvents[i], this, false); + for (let i = 0; i < this.winEvents.length; i++) + this.win.removeEventListener(this.winEvents[i], this, false); for (let i = 0; i < this.tabEvents.length; i++) this.tabbrowser.tabContainer.removeEventListener(this.tabEvents[i], this, false); for (let i = 0; i < tabs.length; i++) this.removeTab(tabs[i]); let idx = AeroPeek.windows.indexOf(this.win.gTaskbarTabGroup);
--- a/browser/modules/test/xpcshell/test_DirectoryLinksProvider.js +++ b/browser/modules/test/xpcshell/test_DirectoryLinksProvider.js @@ -711,123 +711,16 @@ add_task(function* test_frequencyCappedS // Cleanup. NewTabUtils.isTopPlacesSite = origIsTopPlacesSite; DirectoryLinksProvider._getCurrentTopSiteCount = origCurrentTopSiteCount; gLinks.removeProvider(DirectoryLinksProvider); DirectoryLinksProvider.removeObserver(gLinks); Services.prefs.setCharPref(kPingUrlPref, kPingUrl); }); -add_task(function* test_reportSitesAction() { - yield DirectoryLinksProvider.init(); - let deferred, expectedPath, expectedPost; - let done = false; - server.registerPrefixHandler(kPingPath, (aRequest, aResponse) => { - if (done) { - return; - } - - do_check_eq(aRequest.path, expectedPath); - - let bodyStream = new BinaryInputStream(aRequest.bodyInputStream); - let bodyObject = JSON.parse(NetUtil.readInputStreamToString(bodyStream, bodyStream.available())); - isIdentical(bodyObject, expectedPost); - - deferred.resolve(); - }); - - function sendPingAndTest(path, action, index) { - deferred = Promise.defer(); - expectedPath = kPingPath + path; - DirectoryLinksProvider.reportSitesAction(sites, action, index); - return deferred.promise; - } - - // Start with a single pinned link at position 3 - let sites = [,,{ - isPinned: _ => true, - link: { - directoryId: 1, - frecency: 30000, - url: "http://directory1/", - }, - }]; - - // Make sure we get the click ping for the directory link with fields we want - // and unwanted fields removed by stringify/parse - expectedPost = JSON.parse(JSON.stringify({ - click: 0, - locale: "en-US", - tiles: [{ - id: 1, - pin: 1, - pos: 2, - score: 3, - url: undefined, - }], - })); - yield sendPingAndTest("click", "click", 2); - - // Try a pin click ping - delete expectedPost.click; - expectedPost.pin = 0; - yield sendPingAndTest("click", "pin", 2); - - // Try a block click ping - delete expectedPost.pin; - expectedPost.block = 0; - yield sendPingAndTest("click", "block", 2); - - // A view ping has no actions - delete expectedPost.block; - expectedPost.view = 0; - yield sendPingAndTest("view", "view", 2); - - // Remove the identifier that makes it a directory link so just plain history - delete sites[2].link.directoryId; - delete expectedPost.tiles[0].id; - yield sendPingAndTest("view", "view", 2); - - // Add directory link at position 0 - sites[0] = { - isPinned: _ => false, - link: { - directoryId: 1234, - frecency: 1000, - url: "http://directory/", - } - }; - expectedPost.tiles.unshift(JSON.parse(JSON.stringify({ - id: 1234, - pin: undefined, - pos: undefined, - score: undefined, - url: undefined, - }))); - expectedPost.view = 1; - yield sendPingAndTest("view", "view", 2); - - // Make the history tile enhanced so it reports both id and url - sites[2].enhancedId = "id from enhanced"; - expectedPost.tiles[1].id = "id from enhanced"; - expectedPost.tiles[1].url = ""; - yield sendPingAndTest("view", "view", 2); - - // Click the 0th site / 0th tile - delete expectedPost.view; - expectedPost.click = 0; - yield sendPingAndTest("click", "click", 0); - - // Click the 2th site / 1th tile - expectedPost.click = 1; - yield sendPingAndTest("click", "click", 2); - - done = true; -}); - add_task(function* test_fetchAndCacheLinks_local() { yield DirectoryLinksProvider.init(); yield cleanJsonFile(); // Trigger cache of data or chrome uri files in profD yield DirectoryLinksProvider._fetchAndCacheLinks(kTestURL); let data = yield readJsonFile(); isIdentical(data, kURLData); }); @@ -1896,120 +1789,16 @@ add_task(function* test_inadjecentSites( gLinks.removeProvider(DirectoryLinksProvider); DirectoryLinksProvider._inadjacentSitesUrl = origInadjacentSitesUrl; NewTabUtils.isTopPlacesSite = origIsTopPlacesSite; NewTabUtils.getProviderLinks = origGetProviderLinks; DirectoryLinksProvider._getCurrentTopSiteCount = origCurrentTopSiteCount; yield promiseCleanDirectoryLinksProvider(); }); -add_task(function* test_reportPastImpressions() { - let origIsTopPlacesSite = NewTabUtils.isTopPlacesSite; - NewTabUtils.isTopPlacesSite = () => true; - let origCurrentTopSiteCount = DirectoryLinksProvider._getCurrentTopSiteCount; - DirectoryLinksProvider._getCurrentTopSiteCount = () => 8; - - let testUrl = "http://frequency.capped/link"; - let targets = ["top.site.com"]; - let data = { - suggested: [{ - type: "affiliate", - frecent_sites: targets, - url: testUrl, - adgroup_name: "Test" - }] - }; - let dataURI = "data:application/json," + JSON.stringify(data); - yield promiseSetupDirectoryLinksProvider({linksURL: dataURI}); - - // make DirectoryLinksProvider load json - let loadPromise = Promise.defer(); - DirectoryLinksProvider.getLinks(_ => {loadPromise.resolve();}); - yield loadPromise.promise; - - // setup ping handler - let deferred, expectedPath, expectedAction, expectedImpressions; - let done = false; - server.registerPrefixHandler(kPingPath, (aRequest, aResponse) => { - if (done) { - return; - } - - do_check_eq(aRequest.path, expectedPath); - - let bodyStream = new BinaryInputStream(aRequest.bodyInputStream); - let bodyObject = JSON.parse(NetUtil.readInputStreamToString(bodyStream, bodyStream.available())); - let expectedActionIndex = bodyObject[expectedAction]; - if (bodyObject.unpin) { - // unpin should not report past_impressions - do_check_false(bodyObject.tiles[expectedActionIndex].hasOwnProperty("past_impressions")); - } - else if (expectedImpressions) { - do_check_eq(bodyObject.tiles[expectedActionIndex].past_impressions.total, expectedImpressions); - do_check_eq(bodyObject.tiles[expectedActionIndex].past_impressions.daily, expectedImpressions); - } - else { - do_check_eq(expectedPath, "/ping/view"); - do_check_false(bodyObject.tiles[expectedActionIndex].hasOwnProperty("past_impressions")); - } - - deferred.resolve(); - }); - - // setup ping sender - function sendPingAndTest(path, action, index) { - deferred = Promise.defer(); - expectedPath = kPingPath + path; - expectedAction = action; - DirectoryLinksProvider.reportSitesAction(sites, action, index); - return deferred.promise; - } - - // Start with a view ping first - let site = { - isPinned: _ => false, - link: { - directoryId: 1, - frecency: 30000, - frecent_sites: targets, - targetedSite: targets[0], - url: testUrl - } - }; - let sites = [, - { - isPinned: _ => false, - link: {type: "history", url: "https://foo.com"} - }, - site - ]; - - yield sendPingAndTest("view", "view", 2); - yield sendPingAndTest("view", "view", 2); - yield sendPingAndTest("view", "view", 2); - - expectedImpressions = DirectoryLinksProvider._frequencyCaps[testUrl].totalViews; - do_check_eq(expectedImpressions, 3); - - // now report pin, unpin, block and click - sites.isPinned = _ => true; - yield sendPingAndTest("click", "pin", 2); - sites.isPinned = _ => false; - yield sendPingAndTest("click", "unpin", 2); - sites.isPinned = _ => false; - yield sendPingAndTest("click", "click", 2); - sites.isPinned = _ => false; - yield sendPingAndTest("click", "block", 2); - - // Cleanup. - done = true; - NewTabUtils.isTopPlacesSite = origIsTopPlacesSite; - DirectoryLinksProvider._getCurrentTopSiteCount = origCurrentTopSiteCount; -}); - add_task(function* test_blockSuggestedTiles() { // Initial setup let suggestedTile = suggestedTile1; let topSites = ["site0.com", "1040.com", "site2.com", "hrblock.com", "site4.com", "freetaxusa.com", "site6.com"]; let data = {"suggested": [suggestedTile1, suggestedTile2, suggestedTile3], "directory": [someOtherSite]}; let dataURI = 'data:application/json,' + JSON.stringify(data); yield promiseSetupDirectoryLinksProvider({linksURL: dataURI});
--- a/build/autoconf/config.status.m4 +++ b/build/autoconf/config.status.m4 @@ -226,14 +226,14 @@ define([AC_CONFIG_HEADER], [m4_fatal([Use CONFIGURE_DEFINE_FILES in moz.build files to produce header files.]) ]) define([MOZ_BUILD_BACKEND], [ BUILD_BACKENDS="RecursiveMake" MOZ_ARG_ENABLE_STRING(build-backend, -[ --enable-build-backend={AndroidEclipse,CppEclipse,VisualStudio,FasterMake,CompileDB,ChromeMap} +[ --enable-build-backend={$($(dirname ]$[0)/$1/mach python -c "from mozbuild.backend import backends; print ','.join(sorted(backends))")} Enable additional build backends], [ BUILD_BACKENDS="RecursiveMake `echo $enableval | sed 's/,/ /g'`"]) AC_SUBST_SET([BUILD_BACKENDS]) ])
--- a/build/unix/elfhack/elf.cpp +++ b/build/unix/elfhack/elf.cpp @@ -253,26 +253,29 @@ Elf::Elf(std::ifstream &file) } if (phdr.p_type == PT_PHDR) segment->addSection(phdr_section); for (int j = 1; j < ehdr->e_shnum; j++) if (phdr.contains(sections[j])) segment->addSection(sections[j]); // Make sure that our view of segments corresponds to the original // ELF file. - assert(segment->getFileSize() == phdr.p_filesz); + // GNU gold likes to start some segments before the first section + // they contain. https://sourceware.org/bugzilla/show_bug.cgi?id=19392 + unsigned int gold_adjustment = segment->getAddr() - phdr.p_vaddr; + assert(segment->getFileSize() == phdr.p_filesz - gold_adjustment); // gold makes TLS segments end on an aligned virtual address, even // when the underlying section ends before that, while bfd ld // doesn't. It's fine if we don't keep that alignment. unsigned int memsize = segment->getMemSize(); if (phdr.p_type == PT_TLS && memsize != phdr.p_memsz) { unsigned int align = segment->getAlign(); memsize = (memsize + align - 1) & ~(align - 1); } - assert(memsize == phdr.p_memsz); + assert(memsize == phdr.p_memsz - gold_adjustment); segments.push_back(segment); } new (&eh_entry) ElfLocation(ehdr->e_entry, this); } Elf::~Elf() {
--- a/config/external/nss/nss.symbols +++ b/config/external/nss/nss.symbols @@ -687,11 +687,16 @@ VFY_DestroyContext VFY_End VFY_EndWithSignature VFY_Update VFY_VerifyData VFY_VerifyDataWithAlgorithmID VFY_VerifyDigestDirect _SGN_VerifyPKCS1DigestInfo __PK11_SetCertificateNickname +# These symbols are not used by Firefox itself, but are used by Java's security +# libraries, which in turn are used by Java applets/plugins/etc. Provide them +# to make Java code happy. +NSS_VersionCheck +NSS_Initialize #ifdef NSS_EXTRA_SYMBOLS_FILE #include @NSS_EXTRA_SYMBOLS_FILE@ #endif
--- a/config/rules.mk +++ b/config/rules.mk @@ -1576,12 +1576,8 @@ default all:: $(PURGECACHES_FILES) ############################################################################# # Derived targets and dependencies include $(MOZILLA_DIR)/config/makefiles/autotargets.mk ifneq ($(NULL),$(AUTO_DEPS)) default all libs tools export:: $(AUTO_DEPS) endif - -export:: $(GENERATED_FILES) - -GARBAGE += $(GENERATED_FILES)
--- a/configure.in +++ b/configure.in @@ -134,17 +134,17 @@ EOF exit 1 break fi MOZ_BUILD_ROOT=`pwd -W 2>/dev/null || pwd -P` DIST="$MOZ_BUILD_ROOT/dist" MOZ_PYTHON -MOZ_BUILD_BACKEND +MOZ_BUILD_BACKEND(.) MOZ_DEFAULT_COMPILER COMPILE_ENVIRONMENT=1 MOZ_ARG_DISABLE_BOOL(compile-environment, [ --disable-compile-environment Disable compiler/library checks.], COMPILE_ENVIRONMENT= ) @@ -2283,16 +2283,21 @@ ia64*-hpux*) # tree. clang-cl doesn't support -fno-exceptions or equivalent, # so there doesn't seem to be any way to convince clang-cl to # declare |delete| differently. Therefore, suppress this # warning. CXXFLAGS="$CXXFLAGS -Wno-implicit-exception-spec-mismatch" # At least one MSVC header and several headers in-tree have # unused typedefs, so turn this on. CXXFLAGS="$CXXFLAGS -Wno-unused-local-typedef" + # Several JS engine header files use __declspec(dllimport) on + # classes, and clang-cl helpfully warns about its non-support + # for such cases. We're not particularly worried about that, + # so ignore that warning. + CXXFLAGS="$CXXFLAGS -Wno-ignored-attributes" fi # make 'foo == bar;' error out CFLAGS="$CFLAGS -we4553" CXXFLAGS="$CXXFLAGS -we4553" LIBS="$LIBS kernel32.lib user32.lib gdi32.lib winmm.lib wsock32.lib advapi32.lib secur32.lib netapi32.lib" MOZ_DEBUG_LDFLAGS='-DEBUG -DEBUGTYPE:CV' WARNINGS_AS_ERRORS='-WX' MOZ_OPTIMIZE_FLAGS='-O1 -Oi'
--- a/devtools/client/shared/browser-loader.js +++ b/devtools/client/shared/browser-loader.js @@ -65,16 +65,19 @@ function BrowserLoader(baseURI, window) if (!uri.startsWith(baseURI) && !isBrowserDir) { return devtools.require(uri); } return require(uri); }, globals: { + // Allow modules to use the window's console to ensure logs appear in a + // tab toolbox, if one exists, instead of just the browser console. + console: window.console, // Make sure 'define' function exists. This allows reusing AMD modules. define: function(callback) { callback(this.require, this.exports, this.module); return this.exports; } } };
--- a/devtools/shared/heapsnapshot/tests/gtest/DoesCrossZoneBoundaries.cpp +++ b/devtools/shared/heapsnapshot/tests/gtest/DoesCrossZoneBoundaries.cpp @@ -4,20 +4,22 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ // Test that heap snapshots cross zone boundaries when expected. #include "DevTools.h" DEF_TEST(DoesCrossZoneBoundaries, { // Create a new global to get a new zone. + JS::CompartmentOptions options; JS::RootedObject newGlobal(cx, JS_NewGlobalObject(cx, getGlobalClass(), nullptr, - JS::FireOnNewGlobalHook)); + JS::FireOnNewGlobalHook, + options)); ASSERT_TRUE(newGlobal); JS::Zone* newZone = nullptr; { JSAutoCompartment ac(cx, newGlobal); ASSERT_TRUE(JS_InitStandardClasses(cx, newGlobal)); newZone = js::GetContextZone(cx); } ASSERT_TRUE(newZone);
--- a/devtools/shared/heapsnapshot/tests/gtest/DoesntCrossZoneBoundaries.cpp +++ b/devtools/shared/heapsnapshot/tests/gtest/DoesntCrossZoneBoundaries.cpp @@ -4,20 +4,22 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ // Test that heap snapshots walk the zone boundaries correctly. #include "DevTools.h" DEF_TEST(DoesntCrossZoneBoundaries, { // Create a new global to get a new zone. + JS::CompartmentOptions options; JS::RootedObject newGlobal(cx, JS_NewGlobalObject(cx, getGlobalClass(), nullptr, - JS::FireOnNewGlobalHook)); + JS::FireOnNewGlobalHook, + options)); ASSERT_TRUE(newGlobal); JS::Zone* newZone = nullptr; { JSAutoCompartment ac(cx, newGlobal); ASSERT_TRUE(JS_InitStandardClasses(cx, newGlobal)); newZone = js::GetContextZone(cx); } ASSERT_TRUE(newZone);
--- a/dom/animation/EffectCompositor.cpp +++ b/dom/animation/EffectCompositor.cpp @@ -138,19 +138,20 @@ EffectCompositor::RequestRestyle(dom::El if (!mPresContext) { // Pres context will be null after the effect compositor is disconnected. return; } auto& elementsToRestyle = mElementsToRestyle[aCascadeLevel]; PseudoElementHashKey key = { aElement, aPseudoType }; - if (aRestyleType == RestyleType::Throttled && - !elementsToRestyle.Contains(key)) { - elementsToRestyle.Put(key, false); + if (aRestyleType == RestyleType::Throttled) { + if (!elementsToRestyle.Contains(key)) { + elementsToRestyle.Put(key, false); + } mPresContext->Document()->SetNeedStyleFlush(); } else { // Get() returns 0 if the element is not found. It will also return // false if the element is found but does not have a pending restyle. bool hasPendingRestyle = elementsToRestyle.Get(key); if (!hasPendingRestyle) { PostRestyleForAnimation(aElement, aPseudoType, aCascadeLevel); }
--- a/dom/animation/test/chrome.ini +++ b/dom/animation/test/chrome.ini @@ -1,8 +1,9 @@ [DEFAULT] support-files = testcommon.js ../../imptests/testharness.js ../../imptests/testharnessreport.js [chrome/test_animation_observers.html] +[chrome/test_restyles.html] [chrome/test_running_on_compositor.html] skip-if = buildapp == 'b2g'
new file mode 100644 --- /dev/null +++ b/dom/animation/test/chrome/test_restyles.html @@ -0,0 +1,328 @@ +<!doctype html> +<head> +<meta charset=utf-8> +<title>Tests restyles caused by animations</title> +<script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> +<script src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script> +<script src="chrome://mochikit/content/tests/SimpleTest/SpawnTask.js"></script> +<script src="chrome://mochikit/content/tests/SimpleTest/paint_listener.js"></script> +<script src="../testcommon.js"></script> +<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"> +<style> +@keyframes opacity { + from { opacity: 1; } + to { opacity: 0; } +} +@keyframes background-color { + from { background-color: red; } + to { background-color: blue; } +} +@keyframes rotate { + from { transform: rotate(0deg); } + to { transform: rotate(360deg); } +} +div { + /* Element needs geometry to be eligible for layerization */ + width: 100px; + height: 100px; + background-color: white; +} +</style> +</head> +<body> +<script> +'use strict'; + +function observeStyling(frameCount, onFrame) { + var docShell = window.QueryInterface(SpecialPowers.Ci.nsIInterfaceRequestor) + .getInterface(SpecialPowers.Ci.nsIWebNavigation) + .QueryInterface(SpecialPowers.Ci.nsIDocShell); + + docShell.recordProfileTimelineMarkers = true; + docShell.popProfileTimelineMarkers(); + + return new Promise(function(resolve) { + return waitForAnimationFrames(frameCount, onFrame).then(function() { + var markers = docShell.popProfileTimelineMarkers(); + docShell.recordProfileTimelineMarkers = false; + var stylingMarkers = markers.filter(function(marker, index) { + return marker.name == 'Styles'; + }); + resolve(stylingMarkers); + }); + }); +} + +SimpleTest.waitForExplicitFinish(); + +const OMTAPrefKey = 'layers.offmainthreadcomposition.async-animations'; +var omtaEnabled = SpecialPowers.DOMWindowUtils.layerManagerRemote && + SpecialPowers.getBoolPref(OMTAPrefKey); + +function add_task_if_omta_enabled(test) { + if (!omtaEnabled) { + info(test.name + " is skipped because OMTA is disabled"); + return; + } + add_task(test); +} + +// We need to wait for all paints before running tests to avoid contaminations +// from styling of this document itself. +waitForAllPaints(function() { + add_task_if_omta_enabled(function* no_restyling_for_compositor_animations() { + var div = addDiv(null, { style: 'animation: opacity 100s' }); + var animation = div.getAnimations()[0]; + + yield animation.ready; + ok(animation.isRunningOnCompositor); + + var markers = yield observeStyling(5); + is(markers.length, 0, + 'CSS animations running on the compositor should not update style ' + + 'on the main thread'); + div.remove(div); + }); + + add_task_if_omta_enabled(function* no_restyling_for_compositor_transitions() { + var div = addDiv(null, { style: 'transition: opacity 100s; opacity: 0' }); + getComputedStyle(div).opacity; + div.style.opacity = 1; + + var animation = div.getAnimations()[0]; + + yield animation.ready; + ok(animation.isRunningOnCompositor); + + var markers = yield observeStyling(5); + is(markers.length, 0, + 'CSS transitions running on the compositor should not update style ' + + 'on the main thread'); + div.remove(div); + }); + + add_task_if_omta_enabled(function* no_restyling_when_animation_duration_is_changed() { + var div = addDiv(null, { style: 'animation: opacity 100s' }); + var animation = div.getAnimations()[0]; + + yield animation.ready; + ok(animation.isRunningOnCompositor); + + div.animationDuration = '200s'; + + var markers = yield observeStyling(5); + is(markers.length, 0, + 'Animations running on the compositor should not update style ' + + 'on the main thread'); + div.remove(div); + }); + + add_task_if_omta_enabled(function* only_one_restyling_after_finish_is_called() { + var div = addDiv(null, { style: 'animation: opacity 100s' }); + var animation = div.getAnimations()[0]; + + yield animation.ready; + ok(animation.isRunningOnCompositor); + + animation.finish(); + + var markers = yield observeStyling(5); + is(markers.length, 1, + 'Animations running on the compositor should only update style ' + + 'once after finish() is called'); + div.remove(div); + }); + + add_task(function* no_restyling_mouse_movement_on_finished_transition() { + var div = addDiv(null, { style: 'transition: opacity 1ms; opacity: 0' }); + getComputedStyle(div).opacity; + div.style.opacity = 1; + + var animation = div.getAnimations()[0]; + var initialRect = div.getBoundingClientRect(); + + yield animation.finished; + + var mouseX = initialRect.left + initialRect.width / 2; + var mouseY = initialRect.top + initialRect.height / 2; + var markers = yield observeStyling(5, function() { + // We can't use synthesizeMouse here since synthesizeMouse causes + // layout flush. + synthesizeMouseAtPoint(mouseX++, mouseY++, + { type: 'mousemove' }, window); + }); + + is(markers.length, 0, + 'Bug 1219236: Finished transitions should never cause restyles ' + + 'when mouse is moved on the animations'); + div.remove(div); + }); + + add_task(function* no_restyling_mouse_movement_on_finished_animation() { + var div = addDiv(null, { style: 'animation: opacity 1ms' }); + var animation = div.getAnimations()[0]; + + var initialRect = div.getBoundingClientRect(); + + yield animation.finished; + + var mouseX = initialRect.left + initialRect.width / 2; + var mouseY = initialRect.top + initialRect.height / 2; + var markers = yield observeStyling(5, function() { + // We can't use synthesizeMouse here since synthesizeMouse causes + // layout flush. + synthesizeMouseAtPoint(mouseX++, mouseY++, + { type: 'mousemove' }, window); + }); + + is(markers.length, 0, + 'Bug 1219236: Finished animations should never cause restyles ' + + 'when mouse is moved on the animations'); + div.remove(div); + }); + + add_task_if_omta_enabled(function* no_restyling_compositor_animations_out_of_view_element() { + var div = addDiv(null, + { style: 'animation: opacity 100s; transform: translateY(-400px);' }); + var animation = div.getAnimations()[0]; + + yield animation.ready; + ok(!animation.isRunningOnCompositor); + + var markers = yield observeStyling(5); + + todo_is(markers.length, 0, + 'Bug 1166500: Animations running on the compositor in out of ' + + 'view element should never cause restyles'); + div.remove(div); + }); + + add_task(function* no_restyling_main_thread_animations_out_of_view_element() { + var div = addDiv(null, + { style: 'animation: background-color 100s; transform: translateY(-400px);' }); + var animation = div.getAnimations()[0]; + + yield animation.ready; + var markers = yield observeStyling(5); + + todo_is(markers.length, 0, + 'Bug 1166500: Animations running on the main-thread in out of ' + + 'view element should never cause restyles'); + div.remove(div); + }); + + /* + Disabled for now since, on Android, the opacity animation runs on the + compositor even if it is scrolled out of view. + We will fix this in bug 1166500 or a follow-up bug + add_task_if_omta_enabled(function* no_restyling_compositor_animations_in_scrolled_out_element() { + var parentElement = addDiv(null, + { style: 'overflow-y: scroll; height: 20px;' }); + var div = addDiv(null, + { style: 'animation: opacity 100s; position: relative; top: 100px;' }); + parentElement.appendChild(div); + var animation = div.getAnimations()[0]; + + yield animation.ready; + ok(!animation.isRunningOnCompositor); + + var markers = yield observeStyling(5); + + todo_is(markers.length, 0, + 'Bug 1166500: Animations running on the compositor in elements ' + + 'which are scrolled out should never cause restyles'); + parentElement.remove(div); + }); + */ + + add_task(function* no_restyling_main_thread_animations_in_scrolled_out_element() { + var parentElement = addDiv(null, + { style: 'overflow-y: scroll; height: 20px;' }); + var div = addDiv(null, + { style: 'animation: background-color 100s; position: relative; top: 100px;' }); + parentElement.appendChild(div); + var animation = div.getAnimations()[0]; + + yield animation.ready; + var markers = yield observeStyling(5); + + todo_is(markers.length, 0, + 'Bug 1166500: Animations running on the main-thread in elements ' + + 'which are scrolled out should never cause restyles'); + parentElement.remove(div); + }); + + /* + Disabled for now since, on Android and B2G, the opacity animation runs on the + compositor even if the associated element has visibility:hidden. + We will fix this in bug 1237454 or a follow-up bug. + add_task_if_omta_enabled(function* no_restyling_compositor_animations_in_visiblily_hidden_element() { + var div = addDiv(null, + { style: 'animation: opacity 100s; visibility: hidden' }); + var animation = div.getAnimations()[0]; + + yield animation.ready; + ok(!animation.isRunningOnCompositor); + + var markers = yield observeStyling(5); + + todo_is(markers.length, 0, + 'Bug 1237454: Animations running on the compositor in ' + + 'visibility hidden element should never cause restyles'); + div.remove(div); + }); + */ + + add_task(function* no_restyling_main_thread_animations_in_visiblily_hidden_element() { + var div = addDiv(null, + { style: 'animation: background-color 100s; visibility: hidden' }); + var animation = div.getAnimations()[0]; + + yield animation.ready; + var markers = yield observeStyling(5); + + todo_is(markers.length, 0, + 'Bug 1237454: Animations running on the main-thread in ' + + 'visibility hidden element should never cause restyles'); + div.remove(div); + }); + + add_task_if_omta_enabled(function* no_restyling_compositor_animations_after_pause_is_called() { + var div = addDiv(null, { style: 'animation: opacity 100s' }); + var animation = div.getAnimations()[0]; + + yield animation.ready; + ok(animation.isRunningOnCompositor); + + animation.pause(); + + yield animation.ready; + + var markers = yield observeStyling(5); + is(markers.length, 0, + 'Bug 1232563: Paused animations running on the compositor should ' + + 'never cause restyles once after pause() is called'); + div.remove(div); + }); + + add_task(function* no_restyling_matn_thread_animations_after_pause_is_called() { + var div = addDiv(null, { style: 'animation: background-color 100s' }); + var animation = div.getAnimations()[0]; + + yield animation.ready; + + animation.pause(); + + yield animation.ready; + + var markers = yield observeStyling(5); + is(markers.length, 0, + 'Bug 1232563: Paused animations running on the main-thread should ' + + 'never cause restyles after pause() is called'); + div.remove(div); + }); + +}); + +</script> +</body>
--- a/dom/animation/test/testcommon.js +++ b/dom/animation/test/testcommon.js @@ -36,23 +36,29 @@ function waitForFrame() { return new Promise(function(resolve, reject) { window.requestAnimationFrame(resolve); }); } /** * Returns a Promise that is resolved after the given number of consecutive * animation frames have occured (using requestAnimationFrame callbacks). + * + * @param frameCount The number of animation frames. + * @param onFrame An optional function to be processed in each animation frame. */ -function waitForAnimationFrames(frameCount) { +function waitForAnimationFrames(frameCount, onFrame) { return new Promise(function(resolve, reject) { function handleFrame() { if (--frameCount <= 0) { resolve(); } else { + if (onFrame && typeof onFrame === 'function') { + onFrame(); + } window.requestAnimationFrame(handleFrame); // wait another frame } } window.requestAnimationFrame(handleFrame); }); } /**
--- a/dom/apps/Webapps.jsm +++ b/dom/apps/Webapps.jsm @@ -2046,17 +2046,17 @@ this.DOMApplicationRegistry = { }, id: aApp.id }); let appURI = NetUtil.newURI(aApp.origin, null, null); let principal = Services.scriptSecurityManager.createCodebasePrincipal(appURI, {appId: aApp.localId}); let cacheUpdate = updateSvc.scheduleAppUpdate( - appcacheURI, docURI, principal, aApp.localId, false, aProfileDir); + appcacheURI, docURI, principal, aProfileDir); // We save the download details for potential further usage like // cancelling it. let download = { cacheUpdate: cacheUpdate, appId: this._appIdForManifestURL(aApp.manifestURL), previousState: aIsUpdate ? "installed" : "pending" }; @@ -2200,17 +2200,17 @@ this.DOMApplicationRegistry = { new ManifestHelper(manifest, aData.origin, aData.manifestURL); debug("onlyCheckAppCache - launch updateSvc.checkForUpdate for " + helper.fullAppcachePath()); let appURI = NetUtil.newURI(aApp.origin, null, null); let principal = Services.scriptSecurityManager.createCodebasePrincipal(appURI, {appId: aApp.localId}); updateSvc.checkForUpdate(Services.io.newURI(helper.fullAppcachePath(), null, null), - principal, app.localId, false, updateObserver); + principal, updateObserver); }); return; } // On xhr load request event function onload(xhr, oldManifest) { debug("Got http status=" + xhr.status + " for " + aData.manifestURL); let oldHash = app.manifestHash; @@ -2474,18 +2474,17 @@ this.DOMApplicationRegistry = { let updateDeferred = Promise.defer(); let appURI = NetUtil.newURI(aApp.origin, null, null); let principal = Services.scriptSecurityManager.createCodebasePrincipal(appURI, {appId: aApp.localId}); updateSvc.checkForUpdate(Services.io.newURI(manifest.fullAppcachePath(), null, null), - principal, aApp.localId, false, - (aSubject, aTopic, aData) => updateDeferred.resolve(aTopic)); + principal, (aSubject, aTopic, aData) => updateDeferred.resolve(aTopic)); let topic = yield updateDeferred.promise; debug("updateHostedApp: updateSvc.checkForUpdate return for " + aApp.manifestURL + " - event is " + topic); let eventType = topic == "offline-cache-update-available" ? "downloadavailable"
--- a/dom/base/DOMMatrix.h +++ b/dom/base/DOMMatrix.h @@ -137,17 +137,17 @@ protected: virtual ~DOMMatrixReadOnly() {} private: DOMMatrixReadOnly() = delete; DOMMatrixReadOnly(const DOMMatrixReadOnly&) = delete; DOMMatrixReadOnly& operator=(const DOMMatrixReadOnly&) = delete; }; -class DOMMatrix final : public DOMMatrixReadOnly +class DOMMatrix : public DOMMatrixReadOnly { public: explicit DOMMatrix(nsISupports* aParent) : DOMMatrixReadOnly(aParent) {} DOMMatrix(nsISupports* aParent, const DOMMatrixReadOnly& other) : DOMMatrixReadOnly(aParent, other) @@ -239,16 +239,18 @@ public: DOMMatrix* RotateAxisAngleSelf(double aX, double aY, double aZ, double aAngle); DOMMatrix* SkewXSelf(double aSx); DOMMatrix* SkewYSelf(double aSy); DOMMatrix* InvertSelf(); DOMMatrix* SetMatrixValue(const nsAString& aTransformList, ErrorResult& aRv); -private: +protected: void Ensure3DMatrix(); + + virtual ~DOMMatrix() {} }; } // namespace dom } // namespace mozilla #endif /*MOZILLA_DOM_DOMMATRIX_H_*/
--- a/dom/base/Element.cpp +++ b/dom/base/Element.cpp @@ -2910,32 +2910,32 @@ Element::PreHandleEventForLinks(EventCha nsresult rv = NS_OK; // We do the status bar updates in PreHandleEvent so that the status bar gets // updated even if the event is consumed before we have a chance to set it. switch (aVisitor.mEvent->mMessage) { // Set the status bar similarly for mouseover and focus case eMouseOver: aVisitor.mEventStatus = nsEventStatus_eConsumeNoDefault; - // FALL THROUGH + MOZ_FALLTHROUGH; case eFocus: { InternalFocusEvent* focusEvent = aVisitor.mEvent->AsFocusEvent(); if (!focusEvent || !focusEvent->isRefocus) { nsAutoString target; GetLinkTarget(target); nsContentUtils::TriggerLink(this, aVisitor.mPresContext, absURI, target, false, true, true); // Make sure any ancestor links don't also TriggerLink aVisitor.mEvent->mFlags.mMultipleActionsPrevented = true; } break; } case eMouseOut: aVisitor.mEventStatus = nsEventStatus_eConsumeNoDefault; - // FALL THROUGH + MOZ_FALLTHROUGH; case eBlur: rv = LeaveLink(aVisitor.mPresContext); if (NS_SUCCEEDED(rv)) { aVisitor.mEvent->mFlags.mMultipleActionsPrevented = true; } break; default:
--- a/dom/base/File.cpp +++ b/dom/base/File.cpp @@ -953,33 +953,33 @@ BlobImplFile::LookupAndCacheIsDirectory( "This should only be called when this object has been created " "from an nsIFile to note that the nsIFile is a directory"); bool isDir; mFile->IsDirectory(&isDir); mDirState = isDir ? BlobDirState::eIsDir : BlobDirState::eIsNotDir; } //////////////////////////////////////////////////////////////////////////// -// BlobImplEmptyFile implementation +// EmptyBlobImpl implementation -NS_IMPL_ISUPPORTS_INHERITED0(BlobImplEmptyFile, BlobImpl) +NS_IMPL_ISUPPORTS_INHERITED0(EmptyBlobImpl, BlobImpl) already_AddRefed<BlobImpl> -BlobImplEmptyFile::CreateSlice(uint64_t aStart, uint64_t aLength, - const nsAString& aContentType, - ErrorResult& aRv) +EmptyBlobImpl::CreateSlice(uint64_t aStart, uint64_t aLength, + const nsAString& aContentType, + ErrorResult& aRv) { MOZ_ASSERT(!aStart && !aLength); - RefPtr<BlobImpl> impl = new BlobImplEmptyFile(aContentType); + RefPtr<BlobImpl> impl = new EmptyBlobImpl(aContentType); return impl.forget(); } void -BlobImplEmptyFile::GetInternalStream(nsIInputStream** aStream, - ErrorResult& aRv) +EmptyBlobImpl::GetInternalStream(nsIInputStream** aStream, + ErrorResult& aRv) { nsresult rv = NS_NewCStringInputStream(aStream, EmptyCString()); if (NS_WARN_IF(NS_FAILED(rv))) { aRv.Throw(rv); return; } }
--- a/dom/base/File.h +++ b/dom/base/File.h @@ -65,16 +65,17 @@ class Blob : public nsIDOMBlob public: NS_DECL_NSIDOMBLOB NS_DECL_NSIXHRSENDABLE NS_DECL_NSIMUTABLE NS_DECL_CYCLE_COLLECTING_ISUPPORTS NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_AMBIGUOUS(Blob, nsIDOMBlob) + // This creates a Blob or a File based on the type of BlobImpl. static Blob* Create(nsISupports* aParent, BlobImpl* aImpl); static already_AddRefed<Blob> Create(nsISupports* aParent, const nsAString& aContentType, uint64_t aLength); static already_AddRefed<Blob> @@ -828,37 +829,37 @@ private: CreateSlice(uint64_t aStart, uint64_t aLength, const nsAString& aContentType, ErrorResult& aRv) override; nsCOMPtr<nsIFile> mFile; bool mWholeFile; bool mIsTemporary; }; -class BlobImplEmptyFile final : public BlobImplBase +class EmptyBlobImpl final : public BlobImplBase { public: NS_DECL_ISUPPORTS_INHERITED - explicit BlobImplEmptyFile(const nsAString& aContentType) - : BlobImplBase(EmptyString(), aContentType, 0 /* aLength */) + explicit EmptyBlobImpl(const nsAString& aContentType) + : BlobImplBase(aContentType, 0 /* aLength */) {} virtual void GetInternalStream(nsIInputStream** aStream, ErrorResult& aRv) override; virtual already_AddRefed<BlobImpl> CreateSlice(uint64_t aStart, uint64_t aLength, const nsAString& aContentType, ErrorResult& aRv) override; virtual bool IsMemoryFile() const override { return true; } private: - ~BlobImplEmptyFile() {} + ~EmptyBlobImpl() {} }; } // namespace dom } // namespace mozilla #endif // mozilla_dom_File_h
--- a/dom/base/FileReader.cpp +++ b/dom/base/FileReader.cpp @@ -93,64 +93,56 @@ public: void FileReader::RootResultArrayBuffer() { mozilla::HoldJSObjects(this); } //FileReader constructors/initializers -FileReader::FileReader(nsPIDOMWindow* aWindow, +FileReader::FileReader(nsIGlobalObject* aGlobal, WorkerPrivate* aWorkerPrivate) - : DOMEventTargetHelper(aWindow) + : DOMEventTargetHelper(aGlobal) , mFileData(nullptr) , mDataLen(0) , mDataFormat(FILE_AS_BINARY) , mResultArrayBuffer(nullptr) , mProgressEventWasDelayed(false) , mTimerIsActive(false) , mReadyState(EMPTY) , mTotal(0) , mTransferred(0) , mTarget(do_GetCurrentThread()) , mBusyCount(0) , mWorkerPrivate(aWorkerPrivate) { - MOZ_ASSERT_IF(!NS_IsMainThread(), mWorkerPrivate && !aWindow); - MOZ_ASSERT_IF(NS_IsMainThread(), !mWorkerPrivate); + MOZ_ASSERT(aGlobal); + MOZ_ASSERT(NS_IsMainThread() == !mWorkerPrivate); SetDOMStringToNull(mResult); } FileReader::~FileReader() { Shutdown(); DropJSObjects(this); } /* static */ already_AddRefed<FileReader> FileReader::Constructor(const GlobalObject& aGlobal, ErrorResult& aRv) { - // The owner can be null when this object is used by chrome code. - nsCOMPtr<nsPIDOMWindow> owner = do_QueryInterface(aGlobal.GetAsSupports()); + nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports()); WorkerPrivate* workerPrivate = nullptr; if (!NS_IsMainThread()) { JSContext* cx = aGlobal.Context(); workerPrivate = GetWorkerPrivateFromContext(cx); MOZ_ASSERT(workerPrivate); } - RefPtr<FileReader> fileReader = new FileReader(owner, workerPrivate); - - if (!owner && nsContentUtils::ThreadsafeIsCallerChrome()) { - // Instead of grabbing some random global from the context stack, - // let's use the default one (junk scope) for now. - // We should move away from this Init... - fileReader->BindToOwner(xpc::NativeGlobal(xpc::PrivilegedJunkScope())); - } + RefPtr<FileReader> fileReader = new FileReader(global, workerPrivate); return fileReader.forget(); } // nsIInterfaceRequestor NS_IMETHODIMP FileReader::GetInterface(const nsIID & aIID, void **aResult) @@ -237,27 +229,17 @@ FileReader::DoOnLoadEnd(nsresult aStatus aSuccessEvent = NS_LITERAL_STRING(LOAD_STR); aTerminationEvent = NS_LITERAL_STRING(LOADEND_STR); nsresult rv = NS_OK; switch (mDataFormat) { case FILE_AS_ARRAYBUFFER: { AutoJSAPI jsapi; - nsCOMPtr<nsIGlobalObject> globalObject; - - if (NS_IsMainThread()) { - globalObject = do_QueryInterface(GetParentObject()); - } else { - MOZ_ASSERT(mWorkerPrivate); - MOZ_ASSERT(mBusyCount); - globalObject = mWorkerPrivate->GlobalScope(); - } - - if (!globalObject || !jsapi.Init(globalObject)) { + if (!jsapi.Init(GetParentObject())) { FreeFileData(); return NS_ERROR_FAILURE; } RootResultArrayBuffer(); mResultArrayBuffer = JS_NewArrayBufferWithContents(jsapi.cx(), mDataLen, mFileData); if (!mResultArrayBuffer) { JS_ClearPendingException(jsapi.cx()); @@ -360,16 +342,24 @@ FileReader::ReadFileContent(Blob& aBlob, eDataFormat aDataFormat, ErrorResult& aRv) { //Implicit abort to clear any other activity going on ErrorResult error; Abort(error); error.SuppressException(); + if (mReadyState == LOADING) { + // A nested ReadAsSomething() as been called during one of the events + // dispatched by Abort(). We have to terminate this operation in order to + // continue the nested one. + aRv.Throw(NS_ERROR_ABORT); + return; + } + mError = nullptr; SetDOMStringToNull(mResult); mTransferred = 0; mTotal = 0; mReadyState = EMPTY; FreeFileData(); mBlob = &aBlob;
--- a/dom/base/FileReader.h +++ b/dom/base/FileReader.h @@ -41,17 +41,17 @@ class FileReader final : public DOMEvent public nsSupportsWeakReference, public nsIInputStreamCallback, public nsITimerCallback, public workers::WorkerFeature { friend class FileReaderDecreaseBusyCounter; public: - FileReader(nsPIDOMWindow* aWindow, + FileReader(nsIGlobalObject* aGlobal, workers::WorkerPrivate* aWorkerPrivate); NS_DECL_ISUPPORTS_INHERITED NS_DECL_NSITIMERCALLBACK NS_DECL_NSIINPUTSTREAMCALLBACK NS_DECL_NSIINTERFACEREQUESTOR
--- a/dom/base/FormData.cpp +++ b/dom/base/FormData.cpp @@ -18,42 +18,26 @@ using namespace mozilla::dom; FormData::FormData(nsISupports* aOwner) : nsFormSubmission(NS_LITERAL_CSTRING("UTF-8"), nullptr) , mOwner(aOwner) { } namespace { -// Implements steps 3 and 4 of the "create an entry" algorithm of FormData. -already_AddRefed<File> -CreateNewFileInstance(Blob& aBlob, const Optional<nsAString>& aFilename, - ErrorResult& aRv) +already_AddRefed<Blob> +GetBlobForFormDataStorage(Blob& aBlob, const Optional<nsAString>& aFilename, + ErrorResult& aRv) { - // Step 3 "If value is a Blob object and not a File object, set value to - // a new File object, representing the same bytes, whose name attribute value - // is "blob"." - // Step 4 "If value is a File object and filename is given, set value to - // a new File object, representing the same bytes, whose name attribute - // value is filename." - nsAutoString filename; - if (aFilename.WasPassed()) { - filename = aFilename.Value(); - } else { - // If value is already a File and filename is not passed, the spec says not - // to create a new instance. - RefPtr<File> file = aBlob.ToFile(); - if (file) { - return file.forget(); - } - - filename = NS_LITERAL_STRING("blob"); + if (!aFilename.WasPassed()) { + RefPtr<Blob> blob = &aBlob; + return blob.forget(); } - RefPtr<File> file = aBlob.ToFile(filename, aRv); + RefPtr<File> file = aBlob.ToFile(aFilename.Value(), aRv); if (NS_WARN_IF(aRv.Failed())) { return nullptr; } return file.forget(); } } // namespace @@ -73,17 +57,17 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(Fo NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER NS_IMPL_CYCLE_COLLECTION_UNLINK_END NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(FormData) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOwner) for (uint32_t i = 0, len = tmp->mFormData.Length(); i < len; ++i) { ImplCycleCollectionTraverse(cb, tmp->mFormData[i].value, - "mFormData[i].GetAsFile()", 0); + "mFormData[i].GetAsBlob()", 0); } NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(FormData) NS_IMPL_CYCLE_COLLECTING_ADDREF(FormData) @@ -111,59 +95,59 @@ FormData::Append(const nsAString& aName, ErrorResult& aRv) { AddNameValuePair(aName, aValue); } void FormData::Append(const nsAString& aName, Blob& aBlob, const Optional<nsAString>& aFilename, - ErrorResult& aRv) + ErrorResult& aRv) { - RefPtr<File> file = CreateNewFileInstance(aBlob, aFilename, aRv); + RefPtr<Blob> blob = GetBlobForFormDataStorage(aBlob, aFilename, aRv); if (NS_WARN_IF(aRv.Failed())) { return; } - AddNameFilePair(aName, file); + AddNameBlobPair(aName, blob); } void FormData::Delete(const nsAString& aName) { // We have to use this slightly awkward for loop since uint32_t >= 0 is an // error for being always true. for (uint32_t i = mFormData.Length(); i-- > 0; ) { if (aName.Equals(mFormData[i].name)) { mFormData.RemoveElementAt(i); } } } void FormData::Get(const nsAString& aName, - Nullable<OwningFileOrUSVString>& aOutValue) + Nullable<OwningBlobOrUSVString>& aOutValue) { for (uint32_t i = 0; i < mFormData.Length(); ++i) { if (aName.Equals(mFormData[i].name)) { aOutValue.SetValue() = mFormData[i].value; return; } } aOutValue.SetNull(); } void FormData::GetAll(const nsAString& aName, - nsTArray<OwningFileOrUSVString>& aValues) + nsTArray<OwningBlobOrUSVString>& aValues) { for (uint32_t i = 0; i < mFormData.Length(); ++i) { if (aName.Equals(mFormData[i].name)) { - OwningFileOrUSVString* element = aValues.AppendElement(); + OwningBlobOrUSVString* element = aValues.AppendElement(); *element = mFormData[i].value; } } } bool FormData::Has(const nsAString& aName) { @@ -172,22 +156,22 @@ FormData::Has(const nsAString& aName) return true; } } return false; } nsresult -FormData::AddNameFilePair(const nsAString& aName, File* aFile) +FormData::AddNameBlobPair(const nsAString& aName, Blob* aBlob) { - MOZ_ASSERT(aFile); + MOZ_ASSERT(aBlob); FormDataTuple* data = mFormData.AppendElement(); - SetNameFilePair(data, aName, aFile); + SetNameBlobPair(data, aName, aBlob); return NS_OK; } FormData::FormDataTuple* FormData::RemoveAllOthersAndGetFirstFormDataTuple(const nsAString& aName) { FormDataTuple* lastFoundTuple = nullptr; uint32_t lastFoundIndex = mFormData.Length(); @@ -210,22 +194,22 @@ FormData::RemoveAllOthersAndGetFirstForm void FormData::Set(const nsAString& aName, Blob& aBlob, const Optional<nsAString>& aFilename, ErrorResult& aRv) { FormDataTuple* tuple = RemoveAllOthersAndGetFirstFormDataTuple(aName); if (tuple) { - RefPtr<File> file = CreateNewFileInstance(aBlob, aFilename, aRv); + RefPtr<Blob> blob = GetBlobForFormDataStorage(aBlob, aFilename, aRv); if (NS_WARN_IF(aRv.Failed())) { return; } - SetNameFilePair(tuple, aName, file); + SetNameBlobPair(tuple, aName, blob); } else { Append(aName, aBlob, aFilename, aRv); } } void FormData::Set(const nsAString& aName, const nsAString& aValue, ErrorResult& aRv) @@ -246,17 +230,17 @@ FormData::GetIterableLength() const const nsAString& FormData::GetKeyAtIndex(uint32_t aIndex) const { MOZ_ASSERT(aIndex < mFormData.Length()); return mFormData[aIndex].name; } -const OwningFileOrUSVString& +const OwningBlobOrUSVString& FormData::GetValueAtIndex(uint32_t aIndex) const { MOZ_ASSERT(aIndex < mFormData.Length()); return mFormData[aIndex].value; } void FormData::SetNameValuePair(FormDataTuple* aData, @@ -264,25 +248,25 @@ FormData::SetNameValuePair(FormDataTuple const nsAString& aValue) { MOZ_ASSERT(aData); aData->name = aName; aData->value.SetAsUSVString() = aValue; } void -FormData::SetNameFilePair(FormDataTuple* aData, +FormData::SetNameBlobPair(FormDataTuple* aData, const nsAString& aName, - File* aFile) + Blob* aBlob) { MOZ_ASSERT(aData); - MOZ_ASSERT(aFile); + MOZ_ASSERT(aBlob); aData->name = aName; - aData->value.SetAsFile() = aFile; + aData->value.SetAsBlob() = aBlob; } // ------------------------------------------------------------------------- // nsIDOMFormData NS_IMETHODIMP FormData::Append(const nsAString& aName, nsIVariant* aValue) { @@ -353,18 +337,31 @@ FormData::Constructor(const GlobalObject NS_IMETHODIMP FormData::GetSendInfo(nsIInputStream** aBody, uint64_t* aContentLength, nsACString& aContentType, nsACString& aCharset) { nsFSMultipartFormData fs(NS_LITERAL_CSTRING("UTF-8"), nullptr); for (uint32_t i = 0; i < mFormData.Length(); ++i) { - if (mFormData[i].value.IsFile()) { - fs.AddNameFilePair(mFormData[i].name, mFormData[i].value.GetAsFile()); + if (mFormData[i].value.IsBlob()) { + RefPtr<File> file = mFormData[i].value.GetAsBlob()->ToFile(); + if (file) { + fs.AddNameBlobPair(mFormData[i].name, file); + continue; + } + + ErrorResult rv; + file = + mFormData[i].value.GetAsBlob()->ToFile(NS_LITERAL_STRING("blob"), rv); + if (NS_WARN_IF(rv.Failed())) { + return rv.StealNSResult(); + } + + fs.AddNameBlobPair(mFormData[i].name, file); } else if (mFormData[i].value.IsUSVString()) { fs.AddNameValuePair(mFormData[i].name, mFormData[i].value.GetAsUSVString()); } else { MOZ_CRASH("This should no be possible."); } }
--- a/dom/base/FormData.h +++ b/dom/base/FormData.h @@ -30,31 +30,31 @@ class FormData final : public nsIDOMForm public nsWrapperCache { private: ~FormData() {} struct FormDataTuple { nsString name; - OwningFileOrUSVString value; + OwningBlobOrUSVString value; }; // Returns the FormDataTuple to modify. This may be null, in which case // no element with aName was found. FormDataTuple* RemoveAllOthersAndGetFirstFormDataTuple(const nsAString& aName); void SetNameValuePair(FormDataTuple* aData, const nsAString& aName, const nsAString& aValue); - void SetNameFilePair(FormDataTuple* aData, + void SetNameBlobPair(FormDataTuple* aData, const nsAString& aName, - File* aFile); + Blob* aBlob); public: explicit FormData(nsISupports* aOwner = nullptr); NS_DECL_CYCLE_COLLECTING_ISUPPORTS NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_AMBIGUOUS(FormData, nsIDOMFormData) @@ -79,51 +79,51 @@ public: void Append(const nsAString& aName, const nsAString& aValue, ErrorResult& aRv); void Append(const nsAString& aName, Blob& aBlob, const Optional<nsAString>& aFilename, ErrorResult& aRv); void Delete(const nsAString& aName); - void Get(const nsAString& aName, Nullable<OwningFileOrUSVString>& aOutValue); + void Get(const nsAString& aName, Nullable<OwningBlobOrUSVString>& aOutValue); - void GetAll(const nsAString& aName, nsTArray<OwningFileOrUSVString>& aValues); + void GetAll(const nsAString& aName, nsTArray<OwningBlobOrUSVString>& aValues); bool Has(const nsAString& aName); void Set(const nsAString& aName, Blob& aBlob, const Optional<nsAString>& aFilename, ErrorResult& aRv); void Set(const nsAString& aName, const nsAString& aValue, ErrorResult& aRv); uint32_t GetIterableLength() const; const nsAString& GetKeyAtIndex(uint32_t aIndex) const; - const OwningFileOrUSVString& GetValueAtIndex(uint32_t aIndex) const; + const OwningBlobOrUSVString& GetValueAtIndex(uint32_t aIndex) const; // nsFormSubmission virtual nsresult GetEncodedSubmission(nsIURI* aURI, nsIInputStream** aPostDataStream) override; virtual nsresult AddNameValuePair(const nsAString& aName, const nsAString& aValue) override { FormDataTuple* data = mFormData.AppendElement(); SetNameValuePair(data, aName, aValue); return NS_OK; } - virtual nsresult AddNameFilePair(const nsAString& aName, - File* aFile) override; + virtual nsresult AddNameBlobPair(const nsAString& aName, + Blob* aBlob) override; typedef bool (*FormDataEntryCallback)(const nsString& aName, - const OwningFileOrUSVString& aValue, + const OwningBlobOrUSVString& aValue, void* aClosure); uint32_t Length() const { return mFormData.Length(); }
--- a/dom/base/StructuredCloneHolder.cpp +++ b/dom/base/StructuredCloneHolder.cpp @@ -831,24 +831,23 @@ ReadFormData(JSContext* aCx, return nullptr; } if (tag == SCTAG_DOM_BLOB) { MOZ_ASSERT(indexOrLengthOfString < aHolder->BlobImpls().Length()); RefPtr<BlobImpl> blobImpl = aHolder->BlobImpls()[indexOrLengthOfString]; - MOZ_ASSERT(blobImpl->IsFile()); - RefPtr<File> file = - File::Create(aHolder->ParentDuringRead(), blobImpl); - MOZ_ASSERT(file); + RefPtr<Blob> blob = + Blob::Create(aHolder->ParentDuringRead(), blobImpl); + MOZ_ASSERT(blob); ErrorResult rv; - formData->Append(name, *file, thirdArg, rv); + formData->Append(name, *blob, thirdArg, rv); if (NS_WARN_IF(rv.Failed())) { return nullptr; } } else { MOZ_ASSERT(tag == 0); nsAutoString value; @@ -907,26 +906,26 @@ WriteFormData(JSStructuredCloneWriter* a public: Closure(JSStructuredCloneWriter* aWriter, StructuredCloneHolder* aHolder) : mWriter(aWriter), mHolder(aHolder) { } static bool - Write(const nsString& aName, const OwningFileOrUSVString& aValue, + Write(const nsString& aName, const OwningBlobOrUSVString& aValue, void* aClosure) { Closure* closure = static_cast<Closure*>(aClosure); if (!WriteString(closure->mWriter, aName)) { return false; } - if (aValue.IsFile()) { - BlobImpl* blobImpl = aValue.GetAsFile()->Impl(); + if (aValue.IsBlob()) { + BlobImpl* blobImpl = aValue.GetAsBlob()->Impl(); if (!JS_WriteUint32Pair(closure->mWriter, SCTAG_DOM_BLOB, closure->mHolder->BlobImpls().Length())) { return false; } closure->mHolder->BlobImpls().AppendElement(blobImpl); return true; }
new file mode 100644 --- /dev/null +++ b/dom/base/WebKitCSSMatrix.cpp @@ -0,0 +1,192 @@ +/* -*- 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/. */ + +#include "mozilla/dom/WebKitCSSMatrix.h" + +#include "mozilla/dom/BindingUtils.h" +#include "mozilla/dom/WebKitCSSMatrixBinding.h" + +namespace mozilla { +namespace dom { + +static const double sRadPerDegree = 2.0 * M_PI / 360.0; + +bool +WebKitCSSMatrix::FeatureEnabled(JSContext* aCx, JSObject* aObj) +{ + return Preferences::GetBool("layout.css.DOMMatrix.enabled", false) && + Preferences::GetBool("layout.css.prefixes.webkit", false); +} + +already_AddRefed<WebKitCSSMatrix> +WebKitCSSMatrix::Constructor(const GlobalObject& aGlobal, ErrorResult& aRv) +{ + RefPtr<WebKitCSSMatrix> obj = new WebKitCSSMatrix(aGlobal.GetAsSupports()); + return obj.forget(); +} + +already_AddRefed<WebKitCSSMatrix> +WebKitCSSMatrix::Constructor(const GlobalObject& aGlobal, + const nsAString& aTransformList, ErrorResult& aRv) +{ + RefPtr<WebKitCSSMatrix> obj = new WebKitCSSMatrix(aGlobal.GetAsSupports()); + obj = obj->SetMatrixValue(aTransformList, aRv); + return obj.forget(); +} + +already_AddRefed<WebKitCSSMatrix> +WebKitCSSMatrix::Constructor(const GlobalObject& aGlobal, + const DOMMatrixReadOnly& aOther, ErrorResult& aRv) +{ + RefPtr<WebKitCSSMatrix> obj = new WebKitCSSMatrix(aGlobal.GetAsSupports(), + aOther); + return obj.forget(); +} + +JSObject* +WebKitCSSMatrix::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) +{ + return WebKitCSSMatrixBinding::Wrap(aCx, this, aGivenProto); +} + +WebKitCSSMatrix* +WebKitCSSMatrix::SetMatrixValue(const nsAString& aTransformList, + ErrorResult& aRv) +{ + DOMMatrix::SetMatrixValue(aTransformList, aRv); + if (NS_WARN_IF(aRv.Failed())) { + return nullptr; + } + + return this; +} + +already_AddRefed<WebKitCSSMatrix> +WebKitCSSMatrix::Multiply(const WebKitCSSMatrix& other) const +{ + RefPtr<WebKitCSSMatrix> retval = new WebKitCSSMatrix(mParent, *this); + retval->MultiplySelf(other); + + return retval.forget(); +} + +already_AddRefed<WebKitCSSMatrix> +WebKitCSSMatrix::Inverse() const +{ + RefPtr<WebKitCSSMatrix> retval = new WebKitCSSMatrix(mParent, *this); + retval->InvertSelf(); + + return retval.forget(); +} + +already_AddRefed<WebKitCSSMatrix> +WebKitCSSMatrix::Translate(double aTx, + double aTy, + double aTz) const +{ + RefPtr<WebKitCSSMatrix> retval = new WebKitCSSMatrix(mParent, *this); + retval->TranslateSelf(aTx, aTy, aTz); + + return retval.forget(); +} + +already_AddRefed<WebKitCSSMatrix> +WebKitCSSMatrix::Scale(double aScaleX, + const Optional<double>& aScaleY, + double aScaleZ) const +{ + double scaleX = aScaleX; + double scaleY = aScaleY.WasPassed() ? aScaleY.Value() : scaleX; + double scaleZ = aScaleZ; + + RefPtr<WebKitCSSMatrix> retval = new WebKitCSSMatrix(mParent, *this); + retval->ScaleNonUniformSelf(scaleX, scaleY, scaleZ); + + return retval.forget(); +} + +already_AddRefed<WebKitCSSMatrix> +WebKitCSSMatrix::Rotate(double aRotX, + const Optional<double>& aRotY, + const Optional<double>& aRotZ) const +{ + double rotX = aRotX; + double rotY; + double rotZ; + + if (!aRotY.WasPassed() && !aRotZ.WasPassed()) { + rotZ = rotX; + rotX = 0; + rotY = 0; + } else { + rotY = aRotY.WasPassed() ? aRotY.Value() : 0; + rotZ = aRotZ.WasPassed() ? aRotZ.Value() : 0; + } + + RefPtr<WebKitCSSMatrix> retval = new WebKitCSSMatrix(mParent, *this); + retval->Rotate3dSelf(rotX, rotY, rotZ); + + return retval.forget(); +} + +WebKitCSSMatrix* +WebKitCSSMatrix::Rotate3dSelf(double aRotX, + double aRotY, + double aRotZ) +{ + if (aRotX != 0 || aRotY != 0) { + Ensure3DMatrix(); + } + + if (mMatrix3D) { + if (fmod(aRotZ, 360) != 0) { + mMatrix3D->RotateZ(aRotZ * sRadPerDegree); + } + if (fmod(aRotY, 360) != 0) { + mMatrix3D->RotateY(aRotY * sRadPerDegree); + } + if (fmod(aRotX, 360) != 0) { + mMatrix3D->RotateX(aRotX * sRadPerDegree); + } + } else if (fmod(aRotZ, 360) != 0) { + mMatrix2D->PreRotate(aRotZ * sRadPerDegree); + } + + return this; +} + +already_AddRefed<WebKitCSSMatrix> +WebKitCSSMatrix::RotateAxisAngle(double aX, + double aY, + double aZ, + double aAngle) const +{ + RefPtr<WebKitCSSMatrix> retval = new WebKitCSSMatrix(mParent, *this); + retval->RotateAxisAngleSelf(aX, aY, aZ, aAngle); + + return retval.forget(); +} + +already_AddRefed<WebKitCSSMatrix> +WebKitCSSMatrix::SkewX(double aSx) const +{ + RefPtr<WebKitCSSMatrix> retval = new WebKitCSSMatrix(mParent, *this); + retval->SkewXSelf(aSx); + + return retval.forget(); +} + +already_AddRefed<WebKitCSSMatrix> +WebKitCSSMatrix::SkewY(double aSy) const +{ + RefPtr<WebKitCSSMatrix> retval = new WebKitCSSMatrix(mParent, *this); + retval->SkewYSelf(aSy); + + return retval.forget(); +} + +} // namespace dom +} // namespace mozilla
new file mode 100644 --- /dev/null +++ b/dom/base/WebKitCSSMatrix.h @@ -0,0 +1,70 @@ +/* -*- 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/. */ + +#ifndef mozilla_dom_webkitcssmatrix_h__ +#define mozilla_dom_webkitcssmatrix_h__ + +#include "mozilla/dom/DOMMatrix.h" + +namespace mozilla { +namespace dom { + +class WebKitCSSMatrix final : public DOMMatrix +{ +public: + explicit WebKitCSSMatrix(nsISupports* aParent) + : DOMMatrix(aParent) + {} + + WebKitCSSMatrix(nsISupports* aParent, const DOMMatrixReadOnly& other) + : DOMMatrix(aParent, other) + {} + + static bool FeatureEnabled(JSContext* aCx, JSObject* aObj); + + static already_AddRefed<WebKitCSSMatrix> + Constructor(const GlobalObject& aGlobal, ErrorResult& aRv); + static already_AddRefed<WebKitCSSMatrix> + Constructor(const GlobalObject& aGlobal, + const nsAString& aTransformList, ErrorResult& aRv); + static already_AddRefed<WebKitCSSMatrix> + Constructor(const GlobalObject& aGlobal, + const DOMMatrixReadOnly& aOther, ErrorResult& aRv); + + nsISupports* GetParentObject() const { return mParent; } + virtual JSObject* WrapObject(JSContext* aCx, + JS::Handle<JSObject*> aGivenProto) override; + + WebKitCSSMatrix* SetMatrixValue(const nsAString& aTransformList, + ErrorResult& aRv); + + already_AddRefed<WebKitCSSMatrix> Multiply(const WebKitCSSMatrix& aOther) const; + already_AddRefed<WebKitCSSMatrix> Inverse() const; + already_AddRefed<WebKitCSSMatrix> Translate(double aTx, + double aTy, + double aTz) const; + already_AddRefed<WebKitCSSMatrix> Scale(double aScaleX, + const Optional<double>& aScaleY, + double aScaleZ) const; + already_AddRefed<WebKitCSSMatrix> Rotate(double aRotX, + const Optional<double>& aRotY, + const Optional<double>& aRotZ) const; + already_AddRefed<WebKitCSSMatrix> RotateAxisAngle(double aX, + double aY, + double aZ, + double aAngle) const; + already_AddRefed<WebKitCSSMatrix> SkewX(double aSx) const; + already_AddRefed<WebKitCSSMatrix> SkewY(double aSy) const; +protected: + WebKitCSSMatrix* Rotate3dSelf(double aRotX, + double aRotY, + double aRotZ); +}; + +} // namespace dom +} // namespace mozilla + +#endif /* mozilla_dom_webkitcssmatrix_h__ */
--- a/dom/base/WindowNamedPropertiesHandler.cpp +++ b/dom/base/WindowNamedPropertiesHandler.cpp @@ -2,16 +2,17 @@ /* 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/. */ #include "WindowNamedPropertiesHandler.h" #include "mozilla/dom/EventTargetBinding.h" #include "mozilla/dom/WindowBinding.h" +#include "nsContentUtils.h" #include "nsDOMClassInfo.h" #include "nsGlobalWindow.h" #include "nsHTMLDocument.h" #include "nsJSUtils.h" #include "xpcprivate.h" namespace mozilla { namespace dom {
new file mode 100644 --- /dev/null +++ b/dom/base/crashtests/1230422.html @@ -0,0 +1,28 @@ +<!DOCTYPE html> +<html> +<head> +</head> +<body> +<script> + +var a = new FileReader(); + +function f() { + a.removeEventListener("loadend", f); + g(); +} + +function g() { + a.readAsBinaryString(new Blob()); +} + +a.addEventListener("loadend", f); + +try { + g(); + g(); +} catch(e) {} + +</script> +</body> +</html>
--- a/dom/base/crashtests/crashtests.list +++ b/dom/base/crashtests/crashtests.list @@ -200,8 +200,9 @@ pref(dom.webcomponents.enabled,true) loa pref(dom.webcomponents.enabled,true) load 1029710.html load 1154598.xhtml load 1157995.html load 1181619.html load structured_clone_container_throws.html HTTP(..) load xhr_abortinprogress.html load xhr_empty_datauri.html load xhr_html_nullresponse.html +load 1230422.html
--- a/dom/base/moz.build +++ b/dom/base/moz.build @@ -207,16 +207,17 @@ EXPORTS.mozilla.dom += [ 'StructuredCloneHolder.h', 'StructuredCloneTags.h', 'StyleSheetList.h', 'SubtleCrypto.h', 'Text.h', 'TreeWalker.h', 'URL.h', 'URLSearchParams.h', + 'WebKitCSSMatrix.h', 'WebSocket.h', 'WindowOrientationObserver.h', ] UNIFIED_SOURCES += [ 'AnonymousContent.cpp', 'Attr.cpp', 'BarProps.cpp', @@ -354,16 +355,17 @@ UNIFIED_SOURCES += [ 'StyleSheetList.cpp', 'SubtleCrypto.cpp', 'Text.cpp', 'TextInputProcessor.cpp', 'ThirdPartyUtil.cpp', 'TreeWalker.cpp', 'URL.cpp', 'URLSearchParams.cpp', + 'WebKitCSSMatrix.cpp', 'WebSocket.cpp', 'WindowNamedPropertiesHandler.cpp', 'WindowOrientationObserver.cpp', ] if CONFIG['MOZ_WEBRTC']: UNIFIED_SOURCES += [ 'nsDOMDataChannel.cpp',
--- a/dom/base/nsContentPermissionHelper.cpp +++ b/dom/base/nsContentPermissionHelper.cpp @@ -440,38 +440,38 @@ nsContentPermissionUtils::NotifyRemoveCo MOZ_ASSERT(it != ContentPermissionRequestChildMap().end()); ContentPermissionRequestChildMap().erase(it); } NS_IMPL_ISUPPORTS(nsContentPermissionRequester, nsIContentPermissionRequester) nsContentPermissionRequester::nsContentPermissionRequester(nsPIDOMWindow* aWindow) - : mWindow(aWindow) + : mWindow(do_GetWeakReference(aWindow)) + , mListener(new VisibilityChangeListener(aWindow)) { - mListener = new VisibilityChangeListener(mWindow); } nsContentPermissionRequester::~nsContentPermissionRequester() { mListener->RemoveListener(); mListener = nullptr; } NS_IMETHODIMP nsContentPermissionRequester::GetVisibility(nsIContentPermissionRequestCallback* aCallback) { NS_ENSURE_ARG_POINTER(aCallback); - if (!mWindow) { - MOZ_ASSERT(false); + nsCOMPtr<nsPIDOMWindow> window = do_QueryReferent(mWindow); + if (!window) { return NS_ERROR_FAILURE; } - nsCOMPtr<nsIDocShell> docshell = mWindow->GetDocShell(); + nsCOMPtr<nsIDocShell> docshell = window->GetDocShell(); if (!docshell) { return NS_ERROR_FAILURE; } bool isActive = false; docshell->GetIsActive(&isActive); aCallback->NotifyVisibility(isActive); return NS_OK;
--- a/dom/base/nsContentPermissionHelper.h +++ b/dom/base/nsContentPermissionHelper.h @@ -122,17 +122,17 @@ public: NS_DECL_ISUPPORTS NS_DECL_NSICONTENTPERMISSIONREQUESTER explicit nsContentPermissionRequester(nsPIDOMWindow* aWindow); private: virtual ~nsContentPermissionRequester(); - nsCOMPtr<nsPIDOMWindow> mWindow; + nsWeakPtr mWindow; RefPtr<VisibilityChangeListener> mListener; }; } // namespace dom } // namespace mozilla using mozilla::dom::ContentPermissionRequestParent;
--- a/dom/base/nsDOMWindowUtils.cpp +++ b/dom/base/nsDOMWindowUtils.cpp @@ -100,16 +100,17 @@ #include "nsIContentIterator.h" #include "nsIDOMStyleSheet.h" #include "nsIStyleSheetService.h" #include "nsContentPermissionHelper.h" #include "nsNetUtil.h" #include "nsDocument.h" #include "HTMLImageElement.h" #include "mozilla/css/ImageLoader.h" +#include "mozilla/layers/APZCTreeManager.h" // for layers::ZoomToRectBehavior #ifdef XP_WIN #undef GetClassName #endif using namespace mozilla; using namespace mozilla::dom; using namespace mozilla::ipc; @@ -2417,16 +2418,69 @@ nsDOMWindowUtils::FlushApzRepaints(bool* return NS_OK; } forwarder->GetShadowManager()->SendFlushApzRepaints(); *aOutResult = true; return NS_OK; } NS_IMETHODIMP +nsDOMWindowUtils::ZoomToFocusedInput() +{ + nsIWidget* widget = GetWidget(); + if (!widget) { + return NS_OK; + } + // If APZ is not enabled, this function is a no-op. + if (!widget->AsyncPanZoomEnabled()) { + return NS_OK; + } + + nsFocusManager* fm = nsFocusManager::GetFocusManager(); + if (!fm) { + return NS_OK; + } + + nsIContent* content = fm->GetFocusedContent(); + if (!content) { + return NS_OK; + } + + nsIPresShell* shell = APZCCallbackHelper::GetRootContentDocumentPresShellForContent(content); + if (!shell) { + return NS_OK; + } + + nsIScrollableFrame* rootScrollFrame = shell->GetRootScrollFrameAsScrollable(); + if (!rootScrollFrame) { + return NS_OK; + } + + nsIDocument* document = shell->GetDocument(); + if (!document) { + return NS_OK; + } + + uint32_t presShellId; + FrameMetrics::ViewID viewId; + if (APZCCallbackHelper::GetOrCreateScrollIdentifiers(document->GetDocumentElement(), &presShellId, &viewId)) { + uint32_t flags = layers::DISABLE_ZOOM_OUT; + if (!Preferences::GetBool("formhelper.autozoom")) { + flags |= layers::PAN_INTO_VIEW_ONLY; + } + + CSSRect bounds = nsLayoutUtils::GetBoundingContentRect(content, rootScrollFrame); + bounds.Inflate(15.0f, 0.0f); + widget->ZoomToRect(presShellId, viewId, bounds, flags); + } + + return NS_OK; +} + +NS_IMETHODIMP nsDOMWindowUtils::ComputeAnimationDistance(nsIDOMElement* aElement, const nsAString& aProperty, const nsAString& aValue1, const nsAString& aValue2, double* aResult) { nsresult rv; nsCOMPtr<nsIContent> content = do_QueryInterface(aElement, &rv);
--- a/dom/base/nsDocument.cpp +++ b/dom/base/nsDocument.cpp @@ -6587,16 +6587,17 @@ nsIDocument::ImportNode(nsINode& aNode, { break; } case nsIDOMNode::DOCUMENT_FRAGMENT_NODE: { if (ShadowRoot::FromNode(imported)) { break; } + MOZ_FALLTHROUGH; } case nsIDOMNode::ATTRIBUTE_NODE: case nsIDOMNode::ELEMENT_NODE: case nsIDOMNode::PROCESSING_INSTRUCTION_NODE: case nsIDOMNode::TEXT_NODE: case nsIDOMNode::CDATA_SECTION_NODE: case nsIDOMNode::COMMENT_NODE: case nsIDOMNode::DOCUMENT_TYPE_NODE: @@ -7623,16 +7624,17 @@ nsIDocument::AdoptNode(nsINode& aAdopted break; } case nsIDOMNode::DOCUMENT_FRAGMENT_NODE: { if (ShadowRoot::FromNode(adoptedNode)) { rv.Throw(NS_ERROR_DOM_HIERARCHY_REQUEST_ERR); return nullptr; } + MOZ_FALLTHROUGH; } case nsIDOMNode::ELEMENT_NODE: case nsIDOMNode::PROCESSING_INSTRUCTION_NODE: case nsIDOMNode::TEXT_NODE: case nsIDOMNode::CDATA_SECTION_NODE: case nsIDOMNode::COMMENT_NODE: case nsIDOMNode::DOCUMENT_TYPE_NODE: { @@ -7914,16 +7916,17 @@ nsDocument::GetViewportInfo(const Screen } mScaleStrEmpty = scaleStr.IsEmpty(); mWidthStrEmpty = widthStr.IsEmpty(); mValidScaleFloat = !scaleStr.IsEmpty() && NS_SUCCEEDED(scaleErrorCode); mValidMaxScale = !maxScaleStr.IsEmpty() && NS_SUCCEEDED(scaleMaxErrorCode); mViewportType = Specified; + MOZ_FALLTHROUGH; } case Specified: default: LayoutDeviceToScreenScale effectiveMinScale = mScaleMinFloat; LayoutDeviceToScreenScale effectiveMaxScale = mScaleMaxFloat; bool effectiveValidMaxScale = mValidMaxScale; bool effectiveAllowZoom = mAllowZoom; if (gfxPrefs::ForceUserScalable()) {
--- a/dom/base/nsDocumentEncoder.cpp +++ b/dom/base/nsDocumentEncoder.cpp @@ -1095,16 +1095,19 @@ nsDocumentEncoder::EncodeToStringWithMax return NS_ERROR_NOT_INITIALIZED; aOutputString.Truncate(); nsString output; static const size_t bufferSize = 2048; if (!mCachedBuffer) { mCachedBuffer = nsStringBuffer::Alloc(bufferSize).take(); + if (NS_WARN_IF(!mCachedBuffer)) { + return NS_ERROR_OUT_OF_MEMORY; + } } NS_ASSERTION(!mCachedBuffer->IsReadonly(), "DocumentEncoder shouldn't keep reference to non-readonly buffer!"); static_cast<char16_t*>(mCachedBuffer->Data())[0] = char16_t(0); mCachedBuffer->ToString(0, output, true); // output owns the buffer now! mCachedBuffer = nullptr;
--- a/dom/base/nsFrameLoader.cpp +++ b/dom/base/nsFrameLoader.cpp @@ -776,16 +776,17 @@ nsFrameLoader::MarginsChanged(uint32_t a if (presContext) presContext->RebuildAllStyleData(nsChangeHint(0), eRestyle_Subtree); } bool nsFrameLoader::ShowRemoteFrame(const ScreenIntSize& size, nsSubDocumentFrame *aFrame) { + PROFILER_LABEL_FUNC(js::ProfileEntry::Category::GRAPHICS); NS_ASSERTION(IsRemoteFrame(), "ShowRemote only makes sense on remote frames."); if (!mRemoteBrowser && !TryRemoteBrowser()) { NS_ERROR("Couldn't create child process."); return false; } // FIXME/bug 589337: Show()/Hide() is pretty expensive for
--- a/dom/base/nsGlobalWindow.cpp +++ b/dom/base/nsGlobalWindow.cpp @@ -7596,16 +7596,17 @@ nsGlobalWindow::RevisePopupAbuseLevel(Po PopupControlState abuse = aControl; switch (abuse) { case openControlled: case openAbused: case openOverridden: if (PopupWhitelisted()) abuse = PopupControlState(abuse - 1); + break; case openAllowed: break; default: NS_WARNING("Strange PopupControlState!"); } // limit the number of simultaneously open popups if (abuse == openAbused || abuse == openControlled) { int32_t popupMax = Preferences::GetInt("dom.popup_maximum", -1);
--- a/dom/base/nsJSEnvironment.cpp +++ b/dom/base/nsJSEnvironment.cpp @@ -1330,16 +1330,18 @@ nsJSContext::ShrinkGCBuffersNow() KillShrinkGCBuffersTimer(); JS::ShrinkGCBuffers(sRuntime); } static void FinishAnyIncrementalGC() { + PROFILER_LABEL_FUNC(js::ProfileEntry::Category::GC); + if (sCCLockedOut) { // We're in the middle of an incremental GC, so finish it. JS::PrepareForIncrementalGC(sRuntime); JS::FinishIncrementalGC(sRuntime, JS::gcreason::CC_FORCED); } } static void
--- a/dom/base/nsObjectLoadingContent.cpp +++ b/dom/base/nsObjectLoadingContent.cpp @@ -3097,16 +3097,17 @@ nsObjectLoadingContent::DoStopPlugin(nsP TeardownProtoChain(); mIsStopping = false; } NS_IMETHODIMP nsObjectLoadingContent::StopPluginInstance() { + PROFILER_LABEL_FUNC(js::ProfileEntry::Category::OTHER); // Clear any pending events mPendingInstantiateEvent = nullptr; mPendingCheckPluginStopEvent = nullptr; // If we're currently instantiating, clearing this will cause // InstantiatePluginInstance's re-entrance check to destroy the created plugin mInstantiating = false;
--- a/dom/base/nsXMLContentSerializer.cpp +++ b/dom/base/nsXMLContentSerializer.cpp @@ -14,16 +14,17 @@ #include "nsGkAtoms.h" #include "nsIDOMProcessingInstruction.h" #include "nsIDOMComment.h" #include "nsIDOMDocumentType.h" #include "nsIContent.h" #include "nsIDocument.h" #include "nsIDocumentEncoder.h" +#include "nsIParserService.h" #include "nsNameSpaceManager.h" #include "nsTextFragment.h" #include "nsString.h" #include "prprf.h" #include "nsUnicharUtils.h" #include "nsCRT.h" #include "nsContentUtils.h" #include "nsAttrName.h" @@ -1424,17 +1425,17 @@ nsXMLContentSerializer::AppendFormatedWr bool sawBlankOrTab = false; bool leaveLoop = false; do { switch (*aPos) { case ' ': case '\t': sawBlankOrTab = true; - // no break + MOZ_FALLTHROUGH; case '\n': ++aPos; // do not increase mColPos, // because we will reduce the whitespace to a single char break; default: leaveLoop = true; break;
--- a/dom/base/test/test_bug1187157.html +++ b/dom/base/test/test_bug1187157.html @@ -11,19 +11,18 @@ https://bugzilla.mozilla.org/show_bug.cg </head> <body> <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=789315">Mozilla Bug 789315</a> <form id="a"><input name="b" type="file"/></form> <script type="text/javascript"> var obj = new FormData(document.getElementById('a')).get('b'); - ok(obj instanceof File, "This should return an empty File"); + ok(obj instanceof Blob, "This should return an empty Blob"); is(obj.size, 0, "Size should be 0"); - is(obj.name, "", "Name should be an empty string"); is(obj.type, "application/octet-stream", "Type should be application/octet-stream"); var o = obj.slice(10, 100, "foo/bar"); is(o.size, 0, "The new blob has size 0"); is(o.type, "foo/bar", "The new blob has type foo/bar"); </script> </body>
--- a/dom/bindings/Codegen.py +++ b/dom/bindings/Codegen.py @@ -8767,17 +8767,17 @@ class CGMemberJITInfo(CGThing): assert(not alwaysInSlot or eliminatable) # Things always in slots had better be eliminatable def jitInfoInitializer(isTypedMethod): initializer = fill( """ { { ${opName} }, { prototypes::id::${name} }, - PrototypeTraits<prototypes::id::${name}>::Depth, + { PrototypeTraits<prototypes::id::${name}>::Depth }, JSJitInfo::${opType}, JSJitInfo::${aliasSet}, /* aliasSet. Not relevant for setters. */ ${returnType}, /* returnType. Not relevant for setters. */ ${isInfallible}, /* isInfallible. False in setters. */ ${isMovable}, /* isMovable. Not relevant for setters. */ ${isEliminatable}, /* isEliminatable. Not relevant for setters. */ ${isAlwaysInSlot}, /* isAlwaysInSlot. Only relevant for getters. */ ${isLazilyCachedInSlot}, /* isLazilyCachedInSlot. Only relevant for getters. */ @@ -9147,17 +9147,17 @@ class CGStaticMethodJitinfo(CGGeneric): A class for generating the JITInfo for a promise-returning static method. """ def __init__(self, method): CGGeneric.__init__( self, "\n" "static const JSJitInfo %s_methodinfo = {\n" " { (JSJitGetterOp)%s },\n" - " { prototypes::id::_ID_Count }, 0, JSJitInfo::StaticMethod,\n" + " { prototypes::id::_ID_Count }, { 0 }, JSJitInfo::StaticMethod,\n" " JSJitInfo::AliasEverything, JSVAL_TYPE_MISSING, false, false,\n" " false, false, 0\n" "};\n" % (IDLToCIdentifier(method.identifier.name), CppKeywords.checkMethodName( IDLToCIdentifier(method.identifier.name))))
--- a/dom/bindings/moz.build +++ b/dom/bindings/moz.build @@ -134,13 +134,8 @@ if CONFIG['MOZ_BUILD_APP'] in ['browser' if CONFIG['MOZ_SIMPLEPUSH']: DEFINES['MOZ_SIMPLEPUSH'] = True PYTHON_UNIT_TESTS += [ 'mozwebidlcodegen/test/test_mozwebidlcodegen.py', ] - -if CONFIG['GNU_CC']: - CXXFLAGS += [ - '-Wno-uninitialized', - ]
--- a/dom/bindings/test/moz.build +++ b/dom/bindings/test/moz.build @@ -47,13 +47,8 @@ LOCAL_INCLUDES += [ ] LOCAL_INCLUDES += [ '!..', '/dom/bindings', '/js/xpconnect/src', '/js/xpconnect/wrappers', ] - -if CONFIG['GNU_CC']: - CXXFLAGS += [ - '-Wno-uninitialized', - ]
--- a/dom/bluetooth/common/webapi/BluetoothGatt.cpp +++ b/dom/bluetooth/common/webapi/BluetoothGatt.cpp @@ -107,35 +107,35 @@ BluetoothGatt::Connect(ErrorResult& aRv) BT_ENSURE_TRUE_REJECT( mConnectionState == BluetoothConnectionState::Disconnected, promise, NS_ERROR_DOM_INVALID_STATE_ERR); BluetoothService* bs = BluetoothService::Get(); BT_ENSURE_TRUE_REJECT(bs, promise, NS_ERROR_NOT_AVAILABLE); - BluetoothUuid appUuid; - BT_ENSURE_TRUE_REJECT(NS_SUCCEEDED(StringToUuid(mAppUuid, appUuid)), - promise, - NS_ERROR_DOM_OPERATION_ERR); - BluetoothAddress deviceAddr; BT_ENSURE_TRUE_REJECT( NS_SUCCEEDED(StringToAddress(mDeviceAddr, deviceAddr)), promise, NS_ERROR_DOM_OPERATION_ERR); if (mAppUuid.IsEmpty()) { nsresult rv = GenerateUuid(mAppUuid); BT_ENSURE_TRUE_REJECT(NS_SUCCEEDED(rv) && !mAppUuid.IsEmpty(), promise, NS_ERROR_DOM_OPERATION_ERR); RegisterBluetoothSignalHandler(mAppUuid, this); } + BluetoothUuid appUuid; + BT_ENSURE_TRUE_REJECT(NS_SUCCEEDED(StringToUuid(mAppUuid, appUuid)), + promise, + NS_ERROR_DOM_OPERATION_ERR); + UpdateConnectionState(BluetoothConnectionState::Connecting); bs->ConnectGattClientInternal( appUuid, deviceAddr, new BluetoothVoidReplyRunnable(nullptr, promise)); return promise.forget(); } already_AddRefed<Promise>
--- a/dom/cache/AutoUtils.cpp +++ b/dom/cache/AutoUtils.cpp @@ -478,16 +478,17 @@ AutoParentOpResult::~AutoParentOpResult( } case CacheOpResult::TStorageOpenResult: { StorageOpenResult& result = mOpResult.get_StorageOpenResult(); if (action == Forget || result.actorParent() == nullptr) { break; } Unused << PCacheParent::Send__delete__(result.actorParent()); + break; } default: // other types do not need clean up break; } if (action == Delete && mStreamControl) { Unused << PCacheStreamControlParent::Send__delete__(mStreamControl);
--- a/dom/canvas/WebGL2ContextState.cpp +++ b/dom/canvas/WebGL2ContextState.cpp @@ -107,16 +107,17 @@ WebGL2Context::GetParameter(JSContext* c case LOCAL_GL_MAX_ELEMENT_INDEX: // GL_MAX_ELEMENT_INDEX becomes available in GL 4.3 or via ES3 // compatibility if (!gl->IsSupported(gl::GLFeature::ES3_compatibility)) return JS::NumberValue(0); /*** fall through to fGetInteger64v ***/ + MOZ_FALLTHROUGH; case LOCAL_GL_MAX_COMBINED_FRAGMENT_UNIFORM_COMPONENTS: case LOCAL_GL_MAX_COMBINED_VERTEX_UNIFORM_COMPONENTS: case LOCAL_GL_MAX_UNIFORM_BLOCK_SIZE: { GLint64 val; gl->fGetInteger64v(pname, &val); return JS::DoubleValue(static_cast<double>(val)); }
--- a/dom/canvas/WebGLVertexAttribData.h +++ b/dom/canvas/WebGLVertexAttribData.h @@ -47,22 +47,28 @@ struct WebGLVertexAttribData return sizeof(GLubyte); case LOCAL_GL_SHORT: return sizeof(GLshort); case LOCAL_GL_UNSIGNED_SHORT: return sizeof(GLushort); + case LOCAL_GL_INT: + return sizeof(GLint); + + case LOCAL_GL_UNSIGNED_INT: + return sizeof(GLuint); + // case LOCAL_GL_FIXED: case LOCAL_GL_FLOAT: return sizeof(GLfloat); default: - NS_ERROR("Should never get here!"); + MOZ_ASSERT(false, "Should never get here!"); return 0; } } GLuint actualStride() const { if (stride) return stride;
--- a/dom/canvas/moz.build +++ b/dom/canvas/moz.build @@ -53,17 +53,16 @@ UNIFIED_SOURCES += [ 'ImageBitmap.cpp', 'ImageBitmapRenderingContext.cpp', 'ImageData.cpp', 'OffscreenCanvas.cpp', ] # WebGL Sources UNIFIED_SOURCES += [ - 'MurmurHash3.cpp', 'TexUnpackBlob.cpp', 'WebGL1Context.cpp', 'WebGL1ContextBuffers.cpp', 'WebGL1ContextUniforms.cpp', 'WebGL2Context.cpp', 'WebGL2ContextBuffers.cpp', 'WebGL2ContextDraw.cpp', 'WebGL2ContextFramebuffers.cpp', @@ -144,16 +143,24 @@ UNIFIED_SOURCES += [ 'WebGLUniformLocation.cpp', 'WebGLValidateStrings.cpp', 'WebGLVertexArray.cpp', 'WebGLVertexArrayFake.cpp', 'WebGLVertexArrayGL.cpp', 'WebGLVertexArrayObject.cpp', ] +SOURCES += [ + 'MurmurHash3.cpp', +] + +# Suppress warnings from third-party code. +if CONFIG['CLANG_CXX']: + SOURCES['MurmurHash3.cpp'].flags += ['-Wno-implicit-fallthrough'] + LOCAL_INCLUDES += [ '/js/xpconnect/wrappers', ] include('/ipc/chromium/chromium-config.mozbuild') FINAL_LIBRARY = 'xul' LOCAL_INCLUDES += [
--- a/dom/events/test/test_bug328885.html +++ b/dom/events/test/test_bug328885.html @@ -108,17 +108,17 @@ https://bugzilla.mozilla.org/show_bug.cg function (evt2) { evt.originalTarget.textContent = "bar" + mutationCount; }, false); // This one deletes and inserts a new node, then DOMSubtreeModified. inputelement.textContent = "bar"; ok(mutationCount == 19, "(20) Mutation listener should have been called! ["+ mutationCount + "]"); } ,false); - synthesizeMouseAtPoint(5, 5, {}, window); + synthesizeMouseAtCenter(inputelement, {}, window); SimpleTest.finish(); } function doTest() { inputelement = document.getElementById('inputelement'); inputelement.focus(); setTimeout(clickTest, 100); }
--- a/dom/geolocation/nsGeolocation.cpp +++ b/dom/geolocation/nsGeolocation.cpp @@ -1250,29 +1250,31 @@ Geolocation::Init(nsIDOMWindow* aContent /* use capture */ true, /* wants untrusted */ false); } nsCOMPtr<nsIURI> uri; nsresult rv = mPrincipal->GetURI(getter_AddRefs(uri)); NS_ENSURE_SUCCESS(rv, rv); - bool isHttp; - rv = uri->SchemeIs("http", &isHttp); - NS_ENSURE_SUCCESS(rv, rv); + if (uri) { + bool isHttp; + rv = uri->SchemeIs("http", &isHttp); + NS_ENSURE_SUCCESS(rv, rv); - bool isHttps; - rv = uri->SchemeIs("https", &isHttps); - NS_ENSURE_SUCCESS(rv, rv); + bool isHttps; + rv = uri->SchemeIs("https", &isHttps); + NS_ENSURE_SUCCESS(rv, rv); - // Store the protocol to send via telemetry later. - if (isHttp) { - mProtocolType = ProtocolType::HTTP; - } else if (isHttps) { - mProtocolType = ProtocolType::HTTPS; + // Store the protocol to send via telemetry later. + if (isHttp) { + mProtocolType = ProtocolType::HTTP; + } else if (isHttps) { + mProtocolType = ProtocolType::HTTPS; + } } } // If no aContentDom was passed into us, we are being used // by chrome/c++ and have no mOwner, no mPrincipal, and no need // to prompt. mService = nsGeolocationService::GetGeolocationService(); if (mService) {
--- a/dom/html/HTMLInputElement.cpp +++ b/dom/html/HTMLInputElement.cpp @@ -3801,16 +3801,17 @@ HTMLInputElement::PostHandleEvent(EventC { // Checkbox and Radio try to submit on Enter press if (keyEvent->keyCode != NS_VK_SPACE) { MaybeSubmitForm(aVisitor.mPresContext); break; // If we are submitting, do not send click event } // else fall through and treat Space like click... + MOZ_FALLTHROUGH; } case NS_FORM_INPUT_BUTTON: case NS_FORM_INPUT_RESET: case NS_FORM_INPUT_SUBMIT: case NS_FORM_INPUT_IMAGE: // Bug 34418 case NS_FORM_INPUT_COLOR: { WidgetMouseEvent event(aVisitor.mEvent->mFlags.mIsTrusted, @@ -3829,17 +3830,17 @@ HTMLInputElement::PostHandleEvent(EventC if (aVisitor.mEvent->mMessage == eKeyPress && mType == NS_FORM_INPUT_RADIO && !keyEvent->IsAlt() && !keyEvent->IsControl() && !keyEvent->IsMeta()) { bool isMovingBack = false; switch (keyEvent->keyCode) { case NS_VK_UP: case NS_VK_LEFT: isMovingBack = true; - // FALLTHROUGH + MOZ_FALLTHROUGH; case NS_VK_DOWN: case NS_VK_RIGHT: // Arrow key pressed, focus+select prev/next radio button nsIRadioGroupContainer* container = GetRadioGroupContainer(); if (container) { nsAutoString name; GetAttr(kNameSpaceID_None, nsGkAtoms::name, name); RefPtr<HTMLInputElement> selectedRadioButton; @@ -5566,24 +5567,24 @@ HTMLInputElement::SubmitNamesValues(nsFo // Submit file if its input type=file and this encoding method accepts files // if (mType == NS_FORM_INPUT_FILE) { // Submit files const nsTArray<RefPtr<File>>& files = GetFilesInternal(); for (uint32_t i = 0; i < files.Length(); ++i) { - aFormSubmission->AddNameFilePair(name, files[i]); + aFormSubmission->AddNameBlobPair(name, files[i]); } if (files.IsEmpty()) { RefPtr<BlobImpl> blobImpl = - new BlobImplEmptyFile(NS_LITERAL_STRING("application/octet-stream")); - RefPtr<File> file = File::Create(OwnerDoc()->GetInnerWindow(), blobImpl); - aFormSubmission->AddNameFilePair(name, file); + new EmptyBlobImpl(NS_LITERAL_STRING("application/octet-stream")); + RefPtr<Blob> blob = Blob::Create(OwnerDoc()->GetInnerWindow(), blobImpl); + aFormSubmission->AddNameBlobPair(name, blob); } return NS_OK; } if (mType == NS_FORM_INPUT_HIDDEN && name.EqualsLiteral("_charset_")) { nsCString charset; aFormSubmission->GetCharset(charset);
--- a/dom/html/nsFormSubmission.cpp +++ b/dom/html/nsFormSubmission.cpp @@ -49,16 +49,27 @@ SendJSWarning(nsIDocument* aDocument, { nsContentUtils::ReportToConsole(nsIScriptError::warningFlag, NS_LITERAL_CSTRING("HTML"), aDocument, nsContentUtils::eFORMS_PROPERTIES, aWarningName, aWarningArgs, aWarningArgsLen); } +static void +RetrieveFileName(Blob* aBlob, nsAString& aFilename) +{ + MOZ_ASSERT(aBlob); + + RefPtr<File> file = aBlob->ToFile(); + if (file) { + file->GetName(aFilename); + } +} + // -------------------------------------------------------------------------- class nsFSURLEncoded : public nsEncodingFormSubmission { public: /** * @param aCharset the charset of the form as a string * @param aMethod the method of the submit (either NS_FORM_METHOD_GET or @@ -72,18 +83,18 @@ public: mMethod(aMethod), mDocument(aDocument), mWarnedFileControl(false) { } virtual nsresult AddNameValuePair(const nsAString& aName, const nsAString& aValue) override; - virtual nsresult AddNameFilePair(const nsAString& aName, - File* aFile) override; + virtual nsresult AddNameBlobPair(const nsAString& aName, + Blob* aBlob) override; virtual nsresult GetEncodedSubmission(nsIURI* aURI, nsIInputStream** aPostDataStream) override; virtual bool SupportsIsindexSubmission() override { return true; } @@ -159,29 +170,28 @@ nsFSURLEncoded::AddIsindex(const nsAStri } else { mQueryString += NS_LITERAL_CSTRING("&isindex=") + convValue; } return NS_OK; } nsresult -nsFSURLEncoded::AddNameFilePair(const nsAString& aName, - File* aFile) +nsFSURLEncoded::AddNameBlobPair(const nsAString& aName, + Blob* aBlob) { - MOZ_ASSERT(aFile); + MOZ_ASSERT(aBlob); if (!mWarnedFileControl) { SendJSWarning(mDocument, "ForgotFileEnctypeWarning", nullptr, 0); mWarnedFileControl = true; } nsAutoString filename; - aFile->GetName(filename); - + RetrieveFileName(aBlob, filename); return AddNameValuePair(aName, filename); } static void HandleMailtoSubject(nsCString& aPath) { // Walk through the string and see if we have a subject already. bool hasSubject = false; @@ -433,61 +443,64 @@ nsFSMultipartFormData::AddNameValuePair( + NS_LITERAL_CSTRING("Content-Disposition: form-data; name=\"") + nameStr + NS_LITERAL_CSTRING("\"" CRLF CRLF) + valueStr + NS_LITERAL_CSTRING(CRLF); return NS_OK; } nsresult -nsFSMultipartFormData::AddNameFilePair(const nsAString& aName, - File* aFile) +nsFSMultipartFormData::AddNameBlobPair(const nsAString& aName, + Blob* aBlob) { - MOZ_ASSERT(aFile); + MOZ_ASSERT(aBlob); // Encode the control name nsAutoCString nameStr; nsresult rv = EncodeVal(aName, nameStr, true); NS_ENSURE_SUCCESS(rv, rv); nsAutoString filename16; - aFile->GetName(filename16); + RetrieveFileName(aBlob, filename16); ErrorResult error; nsAutoString filepath16; - aFile->GetPath(filepath16, error); - if (NS_WARN_IF(error.Failed())) { - return error.StealNSResult(); + RefPtr<File> file = aBlob->ToFile(); + if (file) { + file->GetPath(filepath16, error); + if (NS_WARN_IF(error.Failed())) { + return error.StealNSResult(); + } } if (!filepath16.IsEmpty()) { // File.path includes trailing "/" filename16 = filepath16 + filename16; } nsAutoCString filename; rv = EncodeVal(filename16, filename, true); NS_ENSURE_SUCCESS(rv, rv); // Get content type nsAutoString contentType16; - aFile->GetType(contentType16); + aBlob->GetType(contentType16); if (contentType16.IsEmpty()) { contentType16.AssignLiteral("application/octet-stream"); } nsAutoCString contentType; contentType.Adopt(nsLinebreakConverter:: ConvertLineBreaks(NS_ConvertUTF16toUTF8(contentType16).get(), nsLinebreakConverter::eLinebreakAny, nsLinebreakConverter::eLinebreakSpace)); // Get input stream nsCOMPtr<nsIInputStream> fileStream; - aFile->GetInternalStream(getter_AddRefs(fileStream), error); + aBlob->GetInternalStream(getter_AddRefs(fileStream), error); if (NS_WARN_IF(error.Failed())) { return error.StealNSResult(); } if (fileStream) { // Create buffered stream (for efficiency) nsCOMPtr<nsIInputStream> bufferedStream; rv = NS_NewBufferedInputStream(getter_AddRefs(bufferedStream), @@ -512,17 +525,17 @@ nsFSMultipartFormData::AddNameFilePair(c + filename + NS_LITERAL_CSTRING("\"" CRLF) + NS_LITERAL_CSTRING("Content-Type: ") + contentType + NS_LITERAL_CSTRING(CRLF CRLF); // We should not try to append an invalid stream. That will happen for example // if we try to update a file that actually do not exist. if (fileStream) { ErrorResult error; - uint64_t size = aFile->GetSize(error); + uint64_t size = aBlob->GetSize(error); if (error.Failed()) { error.SuppressException(); } else { // We need to dump the data up to this point into the POST data stream // here, since we're about to add the file input stream AddPostDataStream(); mPostDataStream->AppendStream(fileStream); @@ -585,18 +598,18 @@ class nsFSTextPlain : public nsEncodingF public: nsFSTextPlain(const nsACString& aCharset, nsIContent* aOriginatingElement) : nsEncodingFormSubmission(aCharset, aOriginatingElement) { } virtual nsresult AddNameValuePair(const nsAString& aName, const nsAString& aValue) override; - virtual nsresult AddNameFilePair(const nsAString& aName, - File* aFile) override; + virtual nsresult AddNameBlobPair(const nsAString& aName, + Blob* aBlob) override; virtual nsresult GetEncodedSubmission(nsIURI* aURI, nsIInputStream** aPostDataStream) override; private: nsString mBody; }; @@ -609,22 +622,23 @@ nsFSTextPlain::AddNameValuePair(const ns // values so we'll have to live with it. mBody.Append(aName + NS_LITERAL_STRING("=") + aValue + NS_LITERAL_STRING(CRLF)); return NS_OK; } nsresult -nsFSTextPlain::AddNameFilePair(const nsAString& aName, - File* aFile) +nsFSTextPlain::AddNameBlobPair(const nsAString& aName, + Blob* aBlob) { + MOZ_ASSERT(aBlob); + nsAutoString filename; - aFile->GetName(filename); - + RetrieveFileName(aBlob, filename); AddNameValuePair(aName, filename); return NS_OK; } nsresult nsFSTextPlain::GetEncodedSubmission(nsIURI* aURI, nsIInputStream** aPostDataStream) {
--- a/dom/html/nsFormSubmission.h +++ b/dom/html/nsFormSubmission.h @@ -14,17 +14,17 @@ class nsIURI; class nsIInputStream; class nsGenericHTMLElement; class nsIMultiplexInputStream; namespace mozilla { namespace dom { -class File; +class Blob; } // namespace dom } // namespace mozilla /** * Class for form submissions; encompasses the function to call to submit as * well as the form submission name/value pairs */ class nsFormSubmission @@ -40,23 +40,24 @@ public: * * @param aName the name of the parameter * @param aValue the value of the parameter */ virtual nsresult AddNameValuePair(const nsAString& aName, const nsAString& aValue) = 0; /** - * Submit a name/file pair + * Submit a name/blob pair * * @param aName the name of the parameter - * @param aFile the file to submit. The file's name will be used + * @param aBlob the blob to submit. The file's name will be used if the Blob + * is actually a File, otherwise 'blob' string is used instead. */ - virtual nsresult AddNameFilePair(const nsAString& aName, - mozilla::dom::File* aFile) = 0; + virtual nsresult AddNameBlobPair(const nsAString& aName, + mozilla::dom::Blob* aBlob) = 0; /** * Reports whether the instance supports AddIsindex(). * * @return true if supported. */ virtual bool SupportsIsindexSubmission() { @@ -154,18 +155,18 @@ public: * @param aCharset the charset of the form as a string */ nsFSMultipartFormData(const nsACString& aCharset, nsIContent* aOriginatingElement); ~nsFSMultipartFormData(); virtual nsresult AddNameValuePair(const nsAString& aName, const nsAString& aValue) override; - virtual nsresult AddNameFilePair(const nsAString& aName, - mozilla::dom::File* aFile) override; + virtual nsresult AddNameBlobPair(const nsAString& aName, + mozilla::dom::Blob* aBlob) override; virtual nsresult GetEncodedSubmission(nsIURI* aURI, nsIInputStream** aPostDataStream) override; void GetContentType(nsACString& aContentType) { aContentType = NS_LITERAL_CSTRING("multipart/form-data; boundary=") + mBoundary; }
--- a/dom/html/test/formData_test.js +++ b/dom/html/test/formData_test.js @@ -72,25 +72,21 @@ function testSet() { f.append("other", "value3"); is(f.getAll("other").length, 5, "append() should not replace existing entries."); f.set("other", "value4"); is(f.getAll("other").length, 1, "set() should replace existing entries."); is(f.getAll("other")[0], "value4", "set() should replace existing entries."); } -function testIterate() { - todo(false, "Implement this in Bug 1085284."); -} - function testFilename() { var f = new FormData(); // Spec says if a Blob (which is not a File) is added, the name parameter is set to "blob". f.append("blob", new Blob(["hi"])); - is(f.get("blob").name, "blob", "Blob's filename should be blob."); + ok(f.get("blob") instanceof Blob, "We should have a blob back."); // If a filename is passed, that should replace the original. f.append("blob2", new Blob(["hi"]), "blob2.txt"); is(f.get("blob2").name, "blob2.txt", "Explicit filename should override \"blob\"."); var file = new File(["hi"], "file1.txt"); f.append("file1", file); // If a file is passed, the "create entry" algorithm should not create a new File, but reuse the existing one. @@ -204,15 +200,14 @@ function testSend(doneCb) { } function runTest(doneCb) { testHas(); testGet(); testGetAll(); testDelete(); testSet(); - testIterate(); testFilename(); testIterable(); // Finally, send an XHR and verify the response matches. testSend(doneCb); }
--- a/dom/html/test/test_formSubmission.html +++ b/dom/html/test/test_formSubmission.html @@ -585,17 +585,17 @@ var expectedAugment = [ { name: "aNameNum", value: "9.2" }, { name: "aNameFile1", value: placeholder_myFile1 }, { name: "aNameFile2", value: placeholder_myFile2 }, //{ name: "aNameObj", value: "[object XMLHttpRequest]" }, //{ name: "aNameNull", value: "null" }, //{ name: "aNameUndef", value: "undefined" }, ]; -function checkMPSubmission(sub, expected, test) { +function checkMPSubmission(sub, expected, test, isFormData = false) { function getPropCount(o) { var x, l = 0; for (x in o) ++l; return l; } function mpquote(s) { return s.replace(/\r\n/g, " ") .replace(/\r/g, " ") @@ -620,17 +620,17 @@ function checkMPSubmission(sub, expected "Wrong number of headers in " + test); is(sub[i].body, expected[i].value.replace(/\r\n|\r|\n/, "\r\n"), "Correct value in " + test); } else { is(sub[i].headers["Content-Disposition"], "form-data; name=\"" + mpquote(expected[i].name) + "\"; filename=\"" + - mpquote(expected[i].fileName) + "\"", + mpquote(expected[i].fileName != "" || !isFormData ? expected[i].fileName : "blob") + "\"", "Correct name in " + test); is(sub[i].headers["Content-Type"], expected[i].contentType, "Correct content type in " + test); is (getPropCount(sub[i].headers), 2, "Wrong number of headers in " + test); is(sub[i].body, expected[i].value, @@ -777,48 +777,48 @@ function runTest() { checkURLSubmission(submission, expectedSub); // Send form using XHR and FormData xhr = new XMLHttpRequest(); xhr.onload = function() { gen.next(); }; xhr.open("POST", "form_submit_server.sjs"); xhr.send(new FormData(form)); yield undefined; // Wait for XHR load - checkMPSubmission(JSON.parse(xhr.responseText), expectedSub, "send form using XHR and FormData"); + checkMPSubmission(JSON.parse(xhr.responseText), expectedSub, "send form using XHR and FormData", true); // Send disabled form using XHR and FormData setDisabled(document.querySelectorAll("input, select, textarea"), true); xhr.open("POST", "form_submit_server.sjs"); xhr.send(new FormData(form)); yield undefined; - checkMPSubmission(JSON.parse(xhr.responseText), [], "send disabled form using XHR and FormData"); + checkMPSubmission(JSON.parse(xhr.responseText), [], "send disabled form using XHR and FormData", true); setDisabled(document.querySelectorAll("input, select, textarea"), false); // Send FormData function addToFormData(fd) { fd.append("aName", "aValue"); fd.append("aNameNum", 9.2); fd.append("aNameFile1", myFile1); fd.append("aNameFile2", myFile2); } var fd = new FormData(); addToFormData(fd); xhr.open("POST", "form_submit_server.sjs"); xhr.send(fd); yield undefined; - checkMPSubmission(JSON.parse(xhr.responseText), expectedAugment, "send FormData"); + checkMPSubmission(JSON.parse(xhr.responseText), expectedAugment, "send FormData", true); // Augment <form> using FormData fd = new FormData(form); addToFormData(fd); xhr.open("POST", "form_submit_server.sjs"); xhr.send(fd); yield undefined; checkMPSubmission(JSON.parse(xhr.responseText), - expectedSub.concat(expectedAugment), "send augmented FormData"); + expectedSub.concat(expectedAugment), "send augmented FormData", true); SimpleTest.finish(); yield undefined; } </script> </pre> </body>
--- a/dom/indexedDB/ActorsParent.cpp +++ b/dom/indexedDB/ActorsParent.cpp @@ -23223,18 +23223,19 @@ NormalJSRuntime::Init() mContext = JS_NewContext(mRuntime, 0); if (NS_WARN_IF(!mContext)) { return false; } JSAutoRequest ar(mContext); + JS::CompartmentOptions options; mGlobal = JS_NewGlobalObject(mContext, &kGlobalClass, nullptr, - JS::FireOnNewGlobalHook); + JS::FireOnNewGlobalHook, options); if (NS_WARN_IF(!mGlobal)) { return false; } return true; } // static
--- a/dom/interfaces/base/nsIDOMWindowUtils.idl +++ b/dom/interfaces/base/nsIDOMWindowUtils.idl @@ -44,17 +44,17 @@ interface nsIDOMClientRect; interface nsIURI; interface nsIDOMEventTarget; interface nsIRunnable; interface nsITranslationNodeList; interface nsIJSRAIIHelper; interface nsIContentPermissionRequest; interface nsIObserver; -[scriptable, uuid(7846c43d-e131-40a6-8417-3be2c7e11df1)] +[scriptable, uuid(ca6a458c-82e7-4979-886e-6d214eac6f0b)] interface nsIDOMWindowUtils : nsISupports { /** * Image animation mode of the window. When this attribute's value * is changed, the implementation should set all images in the window * to the given value. That is, when set to kDontAnimMode, all images * will stop animating. The attribute's value must be one of the * animationMode values from imgIContainer. @@ -1454,16 +1454,21 @@ interface nsIDOMWindowUtils : nsISupport * triggered and an "apz-repaints-flushed" notification will be dispatched via * the observer service once the flush is complete. If the function returns * false, an error occurred or a flush is not needed, and the notification * will not fire. This is intended to be used by test code only! */ bool flushApzRepaints(); /** + * Ask APZ to pan and zoom to the focused input element. + */ + void zoomToFocusedInput(); + + /** * Method for testing StyleAnimationValue::ComputeDistance. * * Returns the distance between the two values as reported by * StyleAnimationValue::ComputeDistance for the given element and * property. */ double computeAnimationDistance(in nsIDOMElement element, in AString property,
--- a/dom/ipc/ContentChild.cpp +++ b/dom/ipc/ContentChild.cpp @@ -2808,18 +2808,17 @@ ContentChild::RecvUnregisterSheet(const return true; } POfflineCacheUpdateChild* ContentChild::AllocPOfflineCacheUpdateChild(const URIParams& manifestURI, const URIParams& documentURI, const PrincipalInfo& aLoadingPrincipalInfo, - const bool& stickDocument, - const TabId& aTabId) + const bool& stickDocument) { NS_RUNTIMEABORT("unused"); return nullptr; } bool ContentChild::DeallocPOfflineCacheUpdateChild(POfflineCacheUpdateChild* actor) {
--- a/dom/ipc/ContentChild.h +++ b/dom/ipc/ContentChild.h @@ -569,18 +569,17 @@ public: PBrowserOrId GetBrowserOrId(TabChild* aTabChild); virtual POfflineCacheUpdateChild* AllocPOfflineCacheUpdateChild(const URIParams& manifestURI, const URIParams& documentURI, const PrincipalInfo& aLoadingPrincipalInfo, - const bool& stickDocument, - const TabId& aTabId) override; + const bool& stickDocument) override; virtual bool DeallocPOfflineCacheUpdateChild(POfflineCacheUpdateChild* offlineCacheUpdate) override; virtual PWebrtcGlobalChild* AllocPWebrtcGlobalChild() override; virtual bool DeallocPWebrtcGlobalChild(PWebrtcGlobalChild *aActor) override;
--- a/dom/ipc/ContentParent.cpp +++ b/dom/ipc/ContentParent.cpp @@ -1232,16 +1232,18 @@ ContentParent::RecvFindPlugins(const uin return true; } /*static*/ TabParent* ContentParent::CreateBrowserOrApp(const TabContext& aContext, Element* aFrameElement, ContentParent* aOpenerContentParent) { + PROFILER_LABEL_FUNC(js::ProfileEntry::Category::OTHER); + if (!sCanLaunchSubprocesses) { return nullptr; } if (TabParent* parent = TabParent::GetNextTabParent()) { parent->SetOwnerElement(aFrameElement); return parent; } @@ -2381,16 +2383,18 @@ ContentParent::InitializeMembers() mShutdownPending = false; mIPCOpen = true; mHangMonitorActor = nullptr; } bool ContentParent::LaunchSubprocess(ProcessPriority aInitialPriority /* = PROCESS_PRIORITY_FOREGROUND */) { + PROFILER_LABEL_FUNC(js::ProfileEntry::Category::OTHER); + std::vector<std::string> extraArgs; if (mIsNuwaProcess) { extraArgs.push_back("-nuwa"); } if (!mSubprocess->LaunchAndWaitForProcessHandle(extraArgs)) { MarkAsDead(); return false; @@ -3637,16 +3641,18 @@ ContentParent::ForceKillTimerCallback(ns auto self = static_cast<ContentParent*>(aClosure); self->KillHard("ShutDownKill"); } void ContentParent::KillHard(const char* aReason) { + PROFILER_LABEL_FUNC(js::ProfileEntry::Category::OTHER); + // On Windows, calling KillHard multiple times causes problems - the // process handle becomes invalid on the first call, causing a second call // to crash our process - more details in bug 890840. if (mCalledKillHard) { return; } mCalledKillHard = true; mForceKillTimer = nullptr; @@ -5235,38 +5241,30 @@ ContentParent::GetManagedTabContext() return Move(ContentProcessManager::GetSingleton()-> GetTabContextByContentProcess(this->ChildID())); } mozilla::docshell::POfflineCacheUpdateParent* ContentParent::AllocPOfflineCacheUpdateParent(const URIParams& aManifestURI, const URIParams& aDocumentURI, const PrincipalInfo& aLoadingPrincipalInfo, - const bool& aStickDocument, - const TabId& aTabId) -{ - TabContext tabContext; - if (!ContentProcessManager::GetSingleton()-> - GetTabContextByProcessAndTabId(this->ChildID(), aTabId, &tabContext)) { - return nullptr; - } + const bool& aStickDocument) +{ RefPtr<mozilla::docshell::OfflineCacheUpdateParent> update = - new mozilla::docshell::OfflineCacheUpdateParent( - tabContext.OriginAttributesRef()); + new mozilla::docshell::OfflineCacheUpdateParent(); // Use this reference as the IPDL reference. return update.forget().take(); } bool ContentParent::RecvPOfflineCacheUpdateConstructor(POfflineCacheUpdateParent* aActor, const URIParams& aManifestURI, const URIParams& aDocumentURI, const PrincipalInfo& aLoadingPrincipal, - const bool& aStickDocument, - const TabId& aTabId) + const bool& aStickDocument) { MOZ_ASSERT(aActor); RefPtr<mozilla::docshell::OfflineCacheUpdateParent> update = static_cast<mozilla::docshell::OfflineCacheUpdateParent*>(aActor); nsresult rv = update->Schedule(aManifestURI, aDocumentURI, aLoadingPrincipal, aStickDocument); if (NS_FAILED(rv) && IsAlive()) {
--- a/dom/ipc/ContentParent.h +++ b/dom/ipc/ContentParent.h @@ -452,26 +452,24 @@ public: const ContentParentId& aCpId) override; nsTArray<TabContext> GetManagedTabContext(); virtual POfflineCacheUpdateParent* AllocPOfflineCacheUpdateParent(const URIParams& aManifestURI, const URIParams& aDocumentURI, const PrincipalInfo& aLoadingPrincipalInfo, - const bool& aStickDocument, - const TabId& aTabId) override; + const bool& aStickDocument) override; virtual bool RecvPOfflineCacheUpdateConstructor(POfflineCacheUpdateParent* aActor, const URIParams& aManifestURI, const URIParams& aDocumentURI, const PrincipalInfo& aLoadingPrincipal, - const bool& stickDocument, - const TabId& aTabId) override; + const bool& stickDocument) override; virtual bool DeallocPOfflineCacheUpdateParent(POfflineCacheUpdateParent* aActor) override; virtual bool RecvSetOfflinePermission(const IPC::Principal& principal) override; virtual bool RecvFinishShutdown() override;
--- a/dom/ipc/PBrowser.ipdl +++ b/dom/ipc/PBrowser.ipdl @@ -415,17 +415,17 @@ parent: sync BrowserFrameOpenWindow(PBrowser opener, nsString aURL, nsString aName, nsString aFeatures) returns (bool windowOpened); /** * Instructs the TabParent to forward a request to zoom to a rect given in * CSS pixels. This rect is relative to the document. */ - ZoomToRect(uint32_t aPresShellId, ViewID aViewId, CSSRect aRect); + ZoomToRect(uint32_t aPresShellId, ViewID aViewId, CSSRect aRect, uint32_t aFlags); /** * We know for sure that content has either preventDefaulted or not * preventDefaulted. This applies to an entire batch of touch events. It is * expected that, if there are any DOM touch listeners, touch events will be * batched and only processed for panning and zooming if content does not * preventDefault. */
--- a/dom/ipc/PContent.ipdl +++ b/dom/ipc/PContent.ipdl @@ -1080,18 +1080,17 @@ parent: * cache group update. But we must cache the document (identified by the * documentURI). This argument will ensure that a previously uncached * document will get cached and that we don't re-cache a document that * has already been cached (stickDocument=false). * @param tabId * To identify which tab owns the app. */ POfflineCacheUpdate(URIParams manifestURI, URIParams documentURI, - PrincipalInfo loadingPrincipal, bool stickDocument, - TabId tabId); + PrincipalInfo loadingPrincipal, bool stickDocument); /** * Sets "offline-app" permission for the principal. Called when we hit * a web app with the manifest attribute in <html> and * offline-apps.allow_by_default is set to true. */ SetOfflinePermission(Principal principal);
--- a/dom/ipc/TabChild.cpp +++ b/dom/ipc/TabChild.cpp @@ -1686,17 +1686,17 @@ TabChild::RecvHandleDoubleTap(const CSSP // The double-tap can be dispatched by any scroll frame (so |aGuid| could be // the guid of any scroll frame), but the zoom-to-rect operation must be // performed by the root content scroll frame, so query its identifiers // for the SendZoomToRect() call rather than using the ones from |aGuid|. uint32_t presShellId; ViewID viewId; if (APZCCallbackHelper::GetOrCreateScrollIdentifiers( document->GetDocumentElement(), &presShellId, &viewId)) { - SendZoomToRect(presShellId, viewId, zoomToRect); + SendZoomToRect(presShellId, viewId, zoomToRect, DEFAULT_BEHAVIOR); } return true; } bool TabChild::RecvHandleSingleTap(const CSSPoint& aPoint, const Modifiers& aModifiers, const ScrollableLayerGuid& aGuid) {
--- a/dom/ipc/TabParent.cpp +++ b/dom/ipc/TabParent.cpp @@ -2793,20 +2793,21 @@ TabParent::RecvBrowserFrameOpenWindow(PB this, aURL, aName, aFeatures); *aOutWindowOpened = (opened == BrowserElementParent::OPEN_WINDOW_ADDED); return true; } bool TabParent::RecvZoomToRect(const uint32_t& aPresShellId, const ViewID& aViewId, - const CSSRect& aRect) + const CSSRect& aRect, + const uint32_t& aFlags) { if (RenderFrameParent* rfp = GetRenderFrame()) { - rfp->ZoomToRect(aPresShellId, aViewId, aRect); + rfp->ZoomToRect(aPresShellId, aViewId, aRect, aFlags); } return true; } bool TabParent::RecvUpdateZoomConstraints(const uint32_t& aPresShellId, const ViewID& aViewId, const MaybeZoomConstraints& aConstraints)
--- a/dom/ipc/TabParent.h +++ b/dom/ipc/TabParent.h @@ -294,17 +294,18 @@ public: virtual bool RecvGetWidgetNativeData(WindowsHandle* aValue) override; virtual bool RecvSetNativeChildOfShareableWindow(const uintptr_t& childWindow) override; virtual bool RecvDispatchFocusToTopLevelWindow() override; virtual bool RecvZoomToRect(const uint32_t& aPresShellId, const ViewID& aViewId, - const CSSRect& aRect) override; + const CSSRect& aRect, + const uint32_t& aFlags) override; virtual bool RecvUpdateZoomConstraints(const uint32_t& aPresShellId, const ViewID& aViewId, const MaybeZoomConstraints& aConstraints) override; virtual bool RecvRespondStartSwipeEvent(const uint64_t& aInputBlockId, const bool& aStartSwipe) override;
--- a/dom/media/AudioStream.cpp +++ b/dom/media/AudioStream.cpp @@ -16,23 +16,23 @@ #include <algorithm> #include "mozilla/Telemetry.h" #include "CubebUtils.h" #include "nsPrintfCString.h" #include "gfxPrefs.h" namespace mozilla { -#ifdef LOG #undef LOG -#endif +#undef LOGW LazyLogModule gAudioStreamLog("AudioStream"); // For simple logs -#define LOG(x) MOZ_LOG(gAudioStreamLog, mozilla::LogLevel::Debug, x) +#define LOG(x, ...) MOZ_LOG(gAudioStreamLog, mozilla::LogLevel::Debug, ("%p " x, this, ##__VA_ARGS__)) +#define LOGW(x, ...) MOZ_LOG(gAudioStreamLog, mozilla::LogLevel::Warning, ("%p " x, this, ##__VA_ARGS__)) /** * When MOZ_DUMP_AUDIO is set in the environment (to anything), * we'll drop a series of files in the current working directory named * dumped-audio-<nnn>.wav, one per AudioStream created, containing * the audio for the stream including any skips due to underruns. */ static int gDumpedAudioCount = 0; @@ -122,26 +122,25 @@ AudioStream::AudioStream(DataSource& aSo : mMonitor("AudioStream") , mInRate(0) , mOutRate(0) , mChannels(0) , mOutChannels(0) , mAudioClock(this) , mTimeStretcher(nullptr) , mDumpFile(nullptr) - , mBytesPerFrame(0) , mState(INITIALIZED) , mIsMonoAudioEnabled(gfxPrefs::MonoAudio()) , mDataSource(aSource) { } AudioStream::~AudioStream() { - LOG(("AudioStream: delete %p, state %d", this, mState)); + LOG("deleted, state %d", mState); MOZ_ASSERT(mState == SHUTDOWN && !mCubebStream, "Should've called Shutdown() before deleting an AudioStream"); if (mDumpFile) { fclose(mDumpFile); } if (mTimeStretcher) { soundtouch::destroySoundTouchObj(mTimeStretcher); } @@ -334,17 +333,16 @@ AudioStream::Init(int32_t aNumChannels, return NS_ERROR_INVALID_ARG; } #endif if (AUDIO_OUTPUT_FORMAT == AUDIO_FORMAT_S16) { params.format = CUBEB_SAMPLE_S16NE; } else { params.format = CUBEB_SAMPLE_FLOAT32NE; } - mBytesPerFrame = sizeof(AudioDataValue) * mOutChannels; mAudioClock.Init(); return OpenCubeb(params); } // This code used to live inside AudioStream::Init(), but on Mac (others?) // it has been known to take 300-800 (or even 8500) ms to execute(!) @@ -378,18 +376,18 @@ AudioStream::OpenCubeb(cubeb_stream_para return NS_ERROR_FAILURE; } } mState = INITIALIZED; if (!mStartTime.IsNull()) { TimeDuration timeDelta = TimeStamp::Now() - mStartTime; - LOG(("AudioStream creation time %sfirst: %u ms", mIsFirst ? "" : "not ", - (uint32_t) timeDelta.ToMilliseconds())); + LOG("creation time %sfirst: %u ms", mIsFirst ? "" : "not ", + (uint32_t) timeDelta.ToMilliseconds()); Telemetry::Accumulate(mIsFirst ? Telemetry::AUDIOSTREAM_FIRST_OPEN_MS : Telemetry::AUDIOSTREAM_LATER_OPEN_MS, timeDelta.ToMilliseconds()); } return NS_OK; } void @@ -425,17 +423,17 @@ AudioStream::StartUnlocked() r = cubeb_stream_start(mCubebStream.get()); // DataCallback might be called before we exit this scope // if cubeb_stream_start() succeeds. mState must be set to STARTED // beforehand. } if (r != CUBEB_OK) { mState = ERRORED; } - LOG(("AudioStream: started %p, state %s", this, mState == STARTED ? "STARTED" : "ERRORED")); + LOG("started, state %s", mState == STARTED ? "STARTED" : "ERRORED"); } } void AudioStream::Pause() { MonitorAutoLock mon(mMonitor); @@ -475,17 +473,17 @@ AudioStream::Resume() mState = STARTED; } } void AudioStream::Shutdown() { MonitorAutoLock mon(mMonitor); - LOG(("AudioStream: Shutdown %p, state %d", this, mState)); + LOG("Shutdown, state %d", mState); if (mCubebStream) { MonitorAutoUnlock mon(mMonitor); // Force stop to put the cubeb stream in a stable state before deletion. cubeb_stream_stop(mCubebStream.get()); // Must not try to shut down cubeb from within the lock! wasapi may still // call our callback after Pause()/stop()!?! Bug 996162 mCubebStream.reset(); @@ -548,157 +546,147 @@ AudioStream::Downmix(AudioDataValue* aBu if (mChannels >= 2 && mIsMonoAudioEnabled) { DownmixStereoToMono(aBuffer, aFrames); } return true; } -long -AudioStream::GetUnprocessed(void* aBuffer, long aFrames) +void +AudioStream::GetUnprocessed(AudioBufferWriter& aWriter) { mMonitor.AssertCurrentThreadOwns(); - uint8_t* wpos = reinterpret_cast<uint8_t*>(aBuffer); // Flush the timestretcher pipeline, if we were playing using a playback rate // other than 1.0. - uint32_t flushedFrames = 0; if (mTimeStretcher && mTimeStretcher->numSamples()) { - flushedFrames = mTimeStretcher->receiveSamples(reinterpret_cast<AudioDataValue*>(wpos), aFrames); - wpos += FramesToBytes(flushedFrames); + auto timeStretcher = mTimeStretcher; + aWriter.Write([timeStretcher] (AudioDataValue* aPtr, uint32_t aFrames) { + return timeStretcher->receiveSamples(aPtr, aFrames); + }, aWriter.Available()); // TODO: There might be still unprocessed samples in the stretcher. // We should either remove or flush them so they won't be in the output // next time we switch a playback rate other than 1.0. NS_WARN_IF(mTimeStretcher->numUnprocessedSamples() > 0); } - uint32_t toPopFrames = aFrames - flushedFrames; - while (toPopFrames > 0) { - UniquePtr<Chunk> c = mDataSource.PopFrames(toPopFrames); + while (aWriter.Available() > 0) { + UniquePtr<Chunk> c = mDataSource.PopFrames(aWriter.Available()); if (c->Frames() == 0) { break; } - MOZ_ASSERT(c->Frames() <= toPopFrames); + MOZ_ASSERT(c->Frames() <= aWriter.Available()); if (Downmix(c->GetWritable(), c->Frames())) { - memcpy(wpos, c->Data(), FramesToBytes(c->Frames())); + aWriter.Write(c->Data(), c->Frames()); } else { // Write silence if downmixing fails. - memset(wpos, 0, FramesToBytes(c->Frames())); + aWriter.WriteZeros(c->Frames()); } - wpos += FramesToBytes(c->Frames()); - toPopFrames -= c->Frames(); } - - return aFrames - toPopFrames; } -long -AudioStream::GetTimeStretched(void* aBuffer, long aFrames) +void +AudioStream::GetTimeStretched(AudioBufferWriter& aWriter) { mMonitor.AssertCurrentThreadOwns(); // We need to call the non-locking version, because we already have the lock. if (EnsureTimeStretcherInitializedUnlocked() != NS_OK) { - return 0; + return; } - uint8_t* wpos = reinterpret_cast<uint8_t*>(aBuffer); double playbackRate = static_cast<double>(mInRate) / mOutRate; - uint32_t toPopFrames = ceil(aFrames * playbackRate); + uint32_t toPopFrames = ceil(aWriter.Available() * playbackRate); - while (mTimeStretcher->numSamples() < static_cast<uint32_t>(aFrames)) { + while (mTimeStretcher->numSamples() < aWriter.Available()) { UniquePtr<Chunk> c = mDataSource.PopFrames(toPopFrames); if (c->Frames() == 0) { break; } MOZ_ASSERT(c->Frames() <= toPopFrames); if (Downmix(c->GetWritable(), c->Frames())) { mTimeStretcher->putSamples(c->Data(), c->Frames()); } else { // Write silence if downmixing fails. nsAutoTArray<AudioDataValue, 1000> buf; buf.SetLength(mOutChannels * c->Frames()); memset(buf.Elements(), 0, buf.Length() * sizeof(AudioDataValue)); mTimeStretcher->putSamples(buf.Elements(), c->Frames()); } } - uint32_t receivedFrames = mTimeStretcher->receiveSamples(reinterpret_cast<AudioDataValue*>(wpos), aFrames); - wpos += FramesToBytes(receivedFrames); - return receivedFrames; + auto timeStretcher = mTimeStretcher; + aWriter.Write([timeStretcher] (AudioDataValue* aPtr, uint32_t aFrames) { + return timeStretcher->receiveSamples(aPtr, aFrames); + }, aWriter.Available()); } long AudioStream::DataCallback(void* aBuffer, long aFrames) { MonitorAutoLock mon(mMonitor); MOZ_ASSERT(mState != SHUTDOWN, "No data callback after shutdown"); - uint32_t underrunFrames = 0; - uint32_t servicedFrames = 0; + + auto writer = AudioBufferWriter( + reinterpret_cast<AudioDataValue*>(aBuffer), mOutChannels, aFrames); // FIXME: cubeb_pulse sometimes calls us before cubeb_stream_start() is called. // We don't want to consume audio data until Start() is called by the client. if (mState == INITIALIZED) { NS_WARNING("data callback fires before cubeb_stream_start() is called"); mAudioClock.UpdateFrameHistory(0, aFrames); - memset(aBuffer, 0, FramesToBytes(aFrames)); - return aFrames; + return writer.WriteZeros(aFrames); } // NOTE: wasapi (others?) can call us back *after* stop()/Shutdown() (mState == SHUTDOWN) // Bug 996162 // callback tells us cubeb succeeded initializing if (mState == STARTED) { mState = RUNNING; } if (mInRate == mOutRate) { - servicedFrames = GetUnprocessed(aBuffer, aFrames); + GetUnprocessed(writer); } else { - servicedFrames = GetTimeStretched(aBuffer, aFrames); + GetTimeStretched(writer); } - underrunFrames = aFrames - servicedFrames; - // Always send audible frames first, and silent frames later. // Otherwise it will break the assumption of FrameHistory. if (!mDataSource.Ended()) { - mAudioClock.UpdateFrameHistory(servicedFrames, underrunFrames); - uint8_t* rpos = static_cast<uint8_t*>(aBuffer) + FramesToBytes(aFrames - underrunFrames); - memset(rpos, 0, FramesToBytes(underrunFrames)); - if (underrunFrames) { - MOZ_LOG(gAudioStreamLog, LogLevel::Warning, - ("AudioStream %p lost %d frames", this, underrunFrames)); + mAudioClock.UpdateFrameHistory(aFrames - writer.Available(), writer.Available()); + if (writer.Available() > 0) { + LOGW("lost %d frames", writer.Available()); + writer.WriteZeros(writer.Available()); } - servicedFrames += underrunFrames; } else { // No more new data in the data source. Don't send silent frames so the // cubeb stream can start draining. - mAudioClock.UpdateFrameHistory(servicedFrames, 0); + mAudioClock.UpdateFrameHistory(aFrames - writer.Available(), 0); } WriteDumpFile(mDumpFile, this, aFrames, aBuffer); - return servicedFrames; + return aFrames - writer.Available(); } void AudioStream::StateCallback(cubeb_state aState) { MonitorAutoLock mon(mMonitor); MOZ_ASSERT(mState != SHUTDOWN, "No state callback after shutdown"); - LOG(("AudioStream: StateCallback %p, mState=%d cubeb_state=%d", this, mState, aState)); + LOG("StateCallback, mState=%d cubeb_state=%d", mState, aState); if (aState == CUBEB_STATE_DRAINED) { mState = DRAINED; mDataSource.Drained(); } else if (aState == CUBEB_STATE_ERROR) { - LOG(("AudioStream::StateCallback() state %d cubeb error", mState)); + LOG("StateCallback() state %d cubeb error", mState); mState = ERRORED; } } AudioClock::AudioClock(AudioStream* aStream) :mAudioStream(aStream), mOutRate(0), mInRate(0),
--- a/dom/media/AudioStream.h +++ b/dom/media/AudioStream.h @@ -143,16 +143,76 @@ public: private: UniquePtr<uint8_t[]> mBuffer; uint32_t mCapacity; uint32_t mStart; uint32_t mCount; }; +/* + * A bookkeeping class to track the read/write position of an audio buffer. + */ +class AudioBufferCursor { +public: + AudioBufferCursor(AudioDataValue* aPtr, uint32_t aChannels, uint32_t aFrames) + : mPtr(aPtr), mChannels(aChannels), mFrames(aFrames) {} + + // Advance the cursor to account for frames that are consumed. + uint32_t Advance(uint32_t aFrames) { + MOZ_ASSERT(mFrames >= aFrames); + mFrames -= aFrames; + mPtr += mChannels * aFrames; + return aFrames; + } + + // The number of frames available for read/write in this buffer. + uint32_t Available() const { return mFrames; } + + // Return a pointer where read/write should begin. + AudioDataValue* Ptr() const { return mPtr; } + +protected: + AudioDataValue* mPtr; + const uint32_t mChannels; + uint32_t mFrames; +}; + +/* + * A helper class to encapsulate pointer arithmetic and provide means to modify + * the underlying audio buffer. + */ +class AudioBufferWriter : private AudioBufferCursor { +public: + AudioBufferWriter(AudioDataValue* aPtr, uint32_t aChannels, uint32_t aFrames) + : AudioBufferCursor(aPtr, aChannels, aFrames) {} + + uint32_t WriteZeros(uint32_t aFrames) { + memset(mPtr, 0, sizeof(AudioDataValue) * mChannels * aFrames); + return Advance(aFrames); + } + + uint32_t Write(const AudioDataValue* aPtr, uint32_t aFrames) { + memcpy(mPtr, aPtr, sizeof(AudioDataValue) * mChannels * aFrames); + return Advance(aFrames); + } + + // Provide a write fuction to update the audio buffer with the following + // signature: uint32_t(const AudioDataValue* aPtr, uint32_t aFrames) + // aPtr: Pointer to the audio buffer. + // aFrames: The number of frames available in the buffer. + // return: The number of frames actually written by the function. + template <typename Function> + uint32_t Write(const Function& aFunction, uint32_t aFrames) { + return Advance(aFunction(mPtr, aFrames)); + } + + using AudioBufferCursor::Available; +}; + // Access to a single instance of this class must be synchronized by // callers, or made from a single thread. One exception is that access to // GetPosition, GetPositionInFrames, SetVolume, and Get{Rate,Channels}, // SetMicrophoneActive is thread-safe without external synchronization. class AudioStream final { virtual ~AudioStream(); @@ -258,18 +318,18 @@ private: long DataCallback(void* aBuffer, long aFrames); void StateCallback(cubeb_state aState); nsresult EnsureTimeStretcherInitializedUnlocked(); // Return true if downmixing succeeds otherwise false. bool Downmix(AudioDataValue* aBuffer, uint32_t aFrames); - long GetUnprocessed(void* aBuffer, long aFrames); - long GetTimeStretched(void* aBuffer, long aFrames); + void GetUnprocessed(AudioBufferWriter& aWriter); + void GetTimeStretched(AudioBufferWriter& aWriter); void StartUnlocked(); // The monitor is held to protect all access to member variables. Monitor mMonitor; // Input rate in Hz (characteristic of the media being played) int mInRate; @@ -287,28 +347,16 @@ private: TimeStamp mStartTime; // Output file for dumping audio FILE* mDumpFile; // Owning reference to a cubeb_stream. UniquePtr<cubeb_stream, CubebDestroyPolicy> mCubebStream; - uint32_t mBytesPerFrame; - - uint32_t BytesToFrames(uint32_t aBytes) { - NS_ASSERTION(aBytes % mBytesPerFrame == 0, - "Byte count not aligned on frames size."); - return aBytes / mBytesPerFrame; - } - - uint32_t FramesToBytes(uint32_t aFrames) { - return aFrames * mBytesPerFrame; - } - enum StreamState { INITIALIZED, // Initialized, playback has not begun. STARTED, // cubeb started, but callbacks haven't started RUNNING, // DataCallbacks have started after STARTED, or after Resume(). STOPPED, // Stopped by a call to Pause(). DRAINED, // StateCallback has indicated that the drain is complete. ERRORED, // Stream disabled due to an internal error. SHUTDOWN // Shutdown has been called
--- a/dom/media/mediasink/DecodedAudioDataSink.cpp +++ b/dom/media/mediasink/DecodedAudioDataSink.cpp @@ -162,27 +162,22 @@ DecodedAudioDataSink::GetEndTime() const return playedUsecs.value(); } UniquePtr<AudioStream::Chunk> DecodedAudioDataSink::PopFrames(uint32_t aFrames) { class Chunk : public AudioStream::Chunk { public: - Chunk(AudioData* aBuffer, uint32_t aFrames, uint32_t aOffset) - : mBuffer(aBuffer) - , mFrames(aFrames) - , mData(aBuffer->mAudioData.get() + aBuffer->mChannels * aOffset) { - MOZ_ASSERT(aOffset + aFrames <= aBuffer->mFrames); - } + Chunk(AudioData* aBuffer, uint32_t aFrames, AudioDataValue* aData) + : mBuffer(aBuffer), mFrames(aFrames), mData(aData) {} Chunk() : mFrames(0), mData(nullptr) {} const AudioDataValue* Data() const { return mData; } uint32_t Frames() const { return mFrames; } AudioDataValue* GetWritable() const { return mData; } - private: const RefPtr<AudioData> mBuffer; const uint32_t mFrames; AudioDataValue* const mData; }; class SilentChunk : public AudioStream::Chunk { public: @@ -227,42 +222,44 @@ DecodedAudioDataSink::PopFrames(uint32_t // hardware so that the next audio chunk begins playback at the correct // time. missingFrames = std::min<int64_t>(UINT32_MAX, missingFrames.value()); auto framesToPop = std::min<uint32_t>(missingFrames.value(), aFrames); mWritten += framesToPop; return MakeUnique<SilentChunk>(framesToPop, mInfo.mChannels); } - mFramesPopped = 0; mCurrentData = dont_AddRef(AudioQueue().PopFront().take()->As<AudioData>()); + mCursor = MakeUnique<AudioBufferCursor>(mCurrentData->mAudioData.get(), + mCurrentData->mChannels, + mCurrentData->mFrames); } - auto framesToPop = std::min(aFrames, mCurrentData->mFrames - mFramesPopped); + auto framesToPop = std::min(aFrames, mCursor->Available()); SINK_LOG_V("playing audio at time=%lld offset=%u length=%u", - mCurrentData->mTime, mFramesPopped, framesToPop); + mCurrentData->mTime, mCurrentData->mFrames - mCursor->Available(), framesToPop); UniquePtr<AudioStream::Chunk> chunk; if (mCurrentData->mRate == mInfo.mRate && mCurrentData->mChannels == mInfo.mChannels) { - chunk = MakeUnique<Chunk>(mCurrentData, framesToPop, mFramesPopped); + chunk = MakeUnique<Chunk>(mCurrentData, framesToPop, mCursor->Ptr()); } else { SINK_LOG_V("mismatched sample format mInfo=[%uHz/%u channels] audio=[%uHz/%u channels]", mInfo.mRate, mInfo.mChannels, mCurrentData->mRate, mCurrentData->mChannels); chunk = MakeUnique<SilentChunk>(framesToPop, mInfo.mChannels); } mWritten += framesToPop; - mFramesPopped += framesToPop; + mCursor->Advance(framesToPop); // All frames are popped. Reset mCurrentData so we can pop new elements from // the audio queue in next calls to PopFrames(). - if (mFramesPopped == mCurrentData->mFrames) { + if (mCursor->Available() == 0) { mCurrentData = nullptr; } return chunk; } bool DecodedAudioDataSink::Ended() const
--- a/dom/media/mediasink/DecodedAudioDataSink.h +++ b/dom/media/mediasink/DecodedAudioDataSink.h @@ -92,18 +92,18 @@ private: MozPromiseHolder<GenericPromise> mEndPromise; /* * Members to implement AudioStream::DataSource. * Used on the callback thread of cubeb. */ // The AudioData at which AudioStream::DataSource is reading. RefPtr<AudioData> mCurrentData; - // The number of frames that have been popped from mCurrentData. - uint32_t mFramesPopped = 0; + // Keep track of the read positoin of mCurrentData. + UniquePtr<AudioBufferCursor> mCursor; // True if there is any error in processing audio data like overflow. bool mErrored = false; }; } // namespace media } // namespace mozilla #endif
--- a/dom/media/systemservices/CamerasChild.cpp +++ b/dom/media/systemservices/CamerasChild.cpp @@ -138,16 +138,72 @@ CamerasChild::RecvReplyNumberOfCapabilit MonitorAutoLock monitor(mReplyMonitor); mReceivedReply = true; mReplySuccess = true; mReplyInteger = numdev; monitor.Notify(); return true; } +// Helper function to dispatch calls to the IPC Thread and +// CamerasChild object. Takes the needed locks and dispatches. +// Takes a "failed" value and a reference to the output variable +// as parameters, will return the right one depending on whether +// dispatching succeeded. +template <class T = int> +class LockAndDispatch +{ +public: + LockAndDispatch(CamerasChild* aCamerasChild, + const char* aRequestingFunc, + nsIRunnable *aRunnable, + const T& aFailureValue = T(-1), const T& aSuccessValue = T(0)) + : mCamerasChild(aCamerasChild), mRequestingFunc(aRequestingFunc), + mRunnable(aRunnable), + mReplyLock(aCamerasChild->mReplyMonitor), + mRequestLock(aCamerasChild->mRequestMutex), + mSuccess(true), + mFailureValue(aFailureValue), mSuccessValue(aSuccessValue) + { + Dispatch(); + } + + const T& ReturnValue() const { + if (mSuccess) { + return mSuccessValue; + } else { + return mFailureValue; + } + } + + const bool& Success() const { + return mSuccess; + } + +private: + void Dispatch() { + if (!mCamerasChild->DispatchToParent(mRunnable, mReplyLock)) { + LOG(("Cameras dispatch for IPC failed in %s", mRequestingFunc)); + mSuccess = false; + } + } + + CamerasChild* mCamerasChild; + const char* mRequestingFunc; + nsIRunnable* mRunnable; + // Prevent concurrent use of the reply variables by holding + // the mReplyMonitor. Note that this is unlocked while waiting for + // the reply to be filled in, necessitating the additional mRequestLock/Mutex; + MonitorAutoLock mReplyLock; + MutexAutoLock mRequestLock; + bool mSuccess; + const T& mFailureValue; + const T& mSuccessValue; +}; + bool CamerasChild::DispatchToParent(nsIRunnable* aRunnable, MonitorAutoLock& aMonitor) { CamerasSingleton::Mutex().AssertCurrentThreadOwns(); CamerasSingleton::Thread()->Dispatch(aRunnable, NS_DISPATCH_NORMAL); // We can't see if the send worked, so we need to be able to bail // out on shutdown (when it failed and we won't get a reply). @@ -165,59 +221,45 @@ CamerasChild::DispatchToParent(nsIRunnab } return true; } int CamerasChild::NumberOfCapabilities(CaptureEngine aCapEngine, const char* deviceUniqueIdUTF8) { - // Prevents multiple outstanding requests from happening. - MutexAutoLock requestLock(mRequestMutex); LOG((__PRETTY_FUNCTION__)); LOG(("NumberOfCapabilities for %s", deviceUniqueIdUTF8)); nsCString unique_id(deviceUniqueIdUTF8); nsCOMPtr<nsIRunnable> runnable = media::NewRunnableFrom([this, aCapEngine, unique_id]() -> nsresult { if (this->SendNumberOfCapabilities(aCapEngine, unique_id)) { return NS_OK; } return NS_ERROR_FAILURE; }); - // Prevent concurrent use of the reply variables. Note - // that this is unlocked while waiting for the reply to be - // filled in, necessitating the first Mutex above. - MonitorAutoLock monitor(mReplyMonitor); - if (!DispatchToParent(runnable, monitor)) { - LOG(("Get capture capability count failed")); - return 0; - } - LOG(("Capture capability count: %d", mReplyInteger)); - return mReplyInteger; + LockAndDispatch<> dispatcher(this, __func__, runnable, 0, mReplyInteger); + LOG(("Capture capability count: %d", dispatcher.ReturnValue())); + return dispatcher.ReturnValue(); } int CamerasChild::NumberOfCaptureDevices(CaptureEngine aCapEngine) { - MutexAutoLock requestLock(mRequestMutex); LOG((__PRETTY_FUNCTION__)); nsCOMPtr<nsIRunnable> runnable = media::NewRunnableFrom([this, aCapEngine]() -> nsresult { if (this->SendNumberOfCaptureDevices(aCapEngine)) { return NS_OK; } return NS_ERROR_FAILURE; }); - MonitorAutoLock monitor(mReplyMonitor); - if (!DispatchToParent(runnable, monitor)) { - LOG(("Get NumberOfCaptureDevices failed")); - return 0; - } - LOG(("Capture Devices: %d", mReplyInteger)); - return mReplyInteger; + LockAndDispatch<> dispatcher(this, __func__, runnable, 0, mReplyInteger); + LOG(("Capture Devices: %d", dispatcher.ReturnValue())); + return dispatcher.ReturnValue(); } bool CamerasChild::RecvReplyNumberOfCaptureDevices(const int& numdev) { LOG((__PRETTY_FUNCTION__)); MonitorAutoLock monitor(mReplyMonitor); mReceivedReply = true; @@ -228,32 +270,30 @@ CamerasChild::RecvReplyNumberOfCaptureDe } int CamerasChild::GetCaptureCapability(CaptureEngine aCapEngine, const char* unique_idUTF8, const unsigned int capability_number, webrtc::CaptureCapability& capability) { - MutexAutoLock requestLock(mRequestMutex); LOG(("GetCaptureCapability: %s %d", unique_idUTF8, capability_number)); nsCString unique_id(unique_idUTF8); nsCOMPtr<nsIRunnable> runnable = media::NewRunnableFrom([this, aCapEngine, unique_id, capability_number]() -> nsresult { if (this->SendGetCaptureCapability(aCapEngine, unique_id, capability_number)) { return NS_OK; } return NS_ERROR_FAILURE; }); - MonitorAutoLock monitor(mReplyMonitor); - if (!DispatchToParent(runnable, monitor)) { - return -1; + LockAndDispatch<> dispatcher(this, __func__, runnable); + if (dispatcher.Success()) { + capability = mReplyCapability; } - capability = mReplyCapability; - return 0; + return dispatcher.ReturnValue(); } bool CamerasChild::RecvReplyGetCaptureCapability(const CaptureCapability& ipcCapability) { LOG((__PRETTY_FUNCTION__)); MonitorAutoLock monitor(mReplyMonitor); mReceivedReply = true; @@ -271,34 +311,31 @@ CamerasChild::RecvReplyGetCaptureCapabil int CamerasChild::GetCaptureDevice(CaptureEngine aCapEngine, unsigned int list_number, char* device_nameUTF8, const unsigned int device_nameUTF8Length, char* unique_idUTF8, const unsigned int unique_idUTF8Length) { - MutexAutoLock requestLock(mRequestMutex); LOG((__PRETTY_FUNCTION__)); nsCOMPtr<nsIRunnable> runnable = media::NewRunnableFrom([this, aCapEngine, list_number]() -> nsresult { if (this->SendGetCaptureDevice(aCapEngine, list_number)) { return NS_OK; } return NS_ERROR_FAILURE; }); - MonitorAutoLock monitor(mReplyMonitor); - if (!DispatchToParent(runnable, monitor)) { - LOG(("GetCaptureDevice failed")); - return -1; + LockAndDispatch<> dispatcher(this, __func__, runnable); + if (dispatcher.Success()) { + base::strlcpy(device_nameUTF8, mReplyDeviceName.get(), device_nameUTF8Length); + base::strlcpy(unique_idUTF8, mReplyDeviceID.get(), unique_idUTF8Length); + LOG(("Got %s name %s id", device_nameUTF8, unique_idUTF8)); } - base::strlcpy(device_nameUTF8, mReplyDeviceName.get(), device_nameUTF8Length); - base::strlcpy(unique_idUTF8, mReplyDeviceID.get(), unique_idUTF8Length); - LOG(("Got %s name %s id", device_nameUTF8, unique_idUTF8)); - return 0; + return dispatcher.ReturnValue(); } bool CamerasChild::RecvReplyGetCaptureDevice(const nsCString& device_name, const nsCString& device_id) { LOG((__PRETTY_FUNCTION__)); MonitorAutoLock monitor(mReplyMonitor); @@ -311,34 +348,31 @@ CamerasChild::RecvReplyGetCaptureDevice( } int CamerasChild::AllocateCaptureDevice(CaptureEngine aCapEngine, const char* unique_idUTF8, const unsigned int unique_idUTF8Length, int& capture_id) { - MutexAutoLock requestLock(mRequestMutex); LOG((__PRETTY_FUNCTION__)); nsCString unique_id(unique_idUTF8); nsCOMPtr<nsIRunnable> runnable = media::NewRunnableFrom([this, aCapEngine, unique_id]() -> nsresult { if (this->SendAllocateCaptureDevice(aCapEngine, unique_id)) { return NS_OK; } return NS_ERROR_FAILURE; }); - MonitorAutoLock monitor(mReplyMonitor); - if (!DispatchToParent(runnable, monitor)) { - LOG(("AllocateCaptureDevice failed")); - return -1; + LockAndDispatch<> dispatcher(this, __func__, runnable); + if (dispatcher.Success()) { + LOG(("Capture Device allocated: %d", mReplyInteger)); + capture_id = mReplyInteger; } - LOG(("Capture Device allocated: %d", mReplyInteger)); - capture_id = mReplyInteger; - return 0; + return dispatcher.ReturnValue(); } bool CamerasChild::RecvReplyAllocateCaptureDevice(const int& numdev) { LOG((__PRETTY_FUNCTION__)); MonitorAutoLock monitor(mReplyMonitor); @@ -348,30 +382,26 @@ CamerasChild::RecvReplyAllocateCaptureDe monitor.Notify(); return true; } int CamerasChild::ReleaseCaptureDevice(CaptureEngine aCapEngine, const int capture_id) { - MutexAutoLock requestLock(mRequestMutex); LOG((__PRETTY_FUNCTION__)); nsCOMPtr<nsIRunnable> runnable = media::NewRunnableFrom([this, aCapEngine, capture_id]() -> nsresult { if (this->SendReleaseCaptureDevice(aCapEngine, capture_id)) { return NS_OK; } return NS_ERROR_FAILURE; }); - MonitorAutoLock monitor(mReplyMonitor); - if (!DispatchToParent(runnable, monitor)) { - return -1; - } - return 0; + LockAndDispatch<> dispatcher(this, __func__, runnable); + return dispatcher.ReturnValue(); } void CamerasChild::AddCallback(const CaptureEngine aCapEngine, const int capture_id, webrtc::ExternalRenderer* render) { MutexAutoLock lock(mCallbackMutex); CapturerElement ce; @@ -395,58 +425,52 @@ CamerasChild::RemoveCallback(const Captu } int CamerasChild::StartCapture(CaptureEngine aCapEngine, const int capture_id, webrtc::CaptureCapability& webrtcCaps, webrtc::ExternalRenderer* cb) { - MutexAutoLock requestLock(mRequestMutex); LOG((__PRETTY_FUNCTION__)); AddCallback(aCapEngine, capture_id, cb); CaptureCapability capCap(webrtcCaps.width, webrtcCaps.height, webrtcCaps.maxFPS, webrtcCaps.expectedCaptureDelay, webrtcCaps.rawType, webrtcCaps.codecType, webrtcCaps.interlaced); nsCOMPtr<nsIRunnable> runnable = media::NewRunnableFrom([this, aCapEngine, capture_id, capCap]() -> nsresult { if (this->SendStartCapture(aCapEngine, capture_id, capCap)) { return NS_OK; } return NS_ERROR_FAILURE; }); - MonitorAutoLock monitor(mReplyMonitor); - if (!DispatchToParent(runnable, monitor)) { - return -1; - } - return 0; + LockAndDispatch<> dispatcher(this, __func__, runnable); + return dispatcher.ReturnValue(); } int CamerasChild::StopCapture(CaptureEngine aCapEngine, const int capture_id) { - MutexAutoLock requestLock(mRequestMutex); LOG((__PRETTY_FUNCTION__)); nsCOMPtr<nsIRunnable> runnable = media::NewRunnableFrom([this, aCapEngine, capture_id]() -> nsresult { if (this->SendStopCapture(aCapEngine, capture_id)) { return NS_OK; } return NS_ERROR_FAILURE; }); - MonitorAutoLock monitor(mReplyMonitor); - if (!DispatchToParent(runnable, monitor)) { - return -1; + LockAndDispatch<> dispatcher(this, __func__, runnable); + if (dispatcher.Success()) { + RemoveCallback(aCapEngine, capture_id); } - RemoveCallback(aCapEngine, capture_id); - return 0; + return dispatcher.ReturnValue(); } void Shutdown(void) { OffTheBooksMutexAutoLock lock(CamerasSingleton::Mutex()); if (!CamerasSingleton::Child()) { // We don't want to cause everything to get fired up if we're
--- a/dom/media/systemservices/CamerasChild.h +++ b/dom/media/systemservices/CamerasChild.h @@ -44,16 +44,18 @@ enum CaptureEngine : int { struct CapturerElement { CaptureEngine engine; int id; webrtc::ExternalRenderer* callback; }; // Forward declaration so we can work with pointers to it. class CamerasChild; +// Helper class in impl that we friend. +template <class T> class LockAndDispatch; // We emulate the sync webrtc.org API with the help of singleton // CamerasSingleton, which manages a pointer to an IPC object, a thread // where IPC operations should run on, and a mutex. // The static function Cameras() will use that Singleton to set up, // if needed, both the thread and the associated IPC objects and return // a pointer to the IPC object. Users can then do IPC calls on that object // after dispatching them to aforementioned thread. @@ -135,16 +137,17 @@ int GetChildAndCall(MEM_FUN&& f, ARGS&&. } else { return -1; } } class CamerasChild final : public PCamerasChild { friend class mozilla::ipc::BackgroundChildImpl; + template <class T> friend class mozilla::camera::LockAndDispatch; public: // We are owned by the PBackground thread only. CamerasSingleton // takes a non-owning reference. NS_INLINE_DECL_REFCOUNTING(CamerasChild) // IPC messages recevied, received on the PBackground thread // these are the actual callbacks with data
--- a/dom/media/webspeech/synth/windows/SapiService.cpp +++ b/dom/media/webspeech/synth/windows/SapiService.cpp @@ -3,16 +3,17 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "nsISupports.h" #include "SapiService.h" #include "nsServiceManagerUtils.h" #include "nsWin32Locale.h" +#include "GeckoProfiler.h" #include "mozilla/dom/nsSynthVoiceRegistry.h" #include "mozilla/dom/nsSpeechTask.h" #include "mozilla/Preferences.h" namespace mozilla { namespace dom { @@ -191,16 +192,18 @@ SapiService::SapiService() SapiService::~SapiService() { } bool SapiService::Init() { + PROFILER_LABEL_FUNC(js::ProfileEntry::Category::OTHER); + MOZ_ASSERT(!mInitialized); if (Preferences::GetBool("media.webspeech.synth.test") || !Preferences::GetBool("media.webspeech.synth.enabled")) { // When enabled, we shouldn't add OS backend (Bug 1160844) return false; }
--- a/dom/offline/nsDOMOfflineResourceList.cpp +++ b/dom/offline/nsDOMOfflineResourceList.cpp @@ -20,16 +20,17 @@ #include "nsContentUtils.h" #include "nsIObserverService.h" #include "nsIScriptGlobalObject.h" #include "nsIWebNavigation.h" #include "mozilla/dom/Event.h" #include "mozilla/dom/OfflineResourceListBinding.h" #include "mozilla/EventDispatcher.h" #include "mozilla/Preferences.h" +#include "mozilla/BasePrincipal.h" #include "nsXULAppAPI.h" #define IS_CHILD_PROCESS() \ (GeckoProcessType_Default != XRE_GetProcessType()) using namespace mozilla; using namespace mozilla::dom; @@ -808,26 +809,28 @@ nsDOMOfflineResourceList::CacheKeys() if (mCachedKeys) return NS_OK; nsCOMPtr<nsIDOMWindow> window = do_QueryInterface(GetOwner()); nsCOMPtr<nsIWebNavigation> webNav = do_GetInterface(window); nsCOMPtr<nsILoadContext> loadContext = do_QueryInterface(webNav); - uint32_t appId = 0; - bool inBrowser = false; + nsAutoCString originSuffix; if (loadContext) { - loadContext->GetAppId(&appId); - loadContext->GetIsInBrowserElement(&inBrowser); + mozilla::DocShellOriginAttributes oa; + bool ok = loadContext->GetOriginAttributes(oa); + NS_ENSURE_TRUE(ok, NS_ERROR_UNEXPECTED); + + oa.CreateSuffix(originSuffix); } nsAutoCString groupID; - mApplicationCacheService->BuildGroupIDForApp( - mManifestURI, appId, inBrowser, groupID); + mApplicationCacheService->BuildGroupIDForSuffix( + mManifestURI, originSuffix, groupID); nsCOMPtr<nsIApplicationCache> appCache; mApplicationCacheService->GetActiveCache(groupID, getter_AddRefs(appCache)); if (!appCache) { return NS_ERROR_DOM_INVALID_STATE_ERR; }
--- a/dom/plugins/base/nsJSNPRuntime.cpp +++ b/dom/plugins/base/nsJSNPRuntime.cpp @@ -1641,16 +1641,18 @@ NPObjWrapper_Enumerate(JSContext *cx, JS static bool NPObjWrapper_Resolve(JSContext *cx, JS::Handle<JSObject*> obj, JS::Handle<jsid> id, bool *resolvedp) { if (JSID_IS_SYMBOL(id)) return true; + PROFILER_LABEL_FUNC(js::ProfileEntry::Category::JS); + NPObject *npobj = GetNPObject(cx, obj); if (!npobj || !npobj->_class || !npobj->_class->hasProperty || !npobj->_class->hasMethod) { ThrowJSException(cx, "Bad NPObject as private data!"); return false; } @@ -2105,16 +2107,18 @@ CreateNPObjectMember(NPP npp, JSContext return true; } static bool NPObjectMember_GetProperty(JSContext *cx, JS::HandleObject obj, JS::HandleId id, JS::MutableHandleValue vp) { + PROFILER_LABEL_FUNC(js::ProfileEntry::Category::OTHER); + if (JSID_IS_SYMBOL(id)) { JS::RootedSymbol sym(cx, JSID_TO_SYMBOL(id)); if (JS::GetSymbolCode(sym) == JS::SymbolCode::toPrimitive) { JS::RootedObject obj(cx, JS_GetFunctionObject( JS_NewFunction( cx, NPObjectMember_toPrimitive, 1, 0, "Symbol.toPrimitive"))); if (!obj)
--- a/dom/plugins/base/nsNPAPIPlugin.cpp +++ b/dom/plugins/base/nsNPAPIPlugin.cpp @@ -260,16 +260,18 @@ nsNPAPIPlugin::RunPluginOOP(const nsPlug #else return true; #endif } inline PluginLibrary* GetNewPluginLibrary(nsPluginTag *aPluginTag) { + PROFILER_LABEL_FUNC(js::ProfileEntry::Category::OTHER); + if (!aPluginTag) { return nullptr; } if (XRE_IsContentProcess()) { return PluginModuleContentParent::LoadModule(aPluginTag->mId, aPluginTag); } @@ -278,16 +280,17 @@ GetNewPluginLibrary(nsPluginTag *aPlugin } return new PluginPRLibrary(aPluginTag->mFullPath.get(), aPluginTag->mLibrary); } // Creates an nsNPAPIPlugin object. One nsNPAPIPlugin object exists per plugin (not instance). nsresult nsNPAPIPlugin::CreatePlugin(nsPluginTag *aPluginTag, nsNPAPIPlugin** aResult) { + PROFILER_LABEL_FUNC(js::ProfileEntry::Category::OTHER); *aResult = nullptr; if (!aPluginTag) { return NS_ERROR_FAILURE; } CheckClassInitialized();
--- a/dom/plugins/base/nsNPAPIPluginInstance.cpp +++ b/dom/plugins/base/nsNPAPIPluginInstance.cpp @@ -275,16 +275,17 @@ nsNPAPIPluginInstance::Destroy() TimeStamp nsNPAPIPluginInstance::StopTime() { return mStopTime; } nsresult nsNPAPIPluginInstance::Initialize(nsNPAPIPlugin *aPlugin, nsPluginInstanceOwner* aOwner, const nsACString& aMIMEType) { + PROFILER_LABEL_FUNC(js::ProfileEntry::Category::OTHER); PLUGIN_LOG(PLUGIN_LOG_NORMAL, ("nsNPAPIPluginInstance::Initialize this=%p\n",this)); NS_ENSURE_ARG_POINTER(aPlugin); NS_ENSURE_ARG_POINTER(aOwner); mPlugin = aPlugin; mOwner = aOwner; @@ -650,16 +651,18 @@ nsresult nsNPAPIPluginInstance::Print(NP } nsresult nsNPAPIPluginInstance::HandleEvent(void* event, int16_t* result, NSPluginCallReentry aSafeToReenterGecko) { if (RUNNING != mRunning) return NS_OK; + PROFILER_LABEL_FUNC(js::ProfileEntry::Category::OTHER); + if (!event) return NS_ERROR_FAILURE; PluginDestructionGuard guard(this); if (!mPlugin || !mPlugin->GetLibrary()) return NS_ERROR_FAILURE;
--- a/dom/plugins/base/nsNPAPIPluginStreamListener.cpp +++ b/dom/plugins/base/nsNPAPIPluginStreamListener.cpp @@ -281,16 +281,17 @@ nsNPAPIPluginStreamListener::CallURLNoti ("NPP URLNotify called: this=%p, npp=%p, notify=%p, reason=%d, url=%s\n", this, npp, mNPStreamWrapper->mNPStream.notifyData, reason, mNotifyURL)); } } nsresult nsNPAPIPluginStreamListener::OnStartBinding(nsPluginStreamListenerPeer* streamPeer) { + PROFILER_LABEL_FUNC(js::ProfileEntry::Category::OTHER); if (!mInst || !mInst->CanFireNotifications() || mStreamCleanedUp) return NS_ERROR_FAILURE; PluginDestructionGuard guard(mInst); nsNPAPIPlugin* plugin = mInst->GetPlugin(); if (!plugin || !plugin->GetLibrary()) return NS_ERROR_FAILURE;
--- a/dom/plugins/base/nsPluginHost.cpp +++ b/dom/plugins/base/nsPluginHost.cpp @@ -1361,16 +1361,17 @@ nsresult nsPluginHost::EnsurePluginLoade aPluginTag->mPlugin = plugin; } return NS_OK; } nsresult nsPluginHost::GetPluginForContentProcess(uint32_t aPluginId, nsNPAPIPlugin** aPlugin) { + PROFILER_LABEL_FUNC(js::ProfileEntry::Category::OTHER); MOZ_ASSERT(XRE_IsParentProcess()); // If plugins haven't been scanned yet, do so now LoadPlugins(); nsPluginTag* pluginTag = PluginWithId(aPluginId); if (pluginTag) { // When setting up a bridge, double check with chrome to see if this plugin @@ -3516,16 +3517,17 @@ nsPluginHost::AddHeadersToChannel(const return rv; } } } nsresult nsPluginHost::StopPluginInstance(nsNPAPIPluginInstance* aInstance) { + PROFILER_LABEL_FUNC(js::ProfileEntry::Category::OTHER); if (PluginDestructionGuard::DelayDestroy(aInstance)) { return NS_OK; } PLUGIN_LOG(PLUGIN_LOG_NORMAL, ("nsPluginHost::StopPluginInstance called instance=%p\n",aInstance)); if (aInstance->HasStartedDestroying()) {
--- a/dom/plugins/base/nsPluginInstanceOwner.cpp +++ b/dom/plugins/base/nsPluginInstanceOwner.cpp @@ -3595,16 +3595,18 @@ nsPluginInstanceOwner::UpdateWindowVisib mPluginWindowVisible = aVisible; UpdateWindowPositionAndClipRect(true); } #endif // XP_MACOSX void nsPluginInstanceOwner::UpdateDocumentActiveState(bool aIsActive) { + PROFILER_LABEL_FUNC(js::ProfileEntry::Category::OTHER); + mPluginDocumentActiveState = aIsActive; #ifndef XP_MACOSX UpdateWindowPositionAndClipRect(true); #ifdef MOZ_WIDGET_ANDROID if (mInstance) { if (!mPluginDocumentActiveState) { RemovePluginView();
--- a/dom/plugins/ipc/PluginModuleParent.cpp +++ b/dom/plugins/ipc/PluginModuleParent.cpp @@ -105,16 +105,17 @@ struct RunnableMethodTraits<mozilla::plu bool mozilla::plugins::SetupBridge(uint32_t aPluginId, dom::ContentParent* aContentParent, bool aForceBridgeNow, nsresult* rv, uint32_t* runID) { + PROFILER_LABEL_FUNC(js::ProfileEntry::Category::OTHER); if (NS_WARN_IF(!rv) || NS_WARN_IF(!runID)) { return false; } PluginModuleChromeParent::ClearInstantiationFlag(); RefPtr<nsPluginHost> host = nsPluginHost::GetInst(); RefPtr<nsNPAPIPlugin> plugin; *rv = host->GetPluginForContentProcess(aPluginId, getter_AddRefs(plugin));
--- a/dom/push/PushServiceHttp2.jsm +++ b/dom/push/PushServiceHttp2.jsm @@ -9,16 +9,17 @@ const Cc = Components.classes; const Ci = Components.interfaces; const Cu = Components.utils; const Cr = Components.results; const {PushDB} = Cu.import("resource://gre/modules/PushDB.jsm"); const {PushRecord} = Cu.import("resource://gre/modules/PushRecord.jsm"); Cu.import("resource://gre/modules/XPCOMUtils.jsm"); Cu.import("resource://gre/modules/Services.jsm"); +Cu.import("resource://gre/modules/NetUtil.jsm"); Cu.import("resource://gre/modules/IndexedDBHelper.jsm"); Cu.import("resource://gre/modules/Timer.jsm"); Cu.import("resource://gre/modules/Preferences.jsm"); Cu.import("resource://gre/modules/Promise.jsm"); const { PushCrypto, concatArray, @@ -98,17 +99,17 @@ PushSubscriptionListener.prototype = { this._pushService.connOnStop(aRequest, Components.isSuccessCode(aStatusCode), this.uri); }, onPush: function(associatedChannel, pushChannel) { console.debug("PushSubscriptionListener: onPush()"); var pushChannelListener = new PushChannelListener(this); - pushChannel.asyncOpen(pushChannelListener, pushChannel); + pushChannel.asyncOpen2(pushChannelListener); }, disconnect: function() { this._pushService = null; } }; /** @@ -429,29 +430,18 @@ this.PushServiceHttp2 = { return this._mainPushService != null; }, disconnect: function() { this._shutdownConnections(false); }, _makeChannel: function(aUri) { - - var ios = Cc["@mozilla.org/network/io-service;1"] - .getService(Ci.nsIIOService); - - var chan = ios.newChannel2(aUri, - null, - null, - null, // aLoadingNode - Services.scriptSecurityManager.getSystemPrincipal(), - null, // aTriggeringPrincipal - Ci.nsILoadInfo.SEC_NORMAL, - Ci.nsIContentPolicy.TYPE_OTHER) - .QueryInterface(Ci.nsIHttpChannel); + var chan = NetUtil.newChannel({uri: aUri, loadUsingSystemPrincipal: true}) + .QueryInterface(Ci.nsIHttpChannel); var loadGroup = Cc["@mozilla.org/network/load-group;1"] .createInstance(Ci.nsILoadGroup); chan.loadGroup = loadGroup; return chan; }, /** @@ -490,33 +480,33 @@ this.PushServiceHttp2 = { var listener = new SubscriptionListener(aSubInfo, resolve, reject, this._serverURI, this); var chan = this._makeChannel(this._serverURI.spec); chan.requestMethod = "POST"; - chan.asyncOpen(listener, null); + chan.asyncOpen2(listener); }) .catch(err => { if ("retry" in err) { return this._subscribeResourceInternal(err.subInfo); } else { throw err; } }) }, _deleteResource: function(aUri) { return new Promise((resolve,reject) => { var chan = this._makeChannel(aUri); chan.requestMethod = "DELETE"; - chan.asyncOpen(new PushServiceDelete(resolve, reject), null); + chan.asyncOpen2(new PushServiceDelete(resolve, reject)); }); }, /** * Unsubscribe the resource with a subscription uri aSubscriptionUri. * We can't do anything about it if it fails, so we don't listen for response. */ _unsubscribeResource: function(aSubscriptionUri) { @@ -540,20 +530,20 @@ this.PushServiceHttp2 = { var conn = {}; conn.channel = chan; var listener = new PushSubscriptionListener(this, aSubscriptionUri); conn.listener = listener; chan.notificationCallbacks = listener; try { - chan.asyncOpen(listener, chan); + chan.asyncOpen2(listener); } catch (e) { console.error("listenForMsgs: Error connecting to push server.", - "asyncOpen failed", e); + "asyncOpen2 failed", e); conn.listener.disconnect(); chan.cancel(Cr.NS_ERROR_ABORT); this._retryAfterBackoff(aSubscriptionUri, -1); return; } this._conns[aSubscriptionUri].lastStartListening = Date.now(); this._conns[aSubscriptionUri].channel = conn.channel;
--- a/dom/security/test/mixedcontentblocker/file_frameNavigation_grandchild.html +++ b/dom/security/test/mixedcontentblocker/file_frameNavigation_grandchild.html @@ -18,17 +18,20 @@ https://bugzilla.mozilla.org/show_bug.cg var counter = 0; var child = document.getElementById("child"); function navigationStatus(child) { // When the page is navigating, it goes through about:blank and we will get a permission denied for loc. // Catch that specific exception and return try { - var loc = child.contentDocument.location; + var loc; + if (child.contentDocument) { + loc = child.contentDocument.location; + } } catch(e) { if (e.message && e.message.indexOf("Permission denied to access property") == -1) { // We received an exception we didn't expect. throw e; } counter++; return; }
--- a/dom/storage/DOMStorageDBThread.cpp +++ b/dom/storage/DOMStorageDBThread.cpp @@ -149,16 +149,17 @@ DOMStorageDBThread::Shutdown() mThread = nullptr; return mStatus; } void DOMStorageDBThread::SyncPreload(DOMStorageCacheBridge* aCache, bool aForceSync) { + PROFILER_LABEL_FUNC(js::ProfileEntry::Category::STORAGE); if (!aForceSync && aCache->LoadedCount()) { // Preload already started for this cache, just wait for it to finish. // LoadWait will exit after LoadDone on the cache has been called. SetHigherPriority(); aCache->LoadWait(); SetDefaultPriority(); return; } @@ -252,17 +253,17 @@ DOMStorageDBThread::InsertDBOp(DOMStorag // We need to do this to prevent load of the DB data before the scope has // actually been cleared from the database. Preloads are processed // immediately before update and clear operations on the database that // are flushed periodically in batches. MonitorAutoUnlock unlock(mThreadObserver->GetMonitor()); aOperation->Finalize(NS_OK); return NS_OK; } - // NO BREAK + MOZ_FALLTHROUGH; case DBOperation::opGetUsage: if (aOperation->Type() == DBOperation::opPreloadUrgent) { SetHigherPriority(); // Dropped back after urgent preload execution mPreloads.InsertElementAt(0, aOperation); } else { mPreloads.AppendElement(aOperation); }
--- a/dom/tests/mochitest/ajax/offline/offlineTests.js +++ b/dom/tests/mochitest/ajax/offline/offlineTests.js @@ -319,17 +319,17 @@ loadContextInfo: function() }, getActiveCache: function(overload) { // Note that this is the current active cache in the cache stack, not the // one associated with this window. var serv = Cc["@mozilla.org/network/application-cache-service;1"] .getService(Ci.nsIApplicationCacheService); - var groupID = serv.buildGroupID(this.manifestURL(overload), this.loadContextInfo()); + var groupID = serv.buildGroupIDForInfo(this.manifestURL(overload), this.loadContextInfo()); return serv.getActiveCache(groupID); }, getActiveStorage: function() { var cache = this.getActiveCache(); if (!cache) { return null;
--- a/dom/tests/mochitest/ajax/offline/test_updateCheck.html +++ b/dom/tests/mochitest/ajax/offline/test_updateCheck.html @@ -29,31 +29,31 @@ var manifestURI = Cc["@mozilla.org/netwo var updateService = Cc['@mozilla.org/offlinecacheupdate-service;1'] .getService(Ci.nsIOfflineCacheUpdateService); var systemPrincipal = SpecialPowers.Services.scriptSecurityManager.getSystemPrincipal(); function manifestCached() { // Run first check for an update - updateService.checkForUpdate(manifestURI, systemPrincipal, 0, false, { + updateService.checkForUpdate(manifestURI, systemPrincipal, { observe: function(subject, topic, data) { OfflineTest.is(topic, "offline-cache-update-unavailable", "No update avail"); // Change the manifest content OfflineTest.setSJSState(manifest, "second"); // Check we now get notification on update ready - updateService.checkForUpdate(manifestURI, systemPrincipal, 0, false, { + updateService.checkForUpdate(manifestURI, systemPrincipal, { observe: function(subject, topic, data) { OfflineTest.is(topic, "offline-cache-update-available", "Update avail (1)"); // Do the check again. We must get the same result. Double check is here // to make sure we don't overwrite any data in the cache by the check it self. - updateService.checkForUpdate(manifestURI, systemPrincipal, 0, false, { + updateService.checkForUpdate(manifestURI, systemPrincipal, { observe: function(subject, topic, data) { OfflineTest.is(topic, "offline-cache-update-available", "Update avail (2)"); // Update the manifest, invokes manifestUpdated() applicationCache.onupdateready = OfflineTest.priv(manifestUpdated); applicationCache.update(); } }); @@ -61,17 +61,17 @@ function manifestCached() }); } }); } function manifestUpdated() { // Check for an update after manifest has been updated - updateService.checkForUpdate(manifestURI, systemPrincipal, 0, false, { + updateService.checkForUpdate(manifestURI, systemPrincipal, { observe: function(subject, topic, data) { OfflineTest.is(topic, "offline-cache-update-unavailable", "No update avail (2)"); OfflineTest.teardownAndFinish(); } }); }
--- a/dom/tests/mochitest/general/mochitest.ini +++ b/dom/tests/mochitest/general/mochitest.ini @@ -122,8 +122,9 @@ skip-if = buildapp == 'b2g' # Bug 118442 skip-if = buildapp == 'b2g' # Bug 1184427 - no SSL certs on b2g [test_storagePermissionsReject.html] skip-if = buildapp == 'b2g' # Bug 1184427 - no SSL certs on b2g [test_storagePermissionsLimitForeign.html] skip-if = buildapp == 'b2g' # Bug 1184427 - no SSL certs on b2g [test_selectevents.html] skip-if = buildapp == 'b2g' || buildapp == 'mulet' || toolkit == 'android' # Mouse doesn't select in the same way # Disabled on Android, see bug 1230232 +[test_WebKitCSSMatrix.html]
new file mode 100644 --- /dev/null +++ b/dom/tests/mochitest/general/test_WebKitCSSMatrix.html @@ -0,0 +1,306 @@ +<!DOCTYPE html> +<meta charset=utf-8> +<title>Test for WebKitCSSMatrix</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<div id="log"></div> +<script> +function RoughCompareMatrix(dm1, dm2) +{ + var m1 = dm1.toFloat32Array(); + var m2 = dm2.toFloat32Array(); + + if (m1.length != m2.length) { + return false; + } + + const tolerance = 1 / 65535; + for (var x = 0; x < m1.length; x++) { + if (Math.abs(m1[x] - m2[x]) > tolerance) { + return false; + } + } + + return true; +} + +function CompareMatrix(dm1, dm2) +{ + var m1 = dm1.toFloat32Array(); + var m2 = dm2.toFloat32Array(); + + if (m1.length != m2.length) { + return false; + } + + for (var x = 0; x < m1.length; x++) { + if (m1[x] != m2[x]) { + return false; + } + } + + return true; +} + +test(function() { + var m = new WebKitCSSMatrix(); + + assert_equals(m.m11, 1, "m11 should be 1"); + assert_equals(m.m22, 1, "m22 should be 1"); + assert_equals(m.m33, 1, "m33 should be 1"); + assert_equals(m.m44, 1, "m44 should be 1"); + assert_equals(m.m12, 0, "m12 should be 0"); + assert_equals(m.m13, 0, "m13 should be 0"); + assert_equals(m.m14, 0, "m14 should be 0"); + assert_equals(m.m21, 0, "m21 should be 0"); + assert_equals(m.m23, 0, "m23 should be 0"); + assert_equals(m.m24, 0, "m24 should be 0"); + assert_equals(m.m31, 0, "m31 should be 0"); + assert_equals(m.m32, 0, "m32 should be 0"); + assert_equals(m.m34, 0, "m34 should be 0"); + assert_equals(m.m41, 0, "m41 should be 0"); + assert_equals(m.m42, 0, "m42 should be 0"); + assert_equals(m.m43, 0, "m43 should be 0"); +}, "Test constructor with no arguments."); + +test(function() { + var m = new WebKitCSSMatrix("matrix(1,2,3,4,5,6)"); + + assert_equals(m.m11, 1, "m11 should be 1"); + assert_equals(m.m12, 2, "m12 should be 2"); + assert_equals(m.m21, 3, "m21 should be 3"); + assert_equals(m.m22, 4, "m22 should be 4"); + assert_equals(m.m41, 5, "m41 should be 5"); + assert_equals(m.m42, 6, "m42 should be 6"); +}, "Test constructor with transform list."); + +test(function() { + var m1 = new WebKitCSSMatrix("matrix(1,2,3,4,5,6)"); + var m2 = new WebKitCSSMatrix(m1); + + assert_true(RoughCompareMatrix(m1, m2), "Matrix should be equal."); +}, "Test constructor with other matrix."); + +test(function() { + var m = new WebKitCSSMatrix(); + var mr = m.setMatrixValue("matrix(1,2,3,4,5,6)"); + + assert_equals(m.m11, 1, "m11 should be 1"); + assert_equals(m.m12, 2, "m12 should be 2"); + assert_equals(m.m21, 3, "m21 should be 3"); + assert_equals(m.m22, 4, "m22 should be 4"); + assert_equals(m.m41, 5, "m41 should be 5"); + assert_equals(m.m42, 6, "m42 should be 6"); + + assert_equals(m, mr, "Return value of setMatrixValue should be the same matrix."); +}, "Test setMatrixValue."); + +test(function() { + var m1 = new WebKitCSSMatrix("matrix(1,2,3,4,5,6)"); + var m2 = new DOMMatrix("matrix(1,2,3,4,5,6)"); + var m3 = new WebKitCSSMatrix("matrix(6,5,4,3,2,1)"); + var m4 = new WebKitCSSMatrix("matrix(1,2,3,4,5,6)"); + + var m1r = m1.multiply(m3); + var m2r = m2.multiply(m3); + + assert_true(RoughCompareMatrix(m1r, m2r), "multiply should return the same result as DOMMatrixReadOnly."); + assert_true(CompareMatrix(m1, m4), "Multiply should not mutate original matrix."); +}, "Test multiply."); + +test(function() { + var m1 = new WebKitCSSMatrix("matrix(1,2,3,4,5,6)"); + var m2 = new DOMMatrix("matrix(1,2,3,4,5,6)"); + var m3 = new WebKitCSSMatrix("matrix(1,2,3,4,5,6)"); + + var m1r = m1.inverse(); + var m2r = m2.inverse(); + + assert_true(RoughCompareMatrix(m1r, m2r), "inverse should return the same result as DOMMatrixReadOnly."); + assert_true(CompareMatrix(m1, m3), "inverse should not mutate original matrix."); +}, "Test inverse."); + +test(function() { + var m1 = new WebKitCSSMatrix("matrix(1,2,3,4,5,6)"); + var m2 = new DOMMatrix("matrix(1,2,3,4,5,6)"); + var m3 = new WebKitCSSMatrix("matrix(1,2,3,4,5,6)"); + + var m1r = m1.translate(2, 3, 4); + var m2r = m2.translate(2, 3, 4); + + assert_true(RoughCompareMatrix(m1r, m2r), "translate should return the same result as DOMMatrixReadOnly."); + assert_true(CompareMatrix(m1, m3), "translate should not mutate original matrix."); +}, "Test inverse."); + +test(function() { + var m1 = new WebKitCSSMatrix("matrix(1,2,3,4,5,6)"); + var m2 = new DOMMatrix("matrix(1,2,3,4,5,6)"); + var m3 = new WebKitCSSMatrix("matrix(1,2,3,4,5,6)"); + + var m1r = m1.scale(2); + var m2r = m2.scaleNonUniform(2, 2, 1); + + assert_true(RoughCompareMatrix(m1r, m2r), "scale should return the same result as DOMMatrixReadOnly."); + assert_true(CompareMatrix(m1, m3), "scale should not mutate original matrix."); +}, "Test scale with 1 argument."); + +test(function() { + var m1 = new WebKitCSSMatrix("matrix(1,2,3,4,5,6)"); + var m2 = new DOMMatrix("matrix(1,2,3,4,5,6)"); + var m3 = new WebKitCSSMatrix("matrix(1,2,3,4,5,6)"); + + var m1r = m1.scale(2, 3); + var m2r = m2.scaleNonUniform(2, 3, 1); + + assert_true(RoughCompareMatrix(m1r, m2r), "scale should return the same result as DOMMatrixReadOnly."); + assert_true(CompareMatrix(m1, m3), "scale should not mutate original matrix."); +}, "Test scale with 2 arguments."); + +test(function() { + var m1 = new WebKitCSSMatrix("matrix(1,2,3,4,5,6)"); + var m2 = new DOMMatrix("matrix(1,2,3,4,5,6)"); + var m3 = new WebKitCSSMatrix("matrix(1,2,3,4,5,6)"); + + var m1r = m1.scale(2, 3, 4); + var m2r = m2.scaleNonUniform(2, 3, 4); + + assert_true(RoughCompareMatrix(m1r, m2r), "scale should return the same result as DOMMatrixReadOnly."); + assert_true(CompareMatrix(m1, m3), "scale should not mutate original matrix."); +}, "Test scale with 3 arguments."); + +test(function() { + var m1 = new WebKitCSSMatrix("matrix(1,2,3,4,5,6)"); + var m2 = new DOMMatrix("matrix(1,2,3,4,5,6)"); + var m3 = new WebKitCSSMatrix("matrix(1,2,3,4,5,6)"); + + var m1r = m1.scale(undefined, 3, 4); + var m2r = m2.scaleNonUniform(1, 3, 4); + + assert_true(RoughCompareMatrix(m1r, m2r), "scale should return the same result as DOMMatrixReadOnly."); + assert_true(CompareMatrix(m1, m3), "scale should not mutate original matrix."); +}, "Test scale with undefined scaleX argument."); + +test(function() { + var m1 = new WebKitCSSMatrix("matrix(1,2,3,4,5,6)"); + var m2 = new DOMMatrix("matrix(1,2,3,4,5,6)"); + var m3 = new WebKitCSSMatrix("matrix(1,2,3,4,5,6)"); + + var m1r = m1.scale(2, undefined, 4); + var m2r = m2.scaleNonUniform(2, 2, 4); + + assert_true(RoughCompareMatrix(m1r, m2r), "scale should return the same result as DOMMatrixReadOnly."); + assert_true(CompareMatrix(m1, m3), "scale should not mutate original matrix."); +}, "Test scale with undefined scaleY argument."); + +test(function() { + var m1 = new WebKitCSSMatrix("matrix(1,2,3,4,5,6)"); + var m2 = new DOMMatrix("matrix(1,2,3,4,5,6)"); + var m3 = new WebKitCSSMatrix("matrix(1,2,3,4,5,6)"); + + var m1r = m1.scale(2, 3, undefined); + var m2r = m2.scaleNonUniform(2, 3, 1); + + assert_true(RoughCompareMatrix(m1r, m2r), "scale should return the same result as DOMMatrixReadOnly."); + assert_true(CompareMatrix(m1, m3), "scale should not mutate original matrix."); +}, "Test scale with undefined scaleZ argument."); + +test(function() { + var m1 = new WebKitCSSMatrix("matrix(1,2,3,4,5,6)"); + var m2 = new DOMMatrix("matrix(1,2,3,4,5,6)"); + var m3 = new WebKitCSSMatrix("matrix(1,2,3,4,5,6)"); + + var m1r = m1.rotate(2); + var m2r = m2.rotateAxisAngle(0, 0, 1, 2); // Rotate around unit vector on z-axis. + + assert_true(RoughCompareMatrix(m1r, m2r)); + assert_true(CompareMatrix(m1, m3), "rotate should not mutate original matrix."); +}, "Test rotate with 1 argument."); + +test(function() { + var m1 = new WebKitCSSMatrix("matrix(1,2,3,4,5,6)"); + var m2 = new DOMMatrix("matrix(1,2,3,4,5,6)"); + var m3 = new WebKitCSSMatrix("matrix(1,2,3,4,5,6)"); + + var m1r = m1.rotate(2, 3); + var m2r = m2.rotateAxisAngle(0, 1, 0, 3); // Rotate around unit vector on x-axis. + m2r = m2r.rotateAxisAngle(1, 0, 0, 2); // Rotate around unit vector on y-axis. + + assert_true(RoughCompareMatrix(m1r, m2r)); + assert_true(CompareMatrix(m1, m3), "rotate should not mutate original matrix."); +}, "Test rotate with 2 arguments."); + +test(function() { + var m1 = new WebKitCSSMatrix("matrix(1,2,3,4,5,6)"); + var m2 = new DOMMatrix("matrix(1,2,3,4,5,6)"); + var m3 = new WebKitCSSMatrix("matrix(1,2,3,4,5,6)"); + + var m1r = m1.rotate(2, 3, 4); + var m2r = m2.rotateAxisAngle(0, 0, 1, 4); // Rotate around unit vector on z-axis. + m2r = m2r.rotateAxisAngle(0, 1, 0, 3); // Rotate around unit vector on y-axis. + m2r = m2r.rotateAxisAngle(1, 0, 0, 2); // Rotate around unit vector on x-axis. + + assert_true(RoughCompareMatrix(m1r, m2r)); + assert_true(CompareMatrix(m1, m3), "rotate should not mutate original matrix."); +}, "Test rotate with 3 arguments."); + +test(function() { + var m1 = new WebKitCSSMatrix("matrix(1,2,3,4,5,6)"); + var m2 = new DOMMatrix("matrix(1,2,3,4,5,6)"); + var m3 = new WebKitCSSMatrix("matrix(1,2,3,4,5,6)"); + + var m1r = m1.rotate(2, undefined, undefined); + var m2r = m2.rotateAxisAngle(0, 0, 1, 2); // Rotate around unit vector on z-axis. + + assert_true(RoughCompareMatrix(m1r, m2r)); + assert_true(CompareMatrix(m1, m3), "rotate should not mutate original matrix."); +}, "Test rotate with rotY and rotZ as undefined."); + +test(function() { + var m1 = new WebKitCSSMatrix("matrix(1,2,3,4,5,6)"); + var m2 = new DOMMatrix("matrix(1,2,3,4,5,6)"); + var m3 = new WebKitCSSMatrix("matrix(1,2,3,4,5,6)"); + + var m1r = m1.rotate(undefined, 3, 4); + var m2r = m2.rotateAxisAngle(0, 0, 1, 4); // Rotate around unit vector on z-axis. + m2r = m2r.rotateAxisAngle(0, 1, 0, 3); // Rotate around unit vector on y-axis. + + assert_true(RoughCompareMatrix(m1r, m2r)); + assert_true(CompareMatrix(m1, m3), "rotate should not mutate original matrix."); +}, "Test rotate with rotX as undefined."); + +test(function() { + var m1 = new WebKitCSSMatrix("matrix(1,2,3,4,5,6)"); + var m2 = new DOMMatrix("matrix(1,2,3,4,5,6)"); + var m3 = new WebKitCSSMatrix("matrix(1,2,3,4,5,6)"); + + var m1r = m1.rotateAxisAngle(2, 3, 4, 5); + var m2r = m2.rotateAxisAngle(2, 3, 4, 5); + + assert_true(RoughCompareMatrix(m1r, m2r), "rotateAxisAngle should return the same result as DOMMatrixReadOnly."); + assert_true(CompareMatrix(m1, m3), "rotateAxisAngle should not mutate original matrix."); +}, "Test rotateAxisAngle."); + +test(function() { + var m1 = new WebKitCSSMatrix("matrix(1,2,3,4,5,6)"); + var m2 = new DOMMatrix("matrix(1,2,3,4,5,6)"); + var m3 = new WebKitCSSMatrix("matrix(1,2,3,4,5,6)"); + + var m1r = m1.skewX(2); + var m2r = m2.skewX(2); + + assert_true(RoughCompareMatrix(m1r, m2r), "skewX should return the same result as DOMMatrixReadOnly."); + assert_true(CompareMatrix(m1, m3), "skewX should not mutate original matrix."); +}, "Test skewX."); + +test(function() { + var m1 = new WebKitCSSMatrix("matrix(1,2,3,4,5,6)"); + var m2 = new DOMMatrix("matrix(1,2,3,4,5,6)"); + var m3 = new WebKitCSSMatrix("matrix(1,2,3,4,5,6)"); + + var m1r = m1.skewY(2); + var m2r = m2.skewY(2); + + assert_true(RoughCompareMatrix(m1r, m2r), "skewY should return the same result as DOMMatrixReadOnly."); + assert_true(CompareMatrix(m1, m3), "skewY should not mutate original matrix."); +}, "Test skewY."); +</script>
--- a/dom/tests/mochitest/general/test_interfaces.html +++ b/dom/tests/mochitest/general/test_interfaces.html @@ -1437,16 +1437,18 @@ var interfaceNamesInGlobalScope = "WebGLTexture", // IMPORTANT: Do not change this list without review from a DOM peer! {name: "WebGLTransformFeedback", nightly: true}, // IMPORTANT: Do not change this list without review from a DOM peer! "WebGLUniformLocation", // IMPORTANT: Do not change this list without review from a DOM peer! {name: "WebGLVertexArrayObject", nightly: true}, // IMPORTANT: Do not change this list without review from a DOM peer! + "WebKitCSSMatrix", +// IMPORTANT: Do not change this list without review from a DOM peer! "WebSocket", // IMPORTANT: Do not change this list without review from a DOM peer! "WheelEvent", // IMPORTANT: Do not change this list without review from a DOM peer! "Window", // IMPORTANT: Do not change this list without review from a DOM peer! "Worker", // IMPORTANT: Do not change this list without review from a DOM peer!
--- a/dom/webidl/FormData.webidl +++ b/dom/webidl/FormData.webidl @@ -2,17 +2,17 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * The origin of this IDL file is * http://xhr.spec.whatwg.org */ -typedef (File or USVString) FormDataEntryValue; +typedef (Blob or USVString) FormDataEntryValue; [Constructor(optional HTMLFormElement form), Exposed=(Window,Worker)] interface FormData { [Throws] void append(USVString name, Blob value, optional USVString filename); [Throws] void append(USVString name, USVString value);
new file mode 100644 --- /dev/null +++ b/dom/webidl/WebKitCSSMatrix.webidl @@ -0,0 +1,38 @@ +/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + * + * The origin of this IDL file is + * https://compat.spec.whatwg.org/#webkitcssmatrix-interface + */ + +[Constructor, + Constructor(DOMString transformList), + Constructor(WebKitCSSMatrix other), + Exposed=Window, + Func="mozilla::dom::WebKitCSSMatrix::FeatureEnabled"] +interface WebKitCSSMatrix : DOMMatrix { + // Mutable transform methods + [Throws] + WebKitCSSMatrix setMatrixValue(DOMString transformList); + + // Immutable transform methods + WebKitCSSMatrix multiply(WebKitCSSMatrix other); + WebKitCSSMatrix inverse(); + WebKitCSSMatrix translate(unrestricted double tx, + unrestricted double ty, + unrestricted double tz); + WebKitCSSMatrix scale(optional unrestricted double scaleX = 1, + optional unrestricted double scaleY, + optional unrestricted double scaleZ = 1); + WebKitCSSMatrix rotate(optional unrestricted double rotX = 0, + optional unrestricted double rotY, + optional unrestricted double rotZ); + WebKitCSSMatrix rotateAxisAngle(unrestricted double x, + unrestricted double y, + unrestricted double z, + unrestricted double angle); + WebKitCSSMatrix skewX(unrestricted double sx); + WebKitCSSMatrix skewY(unrestricted double sy); +};
--- a/dom/webidl/moz.build +++ b/dom/webidl/moz.build @@ -585,16 +585,17 @@ WEBIDL_FILES = [ 'VideoTrackList.webidl', 'VRDevice.webidl', 'VTTCue.webidl', 'VTTRegion.webidl', 'WaveShaperNode.webidl', 'WebComponents.webidl', 'WebGL2RenderingContext.webidl', 'WebGLRenderingContext.webidl', + 'WebKitCSSMatrix.webidl', 'WebSocket.webidl', 'WheelEvent.webidl', 'WifiOptions.webidl', 'WindowRoot.webidl', 'Worker.webidl', 'WorkerDebuggerGlobalScope.webidl', 'WorkerGlobalScope.webidl', 'WorkerLocation.webidl',
--- a/dom/workers/ScriptLoader.cpp +++ b/dom/workers/ScriptLoader.cpp @@ -1985,17 +1985,17 @@ void ReportLoadError(JSContext* aCx, nsr case NS_ERROR_FILE_NOT_FOUND: case NS_ERROR_NOT_AVAILABLE: Throw(aCx, NS_ERROR_DOM_NETWORK_ERR); break; case NS_ERROR_MALFORMED_URI: aLoadResult = NS_ERROR_DOM_SYNTAX_ERR; - // fall through + MOZ_FALLTHROUGH; case NS_ERROR_DOM_SECURITY_ERR: case NS_ERROR_DOM_SYNTAX_ERR: Throw(aCx, aLoadResult); break; default: JS_ReportError(aCx, "Failed to load script (nsresult = 0x%x)", aLoadResult); }
new file mode 100644 --- /dev/null +++ b/dom/workers/test/xpcshell/data/worker_fileReader.js @@ -0,0 +1,8 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + + +self.onmessage = function(msg) { + var fr = new FileReader(); + self.postMessage("OK"); +};
new file mode 100644 --- /dev/null +++ b/dom/workers/test/xpcshell/test_fileReader.js @@ -0,0 +1,40 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +Components.utils.import("resource://gre/modules/Promise.jsm"); + +// Worker must be loaded from a chrome:// uri, not a file:// +// uri, so we first need to load it. +var WORKER_SOURCE_URI = "chrome://workers/content/worker_fileReader.js"; +do_load_manifest("data/chrome.manifest"); + +function run_test() { + run_next_test(); +} + +function talk_with_worker(worker) { + let deferred = Promise.defer(); + worker.onmessage = function(event) { + let success = true; + if (event.data == "OK") { + deferred.resolve(); + } else { + success = false; + deferred.reject(event); + } + do_check_true(success); + worker.terminate(); + }; + worker.onerror = function(event) { + let error = new Error(event.message, event.filename, event.lineno); + worker.terminate(); + deferred.reject(error); + }; + worker.postMessage("START"); + return deferred.promise; +} + + +add_task(function test_chrome_worker() { + return talk_with_worker(new ChromeWorker(WORKER_SOURCE_URI)); +});
--- a/dom/workers/test/xpcshell/xpcshell.ini +++ b/dom/workers/test/xpcshell/xpcshell.ini @@ -1,9 +1,11 @@ [DEFAULT] head = tail = skip-if = toolkit == 'android' || toolkit == 'gonk' support-files = data/worker.js + data/worker_fileReader.js data/chrome.manifest [test_workers.js] +[test_fileReader.js]
--- a/gfx/2d/2D.h +++ b/gfx/2d/2D.h @@ -1234,16 +1234,18 @@ public: * within 8k limit. The 8k value is chosen a bit randomly. */ static bool ReasonableSurfaceSize(const IntSize &aSize); static bool AllowedSurfaceSize(const IntSize &aSize); static already_AddRefed<DrawTarget> CreateDrawTargetForCairoSurface(cairo_surface_t* aSurface, const IntSize& aSize, SurfaceFormat* aFormat = nullptr); + static already_AddRefed<SourceSurface> CreateSourceSurfaceForCairoSurface(cairo_surface_t* aSurface, const IntSize& aSize, SurfaceFormat aFormat); + static already_AddRefed<DrawTarget> CreateDrawTarget(BackendType aBackend, const IntSize &aSize, SurfaceFormat aFormat); static already_AddRefed<DrawTarget> CreateRecordingDrawTarget(DrawEventRecorder *aRecorder, DrawTarget *aDT); static already_AddRefed<DrawTarget> CreateDrawTargetForData(BackendType aBackend, unsigned char* aData, const IntSize &aSize, int32_t aStride, SurfaceFormat aFormat);
--- a/gfx/2d/DrawTargetCairo.cpp +++ b/gfx/2d/DrawTargetCairo.cpp @@ -1725,26 +1725,16 @@ DrawTargetCairo::OptimizeSourceSurface(S #endif return surface.forget(); } already_AddRefed<SourceSurface> DrawTargetCairo::CreateSourceSurfaceFromNativeSurface(const NativeSurface &aSurface) const { - if (aSurface.mType == NativeSurfaceType::CAIRO_CONTEXT) { - if (aSurface.mSize.width <= 0 || - aSurface.mSize.height <= 0) { - gfxWarning() << "Can't create a SourceSurface without a valid size"; - return nullptr; - } - cairo_surface_t* surf = static_cast<cairo_surface_t*>(aSurface.mSurface); - return MakeAndAddRef<SourceSurfaceCairo>(surf, aSurface.mSize, aSurface.mFormat); - } - return nullptr; } already_AddRefed<DrawTarget> DrawTargetCairo::CreateSimilarDrawTarget(const IntSize &aSize, SurfaceFormat aFormat) const { if (cairo_surface_status(mSurface)) { RefPtr<DrawTargetCairo> target = new DrawTargetCairo();
--- a/gfx/2d/DrawTargetSkia.cpp +++ b/gfx/2d/DrawTargetSkia.cpp @@ -744,34 +744,26 @@ DrawTargetSkia::OptimizeSourceSurface(So dataSurf->GetFormat()); dataSurf->Unmap(); return result.forget(); } already_AddRefed<SourceSurface> DrawTargetSkia::CreateSourceSurfaceFromNativeSurface(const NativeSurface &aSurface) const { - if (aSurface.mType == NativeSurfaceType::CAIRO_CONTEXT) { - if (aSurface.mSize.width <= 0 || - aSurface.mSize.height <= 0) { - gfxWarning() << "Can't create a SourceSurface without a valid size"; - return nullptr; - } - cairo_surface_t* surf = static_cast<cairo_surface_t*>(aSurface.mSurface); - return MakeAndAddRef<SourceSurfaceCairo>(surf, aSurface.mSize, aSurface.mFormat); #if USE_SKIA_GPU - } else if (aSurface.mType == NativeSurfaceType::OPENGL_TEXTURE && UsingSkiaGPU()) { + if (aSurface.mType == NativeSurfaceType::OPENGL_TEXTURE && UsingSkiaGPU()) { RefPtr<SourceSurfaceSkia> newSurf = new SourceSurfaceSkia(); unsigned int texture = (unsigned int)((uintptr_t)aSurface.mSurface); if (newSurf->InitFromTexture((DrawTargetSkia*)this, texture, aSurface.mSize, aSurface.mFormat)) { return newSurf.forget(); } return nullptr; + } #endif - } return nullptr; } void DrawTargetSkia::CopySurface(SourceSurface *aSurface, const IntRect& aSourceRect, const IntPoint &aDestination)
--- a/gfx/2d/Factory.cpp +++ b/gfx/2d/Factory.cpp @@ -3,16 +3,17 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "2D.h" #ifdef USE_CAIRO #include "DrawTargetCairo.h" #include "ScaledFontCairo.h" +#include "SourceSurfaceCairo.h" #endif #ifdef USE_SKIA #include "DrawTargetSkia.h" #include "ScaledFontBase.h" #ifdef MOZ_ENABLE_FREETYPE #define USE_SKIA_FREETYPE #include "ScaledFontCairo.h" @@ -845,16 +846,31 @@ Factory::CreateDrawTargetForCairoSurface if (mRecorder && retVal) { return MakeAndAddRef<DrawTargetRecording>(mRecorder, retVal, true); } #endif return retVal.forget(); } +already_AddRefed<SourceSurface> +Factory::CreateSourceSurfaceForCairoSurface(cairo_surface_t* aSurface, const IntSize& aSize, SurfaceFormat aFormat) +{ + if (aSize.width <= 0 || aSize.height <= 0) { + gfxWarning() << "Can't create a SourceSurface without a valid size"; + return nullptr; + } + +#ifdef USE_CAIRO + return MakeAndAddRef<SourceSurfaceCairo>(aSurface, aSize, aFormat); +#else + return nullptr; +#endif +} + #ifdef XP_DARWIN already_AddRefed<DrawTarget> Factory::CreateDrawTargetForCairoCGContext(CGContextRef cg, const IntSize& aSize) { RefPtr<DrawTarget> retVal; RefPtr<DrawTargetCG> newTarget = new DrawTargetCG();
deleted file mode 100644 --- a/gfx/2d/moz-d2d1-1.h +++ /dev/null @@ -1,367 +0,0 @@ -//--------------------------------------------------------------------------- -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// This file is automatically generated. Please do not edit it directly. -// -// File name: D2D1_1.h -//--------------------------------------------------------------------------- -#pragma once - -#ifndef _D2D1_1_H_ -#ifndef _D2D1_H_ -#include <d2d1.h> -#endif // #ifndef _D2D1_H_ - -//+----------------------------------------------------------------------------- -// -// Flag: -// D2D1_LAYER_OPTIONS1 -// -// Synopsis: -// Specifies how the layer contents should be prepared. -// -//------------------------------------------------------------------------------ -typedef enum D2D1_LAYER_OPTIONS1 -{ - D2D1_LAYER_OPTIONS1_NONE = 0, - D2D1_LAYER_OPTIONS1_INITIALIZE_FROM_BACKGROUND = 1, - D2D1_LAYER_OPTIONS1_IGNORE_ALPHA = 2, - D2D1_LAYER_OPTIONS1_FORCE_DWORD = 0xffffffff - -} D2D1_LAYER_OPTIONS1; - -//+----------------------------------------------------------------------------- -// -// Struct: -// D2D1_LAYER_PARAMETERS1 -// -// Synopsis: -// All parameters related to pushing a layer. -// -//------------------------------------------------------------------------------ -typedef struct D2D1_LAYER_PARAMETERS1 -{ - D2D1_RECT_F contentBounds; - ID2D1Geometry *geometricMask; - D2D1_ANTIALIAS_MODE maskAntialiasMode; - D2D1_MATRIX_3X2_F maskTransform; - FLOAT opacity; - ID2D1Brush *opacityBrush; - D2D1_LAYER_OPTIONS1 layerOptions; - -} D2D1_LAYER_PARAMETERS1; - -DEFINE_ENUM_FLAG_OPERATORS(D2D1_LAYER_OPTIONS1); - -#ifndef DX_DECLARE_INTERFACE -#define DX_DECLARE_INTERFACE(x) DECLSPEC_UUID(x) DECLSPEC_NOVTABLE -#endif - -namespace D2D1 -{ - D2D1FORCEINLINE - D2D1_LAYER_PARAMETERS1 - LayerParameters1( - CONST D2D1_RECT_F &contentBounds = D2D1::InfiniteRect(), - ID2D1Geometry *geometricMask = NULL, - D2D1_ANTIALIAS_MODE maskAntialiasMode = D2D1_ANTIALIAS_MODE_PER_PRIMITIVE, - D2D1_MATRIX_3X2_F maskTransform = D2D1::IdentityMatrix(), - FLOAT opacity = 1.0, - ID2D1Brush *opacityBrush = NULL, - D2D1_LAYER_OPTIONS1 layerOptions = D2D1_LAYER_OPTIONS1_NONE - ) - { - D2D1_LAYER_PARAMETERS1 layerParameters = { 0 }; - - layerParameters.contentBounds = contentBounds; - layerParameters.geometricMask = geometricMask; - layerParameters.maskAntialiasMode = maskAntialiasMode; - layerParameters.maskTransform = maskTransform; - layerParameters.opacity = opacity; - layerParameters.opacityBrush = opacityBrush; - layerParameters.layerOptions = layerOptions; - - return layerParameters; - } -} - -DEFINE_GUID(IID_ID2D1DeviceContext, 0xe8f7fe7a, 0x191c, 0x466d, 0xad, 0x95, 0x97, 0x56, 0x78, 0xbd, 0xa9, 0x98); - -//+----------------------------------------------------------------------------- -// -// Interface: -// ID2D1DeviceContext -// -// Synopsis: -// The device context represents a set of state and a command buffer that is used -// to render to a target bitmap. -// -//------------------------------------------------------------------------------ -interface DX_DECLARE_INTERFACE("e8f7fe7a-191c-466d-ad95-975678bda998") ID2D1DeviceContext : public ID2D1RenderTarget -{ - - - // - // Creates a bitmap with extended bitmap properties, potentially from a block of - // memory. - // - STDMETHOD(CreateBitmap)() PURE; - - // - // Create a D2D bitmap by copying a WIC bitmap. - // - STDMETHOD(CreateBitmapFromWicBitmap)() PURE; - - // - // Creates a color context from a color space. If the space is Custom, the context - // is initialized from the profile/profileSize arguments. Otherwise the context is - // initialized with the profile bytes associated with the space and - // profile/profileSize are ignored. - // - STDMETHOD(CreateColorContext)() PURE; - - STDMETHOD(CreateColorContextFromFilename)() PURE; - - STDMETHOD(CreateColorContextFromWicColorContext)() PURE; - - - // - // Creates a bitmap from a DXGI surface with a set of extended properties. - // - STDMETHOD(CreateBitmapFromDxgiSurface)() PURE; - - - // - // Create a new effect, the effect must either be built in or previously registered - // through ID2D1Factory1::RegisterEffectFromStream or - // ID2D1Factory1::RegisterEffectFromString. - // - STDMETHOD(CreateEffect)() PURE; - - - // - // A gradient stop collection represents a set of stops in an ideal unit length. - // This is the source resource for a linear gradient and radial gradient brush. - // - STDMETHOD(CreateGradientStopCollection)() PURE; - - // - // Creates an image brush, the input image can be any type of image, including a - // bitmap, effect and a command list. - // - STDMETHOD(CreateImageBrush)() PURE; - - STDMETHOD(CreateBitmapBrush)() PURE; - - // - // Creates a new command list. - // - STDMETHOD(CreateCommandList)() PURE; - - - // - // Indicates whether the format is supported by D2D. - // - STDMETHOD_(BOOL, IsDxgiFormatSupported)() CONST PURE; - - - // - // Indicates whether the buffer precision is supported by D2D. - // - STDMETHOD_(BOOL, IsBufferPrecisionSupported)() CONST PURE; - - - // - // This retrieves the local-space bounds in DIPs of the current image using the - // device context DPI. - // - STDMETHOD(GetImageLocalBounds)() CONST PURE; - - - // - // This retrieves the world-space bounds in DIPs of the current image using the - // device context DPI. - // - STDMETHOD(GetImageWorldBounds)() CONST PURE; - - - // - // Retrieves the world-space bounds in DIPs of the glyph run using the device - // context DPI. - // - STDMETHOD(GetGlyphRunWorldBounds)() CONST PURE; - - - // - // Retrieves the device associated with this device context. - // - STDMETHOD_(void, GetDevice)() CONST PURE; - - - // - // Sets the target for this device context to point to the given image. The image - // can be a command list or a bitmap created with the D2D1_BITMAP_OPTIONS_TARGET - // flag. - // - STDMETHOD_(void, SetTarget)() PURE; - - - // - // Gets the target that this device context is currently pointing to. - // - STDMETHOD_(void, GetTarget)() CONST PURE; - - - // - // Sets tuning parameters for internal rendering inside the device context. - // - STDMETHOD_(void, SetRenderingControls)() PURE; - - - // - // This retrieves the rendering controls currently selected into the device - // context. - // - STDMETHOD_(void, GetRenderingControls)() CONST PURE; - - - // - // Changes the primitive blending mode for all of the rendering operations. - // - STDMETHOD_(void, SetPrimitiveBlend)() PURE; - - - // - // Returns the primitive blend currently selected into the device context. - // - STDMETHOD_(void, GetPrimitiveBlend)( - ) CONST PURE; - - - // - // Changes the units used for all of the rendering operations. - // - STDMETHOD_(void, SetUnitMode)() PURE; - - - // - // Returns the unit mode currently set on the device context. - // - STDMETHOD_(void, GetUnitMode)( - ) CONST PURE; - - - // - // Draws the glyph run with an extended description to describe the glyphs. - // - STDMETHOD_(void, DrawGlyphRun)() PURE; - - // - // Draw an image to the device context. The image represents either a concrete - // bitmap or the output of an effect graph. - // - STDMETHOD_(void, DrawImage)() PURE; - - - // - // Draw a metafile to the device context. - // - STDMETHOD_(void, DrawGdiMetafile)() PURE; - - STDMETHOD_(void, DrawBitmap)() PURE; - - - // - // Push a layer on the device context. - // - STDMETHOD_(void, PushLayer)( - _In_ CONST D2D1_LAYER_PARAMETERS1 *layerParameters, - _In_opt_ ID2D1Layer *layer - ) PURE; - - using ID2D1RenderTarget::PushLayer; - - - // - // This indicates that a portion of an effect's input is invalid. This method can - // be called many times. - // - STDMETHOD(InvalidateEffectInputRectangle)() PURE; - - - // - // Gets the number of invalid ouptut rectangles that have accumulated at the - // effect. - // - STDMETHOD(GetEffectInvalidRectangleCount)() PURE; - - - // - // Gets the invalid rectangles that are at the output of the effect. - // - STDMETHOD(GetEffectInvalidRectangles)() PURE; - - - // - // Gets the maximum region of each specified input which would be used during a - // subsequent rendering operation - // - STDMETHOD(GetEffectRequiredInputRectangles)() PURE; - - - // - // Fill using the alpha channel of the supplied opacity mask bitmap. The brush - // opacity will be modulated by the mask. The render target antialiasing mode must - // be set to aliased. - // - STDMETHOD_(void, FillOpacityMask)() PURE; - - - HRESULT CreateBitmap1() { return S_OK; } - HRESULT CreateBitmap2() { return S_OK; } - HRESULT CreateBitmap3() { return S_OK; } - HRESULT CreateBitmap4() { return S_OK; } - - HRESULT CreateImageBrush1() { return S_OK; } - HRESULT CreateImageBrush2() { return S_OK; } - - HRESULT CreateBitmapBrush1() { return S_OK; } - HRESULT CreateBitmapBrush2() { return S_OK; } - HRESULT CreateBitmapBrush3() { return S_OK; } - - // - // Draws the output of the effect as an image. - // - void DrawImage1() {} - void DrawImage2() {} - void DrawImage3() {} - void DrawImage4() {} - void DrawImage5() {} - void DrawImage6() {} - void DrawImage7() {} - - void - PushLayer( - CONST D2D1_LAYER_PARAMETERS1 &layerParameters, - _In_opt_ ID2D1Layer *layer - ) - { - PushLayer(&layerParameters, layer); - } - - void DrawGdiMetafile1() {} - - void DrawBitmap1() {} - void DrawBitmap2() {} - void DrawBitmap3() {} - - void FillOpacityMask1() {} - void FillOpacityMask2() {} - - // - // Sets tuning parameters for internal rendering inside the device context. - // - void SetRenderingControls1() {} -}; // interface ID2D1DeviceContext - -#endif // #ifndef _D2D1_1_H_
--- a/gfx/cairo/cairo/src/cairo-d2d-surface.cpp +++ b/gfx/cairo/cairo/src/cairo-d2d-surface.cpp @@ -41,18 +41,17 @@ #include "cairo-win32.h" #include "cairo-analysis-surface-private.h" #include "cairo-error-private.h" // Required for using placement new. #include <new> -// HACK WARNING - Workaround for Windows 8 since we don't have the windows 8 SDK. -#include "moz-d2d1-1.h" +#include "d2d1_1.h" #define CAIRO_INT_STATUS_SUCCESS (cairo_int_status_t)CAIRO_STATUS_SUCCESS struct Vertex { float position[2]; };
deleted file mode 100644 --- a/gfx/cairo/cairo/src/moz-d2d1-1.h +++ /dev/null @@ -1,367 +0,0 @@ -//--------------------------------------------------------------------------- -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// This file is automatically generated. Please do not edit it directly. -// -// File name: D2D1_1.h -//--------------------------------------------------------------------------- -#pragma once - -#ifndef _D2D1_1_H_ -#ifndef _D2D1_H_ -#include <d2d1.h> -#endif // #ifndef _D2D1_H_ - -//+----------------------------------------------------------------------------- -// -// Flag: -// D2D1_LAYER_OPTIONS1 -// -// Synopsis: -// Specifies how the layer contents should be prepared. -// -//------------------------------------------------------------------------------ -typedef enum D2D1_LAYER_OPTIONS1 -{ - D2D1_LAYER_OPTIONS1_NONE = 0, - D2D1_LAYER_OPTIONS1_INITIALIZE_FROM_BACKGROUND = 1, - D2D1_LAYER_OPTIONS1_IGNORE_ALPHA = 2, - D2D1_LAYER_OPTIONS1_FORCE_DWORD = 0xffffffff - -} D2D1_LAYER_OPTIONS1; - -//+----------------------------------------------------------------------------- -// -// Struct: -// D2D1_LAYER_PARAMETERS1 -// -// Synopsis: -// All parameters related to pushing a layer. -// -//------------------------------------------------------------------------------ -typedef struct D2D1_LAYER_PARAMETERS1 -{ - D2D1_RECT_F contentBounds; - ID2D1Geometry *geometricMask; - D2D1_ANTIALIAS_MODE maskAntialiasMode; - D2D1_MATRIX_3X2_F maskTransform; - FLOAT opacity; - ID2D1Brush *opacityBrush; - D2D1_LAYER_OPTIONS1 layerOptions; - -} D2D1_LAYER_PARAMETERS1; - -DEFINE_ENUM_FLAG_OPERATORS(D2D1_LAYER_OPTIONS1); - -#ifndef DX_DECLARE_INTERFACE -#define DX_DECLARE_INTERFACE(x) DECLSPEC_UUID(x) DECLSPEC_NOVTABLE -#endif - -namespace D2D1 -{ - D2D1FORCEINLINE - D2D1_LAYER_PARAMETERS1 - LayerParameters1( - CONST D2D1_RECT_F &contentBounds = D2D1::InfiniteRect(), - ID2D1Geometry *geometricMask = NULL, - D2D1_ANTIALIAS_MODE maskAntialiasMode = D2D1_ANTIALIAS_MODE_PER_PRIMITIVE, - D2D1_MATRIX_3X2_F maskTransform = D2D1::IdentityMatrix(), - FLOAT opacity = 1.0, - ID2D1Brush *opacityBrush = NULL, - D2D1_LAYER_OPTIONS1 layerOptions = D2D1_LAYER_OPTIONS1_NONE - ) - { - D2D1_LAYER_PARAMETERS1 layerParameters = {{ 0 }}; - - layerParameters.contentBounds = contentBounds; - layerParameters.geometricMask = geometricMask; - layerParameters.maskAntialiasMode = maskAntialiasMode; - layerParameters.maskTransform = maskTransform; - layerParameters.opacity = opacity; - layerParameters.opacityBrush = opacityBrush; - layerParameters.layerOptions = layerOptions; - - return layerParameters; - } -} - -DEFINE_GUID(IID_ID2D1DeviceContext, 0xe8f7fe7a, 0x191c, 0x466d, 0xad, 0x95, 0x97, 0x56, 0x78, 0xbd, 0xa9, 0x98); - -//+----------------------------------------------------------------------------- -// -// Interface: -// ID2D1DeviceContext -// -// Synopsis: -// The device context represents a set of state and a command buffer that is used -// to render to a target bitmap. -// -//------------------------------------------------------------------------------ -interface DX_DECLARE_INTERFACE("e8f7fe7a-191c-466d-ad95-975678bda998") ID2D1DeviceContext : public ID2D1RenderTarget -{ - - - // - // Creates a bitmap with extended bitmap properties, potentially from a block of - // memory. - // - STDMETHOD(CreateBitmap)() PURE; - - // - // Create a D2D bitmap by copying a WIC bitmap. - // - STDMETHOD(CreateBitmapFromWicBitmap)() PURE; - - // - // Creates a color context from a color space. If the space is Custom, the context - // is initialized from the profile/profileSize arguments. Otherwise the context is - // initialized with the profile bytes associated with the space and - // profile/profileSize are ignored. - // - STDMETHOD(CreateColorContext)() PURE; - - STDMETHOD(CreateColorContextFromFilename)() PURE; - - STDMETHOD(CreateColorContextFromWicColorContext)() PURE; - - - // - // Creates a bitmap from a DXGI surface with a set of extended properties. - // - STDMETHOD(CreateBitmapFromDxgiSurface)() PURE; - - - // - // Create a new effect, the effect must either be built in or previously registered - // through ID2D1Factory1::RegisterEffectFromStream or - // ID2D1Factory1::RegisterEffectFromString. - // - STDMETHOD(CreateEffect)() PURE; - - - // - // A gradient stop collection represents a set of stops in an ideal unit length. - // This is the source resource for a linear gradient and radial gradient brush. - // - STDMETHOD(CreateGradientStopCollection)() PURE; - - // - // Creates an image brush, the input image can be any type of image, including a - // bitmap, effect and a command list. - // - STDMETHOD(CreateImageBrush)() PURE; - - STDMETHOD(CreateBitmapBrush)() PURE; - - // - // Creates a new command list. - // - STDMETHOD(CreateCommandList)() PURE; - - - // - // Indicates whether the format is supported by D2D. - // - STDMETHOD_(BOOL, IsDxgiFormatSupported)() CONST PURE; - - - // - // Indicates whether the buffer precision is supported by D2D. - // - STDMETHOD_(BOOL, IsBufferPrecisionSupported)() CONST PURE; - - - // - // This retrieves the local-space bounds in DIPs of the current image using the - // device context DPI. - // - STDMETHOD(GetImageLocalBounds)() CONST PURE; - - - // - // This retrieves the world-space bounds in DIPs of the current image using the - // device context DPI. - // - STDMETHOD(GetImageWorldBounds)() CONST PURE; - - - // - // Retrieves the world-space bounds in DIPs of the glyph run using the device - // context DPI. - // - STDMETHOD(GetGlyphRunWorldBounds)() CONST PURE; - - - // - // Retrieves the device associated with this device context. - // - STDMETHOD_(void, GetDevice)() CONST PURE; - - - // - // Sets the target for this device context to point to the given image. The image - // can be a command list or a bitmap created with the D2D1_BITMAP_OPTIONS_TARGET - // flag. - // - STDMETHOD_(void, SetTarget)() PURE; - - - // - // Gets the target that this device context is currently pointing to. - // - STDMETHOD_(void, GetTarget)() CONST PURE; - - - // - // Sets tuning parameters for internal rendering inside the device context. - // - STDMETHOD_(void, SetRenderingControls)() PURE; - - - // - // This retrieves the rendering controls currently selected into the device - // context. - // - STDMETHOD_(void, GetRenderingControls)() CONST PURE; - - - // - // Changes the primitive blending mode for all of the rendering operations. - // - STDMETHOD_(void, SetPrimitiveBlend)() PURE; - - - // - // Returns the primitive blend currently selected into the device context. - // - STDMETHOD_(void, GetPrimitiveBlend)( - ) CONST PURE; - - - // - // Changes the units used for all of the rendering operations. - // - STDMETHOD_(void, SetUnitMode)() PURE; - - - // - // Returns the unit mode currently set on the device context. - // - STDMETHOD_(void, GetUnitMode)( - ) CONST PURE; - - - // - // Draws the glyph run with an extended description to describe the glyphs. - // - STDMETHOD_(void, DrawGlyphRun)() PURE; - - // - // Draw an image to the device context. The image represents either a concrete - // bitmap or the output of an effect graph. - // - STDMETHOD_(void, DrawImage)() PURE; - - - // - // Draw a metafile to the device context. - // - STDMETHOD_(void, DrawGdiMetafile)() PURE; - - STDMETHOD_(void, DrawBitmap)() PURE; - - - // - // Push a layer on the device context. - // - STDMETHOD_(void, PushLayer)( - _In_ CONST D2D1_LAYER_PARAMETERS1 *layerParameters, - _In_opt_ ID2D1Layer *layer - ) PURE; - - using ID2D1RenderTarget::PushLayer; - - - // - // This indicates that a portion of an effect's input is invalid. This method can - // be called many times. - // - STDMETHOD(InvalidateEffectInputRectangle)() PURE; - - - // - // Gets the number of invalid ouptut rectangles that have accumulated at the - // effect. - // - STDMETHOD(GetEffectInvalidRectangleCount)() PURE; - - - // - // Gets the invalid rectangles that are at the output of the effect. - // - STDMETHOD(GetEffectInvalidRectangles)() PURE; - - - // - // Gets the maximum region of each specified input which would be used during a - // subsequent rendering operation - // - STDMETHOD(GetEffectRequiredInputRectangles)() PURE; - - - // - // Fill using the alpha channel of the supplied opacity mask bitmap. The brush - // opacity will be modulated by the mask. The render target antialiasing mode must - // be set to aliased. - // - STDMETHOD_(void, FillOpacityMask)() PURE; - - - HRESULT CreateBitmap1() { return S_OK; } - HRESULT CreateBitmap2() { return S_OK; } - HRESULT CreateBitmap3() { return S_OK; } - HRESULT CreateBitmap4() { return S_OK; } - - HRESULT CreateImageBrush1() { return S_OK; } - HRESULT CreateImageBrush2() { return S_OK; } - - HRESULT CreateBitmapBrush1() { return S_OK; } - HRESULT CreateBitmapBrush2() { return S_OK; } - HRESULT CreateBitmapBrush3() { return S_OK; } - - // - // Draws the output of the effect as an image. - // - void DrawImage1() {} - void DrawImage2() {} - void DrawImage3() {} - void DrawImage4() {} - void DrawImage5() {} - void DrawImage6() {} - void DrawImage7() {} - - void - PushLayer( - CONST D2D1_LAYER_PARAMETERS1 &layerParameters, - _In_opt_ ID2D1Layer *layer - ) - { - PushLayer(&layerParameters, layer); - } - - void DrawGdiMetafile1() {} - - void DrawBitmap1() {} - void DrawBitmap2() {} - void DrawBitmap3() {} - - void FillOpacityMask1() {} - void FillOpacityMask2() {} - - // - // Sets tuning parameters for internal rendering inside the device context. - // - void SetRenderingControls1() {} -}; // interface ID2D1DeviceContext - -#endif // #ifndef _D2D1_1_H_
--- a/gfx/gl/GLContextEGL.h +++ b/gfx/gl/GLContextEGL.h @@ -99,19 +99,16 @@ public: return sEGLLibrary.Display(); } bool BindTex2DOffscreen(GLContext *aOffscreen); void UnbindTex2DOffscreen(GLContext *aOffscreen); void BindOffscreenFramebuffer(); static already_AddRefed<GLContextEGL> - CreateEGLPixmapOffscreenContext(const gfx::IntSize& size); - - static already_AddRefed<GLContextEGL> CreateEGLPBufferOffscreenContext(CreateContextFlags flags, const gfx::IntSize& size, const SurfaceCaps& minCaps); protected: friend class GLContextProviderEGL; public:
--- a/gfx/gl/GLContextProviderEGL.cpp +++ b/gfx/gl/GLContextProviderEGL.cpp @@ -960,56 +960,16 @@ GLContextEGL::CreateEGLPBufferOffscreenC NS_WARNING("Failed to initialize GLContext!"); // GLContextEGL::dtor will destroy |surface| for us. return nullptr; } return gl.forget(); } -/*static*/ already_AddRefed<GLContextEGL> -GLContextEGL::CreateEGLPixmapOffscreenContext(const mozilla::gfx::IntSize& size) -{ - gfxASurface *thebesSurface = nullptr; - EGLNativePixmapType pixmap = 0; - - if (!pixmap) { - return nullptr; - } - - EGLSurface surface = 0; - EGLConfig config = 0; - - if (!config) { - return nullptr; - } - MOZ_ASSERT(surface); - - SurfaceCaps dummyCaps = SurfaceCaps::Any(); - RefPtr<GLContextEGL> glContext = - GLContextEGL::CreateGLContext(CreateContextFlags::NONE, dummyCaps, - nullptr, true, - config, surface); - if (!glContext) { - NS_WARNING("Failed to create GLContext from XSurface"); - sEGLLibrary.fDestroySurface(EGL_DISPLAY(), surface); - return nullptr; - } - - if (!glContext->Init()) { - NS_WARNING("Failed to initialize GLContext!"); - // GLContextEGL::dtor will destroy |surface| for us. - return nullptr; - } - - glContext->HoldSurface(thebesSurface); - - return glContext.forget(); -} - /*static*/ already_AddRefed<GLContext> GLContextProviderEGL::CreateHeadless(CreateContextFlags flags) { bool forceEnableHardware = bool(flags & CreateContextFlags::FORCE_ENABLE_HARDWARE); if (!sEGLLibrary.EnsureInitialized(forceEnableHardware)) return nullptr; mozilla::gfx::IntSize dummySize = mozilla::gfx::IntSize(16, 16);
--- a/gfx/gl/GLLibraryEGL.h +++ b/gfx/gl/GLLibraryEGL.h @@ -103,19 +103,62 @@ namespace gl { class GLContext; class GLLibraryEGL { public: GLLibraryEGL() : mInitialized(false), mEGLLibrary(nullptr), + mEGLDisplay(EGL_NO_DISPLAY), mIsANGLE(false), mIsWARP(false) { + ClearSymbols(); + } + + void ClearSymbols() { + mSymbols.fGetDisplay = nullptr; + mSymbols.fGetPlatformDisplayEXT = nullptr; + mSymbols.fTerminate = nullptr; + mSymbols.fGetCurrentSurface = nullptr; + mSymbols.fGetCurrentContext = nullptr; + mSymbols.fMakeCurrent = nullptr; + mSymbols.fDestroyContext = nullptr; + mSymbols.fCreateContext = nullptr; + mSymbols.fDestroySurface = nullptr; + mSymbols.fCreateWindowSurface = nullptr; + mSymbols.fCreatePbufferSurface = nullptr; + mSymbols.fCreatePixmapSurface = nullptr; + mSymbols.fBindAPI = nullptr; + mSymbols.fInitialize = nullptr; + mSymbols.fChooseConfig = nullptr; + mSymbols.fGetError = nullptr; + mSymbols.fGetConfigAttrib = nullptr; + mSymbols.fGetConfigs = nullptr; + mSymbols.fWaitNative = nullptr; + mSymbols.fGetProcAddress = nullptr; + mSymbols.fSwapBuffers = nullptr; + mSymbols.fCopyBuffers = nullptr; + mSymbols.fQueryString = nullptr; + mSymbols.fQueryStringImplementationANDROID = nullptr; + mSymbols.fQueryContext = nullptr; + mSymbols.fBindTexImage = nullptr; + mSymbols.fReleaseTexImage = nullptr; + mSymbols.fCreateImage = nullptr; + mSymbols.fDestroyImage = nullptr; + mSymbols.fLockSurface = nullptr; + mSymbols.fUnlockSurface = nullptr; + mSymbols.fQuerySurface = nullptr; + mSymbols.fQuerySurfacePointerANGLE = nullptr; + mSymbols.fCreateSync = nullptr; + mSymbols.fDestroySync = nullptr; + mSymbols.fClientWaitSync = nullptr; + mSymbols.fGetSyncAttrib = nullptr; + mSymbols.fDupNativeFenceFDANDROID = nullptr; } void InitClientExtensions(); void InitDisplayExtensions(); /** * Known GL extensions that can be queried by * IsExtensionSupported. The results of this are cached, and as
--- a/gfx/gl/GLXLibrary.h +++ b/gfx/gl/GLXLibrary.h @@ -27,23 +27,46 @@ struct PRLibrary; class gfxASurface; namespace mozilla { namespace gl { class GLXLibrary { public: - GLXLibrary() : mInitialized(false), mTriedInitializing(false), - mUseTextureFromPixmap(false), mDebug(false), - mHasRobustness(false), mHasCreateContextAttribs(false), - mIsATI(false), mIsNVIDIA(false), - mClientIsMesa(false), mGLXMajorVersion(0), - mGLXMinorVersion(0), - mOGLLibrary(nullptr) {} + GLXLibrary() + : xDestroyContextInternal(nullptr) + , xMakeCurrentInternal(nullptr) + , xGetCurrentContextInternal(nullptr) + , xGetProcAddressInternal(nullptr) + , xChooseFBConfigInternal(nullptr) + , xGetFBConfigsInternal(nullptr) + , xCreateNewContextInternal(nullptr) + , xGetFBConfigAttribInternal(nullptr) + , xSwapBuffersInternal(nullptr) + , xQueryExtensionsStringInternal(nullptr) + , xGetClientStringInternal(nullptr) + , xQueryServerStringInternal(nullptr) + , xCreatePixmapInternal(nullptr) + , xCreateGLXPixmapWithConfigInternal(nullptr) + , xDestroyPixmapInternal(nullptr) + , xQueryVersionInternal(nullptr) + , xBindTexImageInternal(nullptr) + , xReleaseTexImageInternal(nullptr) + , xWaitGLInternal(nullptr) + , xWaitXInternal(nullptr) + , xCreateContextAttribsInternal(nullptr) + , mInitialized(false), mTriedInitializing(false) + , mUseTextureFromPixmap(false), mDebug(false) + , mHasRobustness(false), mHasCreateContextAttribs(false) + , mIsATI(false), mIsNVIDIA(false) + , mClientIsMesa(false), mGLXMajorVersion(0) + , mGLXMinorVersion(0) + , mOGLLibrary(nullptr) + {} void xDestroyContext(Display* display, GLXContext context); Bool xMakeCurrent(Display* display, GLXDrawable drawable, GLXContext context); GLXContext xGetCurrentContext(); static void* xGetProcAddress(const char *procName);
--- a/gfx/layers/ImageContainer.h +++ b/gfx/layers/ImageContainer.h @@ -862,17 +862,17 @@ public: void SetData(const SidebandStreamData& aData) { mSidebandStream = aData.mStream; mSize = aData.mSize; } already_AddRefed<gfx::SourceSurface> GetAsSourceSurface() { return nullptr; } ; int32_t GetOverlayId() { return mOverlayId; } - const GonkNativeHandle& GetSidebandStream() { return mSidebandStream; } + GonkNativeHandle& GetSidebandStream() { return mSidebandStream; } gfx::IntSize GetSize() { return mSize; } private: int32_t mOverlayId; GonkNativeHandle mSidebandStream; gfx::IntSize mSize; };
--- a/gfx/layers/apz/src/APZCTreeManager.cpp +++ b/gfx/layers/apz/src/APZCTreeManager.cpp @@ -1133,21 +1133,22 @@ APZCTreeManager::ReceiveInputEvent(Widge default: { return ProcessEvent(aEvent, aOutTargetGuid, aOutInputBlockId); } } } void APZCTreeManager::ZoomToRect(const ScrollableLayerGuid& aGuid, - const CSSRect& aRect) + const CSSRect& aRect, + const uint32_t aFlags) { RefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(aGuid); if (apzc) { - apzc->ZoomToRect(aRect); + apzc->ZoomToRect(aRect, aFlags); } } void APZCTreeManager::ContentReceivedInputBlock(uint64_t aInputBlockId, bool aPreventDefault) { APZThreadUtils::AssertOnControllerThread();
--- a/gfx/layers/apz/src/APZCTreeManager.h +++ b/gfx/layers/apz/src/APZCTreeManager.h @@ -36,16 +36,22 @@ enum AllowedTouchBehavior { NONE = 0, VERTICAL_PAN = 1 << 0, HORIZONTAL_PAN = 1 << 1, PINCH_ZOOM = 1 << 2, DOUBLE_TAP_ZOOM = 1 << 3, UNKNOWN = 1 << 4 }; +enum ZoomToRectBehavior : uint32_t { + DEFAULT_BEHAVIOR = 0, + DISABLE_ZOOM_OUT = 1 << 0, + PAN_INTO_VIEW_ONLY = 1 << 1 +}; + class Layer; class AsyncDragMetrics; class AsyncPanZoomController; class CompositorParent; class OverscrollHandoffChain; struct OverscrollHandoffState; struct FlingHandoffState; class LayerMetricsWrapper; @@ -200,19 +206,21 @@ public: nsEventStatus ReceiveInputEvent(WidgetInputEvent& aEvent, ScrollableLayerGuid* aOutTargetGuid, uint64_t* aOutInputBlockId); /** * Kicks an animation to zoom to a rect. This may be either a zoom out or zoom * in. The actual animation is done on the compositor thread after being set * up. |aRect| must be given in CSS pixels, relative to the document. + * |aFlags| is a combination of the ZoomToRectBehavior enum values. */ void ZoomToRect(const ScrollableLayerGuid& aGuid, - const CSSRect& aRect); + const CSSRect& aRect, + const uint32_t aFlags = DEFAULT_BEHAVIOR); /** * If we have touch listeners, this should always be called when we know * definitively whether or not content has preventDefaulted any touch events * that have come in. If |aPreventDefault| is true, any touch events in the * queue will be discarded. This function must be called on the controller * thread. */
--- a/gfx/layers/apz/src/AsyncPanZoomController.cpp +++ b/gfx/layers/apz/src/AsyncPanZoomController.cpp @@ -3367,17 +3367,17 @@ const FrameMetrics& AsyncPanZoomControll return mFrameMetrics; } APZCTreeManager* AsyncPanZoomController::GetApzcTreeManager() const { mMonitor.AssertNotCurrentThreadIn(); return mTreeManager; } -void AsyncPanZoomController::ZoomToRect(CSSRect aRect) { +void AsyncPanZoomController::ZoomToRect(CSSRect aRect, const uint32_t aFlags) { if (!aRect.IsFinite()) { NS_WARNING("ZoomToRect got called with a non-finite rect; ignoring..."); return; } // Only the root APZC is zoomable, and the root APZC is not allowed to have // different x and y scales. If it did, the calculations in this function // would have to be adjusted (as e.g. it would no longer be valid to take @@ -3408,26 +3408,32 @@ void AsyncPanZoomController::ZoomToRect( CSSToParentLayerScale localMaxZoom = mZoomConstraints.mMaxZoom; if (!aRect.IsEmpty()) { // Intersect the zoom-to-rect to the CSS rect to make sure it fits. aRect = aRect.Intersect(cssPageRect); targetZoom = CSSToParentLayerScale(std::min(compositionBounds.width / aRect.width, compositionBounds.height / aRect.height)); } + // 1. If the rect is empty, the content-side logic for handling a double-tap // requested that we zoom out. // 2. currentZoom is equal to mZoomConstraints.mMaxZoom and user still double-tapping it // 3. currentZoom is equal to localMinZoom and user still double-tapping it // Treat these three cases as a request to zoom out as much as possible. - bool zoomOut = false; - if (aRect.IsEmpty() || + bool zoomOut; + if (aFlags & DISABLE_ZOOM_OUT) { + zoomOut = false; + } else { + zoomOut = aRect.IsEmpty() || (currentZoom == localMaxZoom && targetZoom >= localMaxZoom) || - (currentZoom == localMinZoom && targetZoom <= localMinZoom)) { - zoomOut = true; + (currentZoom == localMinZoom && targetZoom <= localMinZoom); + } + + if (zoomOut) { CSSSize compositedSize = mFrameMetrics.CalculateCompositedSizeInCssPixels(); float y = scrollOffset.y; float newHeight = cssPageRect.width * (compositedSize.height / compositedSize.width); float dh = compositedSize.height - newHeight; aRect = CSSRect(0.0f, y + dh/2, @@ -3435,16 +3441,19 @@ void AsyncPanZoomController::ZoomToRect( newHeight); aRect = aRect.Intersect(cssPageRect); targetZoom = CSSToParentLayerScale(std::min(compositionBounds.width / aRect.width, compositionBounds.height / aRect.height)); } targetZoom.scale = clamped(targetZoom.scale, localMinZoom.scale, localMaxZoom.scale); FrameMetrics endZoomToMetrics = mFrameMetrics; + if (aFlags & PAN_INTO_VIEW_ONLY) { + targetZoom = currentZoom; + } endZoomToMetrics.SetZoom(CSSToParentLayerScale2D(targetZoom)); // Adjust the zoomToRect to a sensible position to prevent overscrolling. CSSSize sizeAfterZoom = endZoomToMetrics.CalculateCompositedSizeInCssPixels(); // If either of these conditions are met, the page will be // overscrolled after zoomed if (aRect.y + sizeAfterZoom.height > cssPageRect.height) {
--- a/gfx/layers/apz/src/AsyncPanZoomController.h +++ b/gfx/layers/apz/src/AsyncPanZoomController.h @@ -121,17 +121,17 @@ public: // These methods must only be called on the controller/UI thread. // /** * Kicks an animation to zoom to a rect. This may be either a zoom out or zoom * in. The actual animation is done on the compositor thread after being set * up. */ - void ZoomToRect(CSSRect aRect); + void ZoomToRect(CSSRect aRect, const uint32_t aFlags); /** * Updates any zoom constraints contained in the <meta name="viewport"> tag. */ void UpdateZoomConstraints(const ZoomConstraints& aConstraints); /** * Return the zoom constraints last set for this APZC (in the constructor
--- a/gfx/layers/apz/util/DoubleTapToZoom.cpp +++ b/gfx/layers/apz/util/DoubleTapToZoom.cpp @@ -65,57 +65,16 @@ ShouldZoomToElement(const nsCOMPtr<dom:: } } if (aElement->IsAnyOfHTMLElements(nsGkAtoms::li, nsGkAtoms::q)) { return false; } return true; } -// Calculate the bounding rect of |aElement|, relative to the origin -// of the scrolled content of |aRootScrollFrame|. -// The implementation of this calculation is adapted from -// Element::GetBoundingClientRect(). -// -// Where the element is contained inside a scrollable subframe, the -// bounding rect is clipped to the bounds of the subframe. -static CSSRect -GetBoundingContentRect(const nsCOMPtr<dom::Element>& aElement, - const nsIScrollableFrame* aRootScrollFrame) { - - CSSRect result; - if (nsIFrame* frame = aElement->GetPrimaryFrame()) { - nsIFrame* relativeTo = aRootScrollFrame->GetScrolledFrame(); - result = CSSRect::FromAppUnits( - nsLayoutUtils::GetAllInFlowRectsUnion( - frame, - relativeTo, - nsLayoutUtils::RECTS_ACCOUNT_FOR_TRANSFORMS)); - - // If the element is contained in a scrollable frame that is not - // the root scroll frame, make sure to clip the result so that it is - // not larger than the containing scrollable frame's bounds. - nsIScrollableFrame* scrollFrame = nsLayoutUtils::GetNearestScrollableFrame(frame); - if (scrollFrame && scrollFrame != aRootScrollFrame) { - nsIFrame* subFrame = do_QueryFrame(scrollFrame); - MOZ_ASSERT(subFrame); - // Get the bounds of the scroll frame in the same coordinate space - // as |result|. - CSSRect subFrameRect = CSSRect::FromAppUnits( - nsLayoutUtils::TransformFrameRectToAncestor( - subFrame, - subFrame->GetRectRelativeToSelf(), - relativeTo)); - - result = subFrameRect.Intersect(result); - } - } - return result; -} - static bool IsRectZoomedIn(const CSSRect& aRect, const CSSRect& aCompositedArea) { // This functions checks to see if the area of the rect visible in the // composition bounds (i.e. the overlapArea variable below) is approximately // the max area of the rect we can show. CSSRect overlap = aCompositedArea.Intersect(aRect); float overlapArea = overlap.width * overlap.height; @@ -159,17 +118,17 @@ CalculateRectToZoomTo(const nsCOMPtr<nsI if (!element) { return zoomOut; } FrameMetrics metrics = nsLayoutUtils::CalculateBasicFrameMetrics(rootScrollFrame); CSSRect compositedArea(metrics.GetScrollOffset(), metrics.CalculateCompositedSizeInCssPixels()); const CSSCoord margin = 15; - CSSRect rect = GetBoundingContentRect(element, rootScrollFrame); + CSSRect rect = nsLayoutUtils::GetBoundingContentRect(element, rootScrollFrame); // If the element is taller than the visible area of the page scale // the height of the |rect| so that it has the same aspect ratio as // the root frame. The clipped |rect| is centered on the y value of // the touch point. This allows tall narrow elements to be zoomed. if (!rect.IsEmpty() && compositedArea.width > 0.0f) { const float widthRatio = rect.width / compositedArea.width; float targetHeight = compositedArea.height * widthRatio;
--- a/gfx/layers/basic/X11TextureSourceBasic.cpp +++ b/gfx/layers/basic/X11TextureSourceBasic.cpp @@ -30,22 +30,19 @@ X11TextureSourceBasic::GetFormat() const gfxContentType type = mSurface->GetContentType(); return X11TextureSourceBasic::ContentTypeToSurfaceFormat(type); } SourceSurface* X11TextureSourceBasic::GetSurface(DrawTarget* aTarget) { if (!mSourceSurface) { - NativeSurface surf; - surf.mFormat = GetFormat(); - surf.mType = NativeSurfaceType::CAIRO_CONTEXT; - surf.mSurface = mSurface->CairoSurface(); - surf.mSize = GetSize(); - mSourceSurface = aTarget->CreateSourceSurfaceFromNativeSurface(surf); + mSourceSurface = + Factory::CreateSourceSurfaceForCairoSurface(mSurface->CairoSurface(), + GetSize(), GetFormat()); } return mSourceSurface; } void X11TextureSourceBasic::SetCompositor(Compositor* aCompositor) { MOZ_ASSERT(aCompositor->GetBackendType() == LayersBackend::LAYERS_BASIC);
--- a/gfx/layers/client/ImageClient.cpp +++ b/gfx/layers/client/ImageClient.cpp @@ -1,22 +1,26 @@ /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "ImageClient.h" + #include <stdint.h> // for uint32_t + +#include "ClientLayerManager.h" // for ClientLayer #include "ImageContainer.h" // for Image, PlanarYCbCrImage, etc #include "ImageTypes.h" // for ImageFormat::PLANAR_YCBCR, etc #include "GLImages.h" // for SurfaceTextureImage::Data, etc #include "gfx2DGlue.h" // for ImageFormatToSurfaceFormat #include "gfxPlatform.h" // for gfxPlatform #include "mozilla/Assertions.h" // for MOZ_ASSERT, etc #include "mozilla/RefPtr.h" // for RefPtr, already_AddRefed +#include "mozilla/gfx/2D.h" #include "mozilla/gfx/BaseSize.h" // for BaseSize #include "mozilla/gfx/Point.h" // for IntSize #include "mozilla/gfx/Types.h" // for SurfaceFormat, etc #include "mozilla/layers/CompositableClient.h" // for CompositableClient #include "mozilla/layers/CompositableForwarder.h" #include "mozilla/layers/CompositorTypes.h" // for CompositableType, etc #include "mozilla/layers/ISurfaceAllocator.h" #include "mozilla/layers/LayersSurfaces.h" // for SurfaceDescriptor, etc @@ -24,17 +28,17 @@ #include "mozilla/layers/TextureClient.h" // for TextureClient, etc #include "mozilla/layers/TextureClientOGL.h" // for SurfaceTextureClient #include "mozilla/mozalloc.h" // for operator delete, etc #include "nsAutoPtr.h" // for nsRefPtr #include "nsCOMPtr.h" // for already_AddRefed #include "nsDebug.h" // for NS_WARNING, NS_ASSERTION #include "nsISupportsImpl.h" // for Image::Release, etc #include "nsRect.h" // for mozilla::gfx::IntRect -#include "mozilla/gfx/2D.h" + #ifdef MOZ_WIDGET_GONK #include "GrallocImages.h" #endif namespace mozilla { namespace layers { using namespace mozilla::gfx; @@ -150,17 +154,25 @@ ImageClientSingle::UpdateImage(ImageCont for (auto& img : images) { Image* image = img.mImage; #ifdef MOZ_WIDGET_GONK if (image->GetFormat() == ImageFormat::OVERLAY_IMAGE) { OverlayImage* overlayImage = static_cast<OverlayImage*>(image); OverlaySource source; if (overlayImage->GetSidebandStream().IsValid()) { - source.handle() = OverlayHandle(overlayImage->GetSidebandStream()); + // Duplicate GonkNativeHandle::NhObj for ipc, + // since ParamTraits<GonkNativeHandle>::Write() absorbs native_handle_t. + RefPtr<GonkNativeHandle::NhObj> nhObj = overlayImage->GetSidebandStream().GetDupNhObj(); + GonkNativeHandle handle(nhObj); + if (!handle.IsValid()) { + gfxWarning() << "ImageClientSingle::UpdateImage failed in GetDupNhObj"; + return false; + } + source.handle() = OverlayHandle(handle); } else { source.handle() = OverlayHandle(overlayImage->GetOverlayId()); } source.size() = overlayImage->GetSize(); GetForwarder()->UseOverlaySource(this, source, image->GetPictureRect()); continue; } #endif
--- a/gfx/layers/client/TextureClient.cpp +++ b/gfx/layers/client/TextureClient.cpp @@ -201,16 +201,17 @@ static void DestroyTextureData(TextureDa aTextureData->Forget(aAllocator); } delete aTextureData; } void TextureChild::ActorDestroy(ActorDestroyReason why) { + PROFILER_LABEL_FUNC(js::ProfileEntry::Category::GRAPHICS); mWaitForRecycle = nullptr; if (mTextureData) { DestroyTextureData(mTextureData, GetAllocator(), mOwnsTextureData, mMainThreadOnly); mTextureData = nullptr; } }
--- a/gfx/layers/ipc/CompositorParent.cpp +++ b/gfx/layers/ipc/CompositorParent.cpp @@ -70,16 +70,17 @@ #include "mozilla/StaticPtr.h" #ifdef MOZ_ENABLE_PROFILER_SPS #include "ProfilerMarkers.h" #endif #include "mozilla/VsyncDispatcher.h" #ifdef MOZ_WIDGET_GONK #include "GeckoTouchDispatcher.h" +#include "nsScreenManagerGonk.h" #endif #include "LayerScope.h" namespace mozilla { namespace gfx { // See VRManagerChild.cpp @@ -281,30 +282,42 @@ CompositorVsyncScheduler::Observer::Dest { MutexAutoLock lock(mMutex); mOwner = nullptr; } CompositorVsyncScheduler::CompositorVsyncScheduler(CompositorParent* aCompositorParent, nsIWidget* aWidget) : mCompositorParent(aCompositorParent) , mLastCompose(TimeStamp::Now()) - , mCurrentCompositeTask(nullptr) , mIsObservingVsync(false) , mNeedsComposite(0) , mVsyncNotificationsSkipped(0) , mCurrentCompositeTaskMonitor("CurrentCompositeTaskMonitor") + , mCurrentCompositeTask(nullptr) , mSetNeedsCompositeMonitor("SetNeedsCompositeMonitor") , mSetNeedsCompositeTask(nullptr) +#ifdef MOZ_WIDGET_GONK +#if ANDROID_VERSION >= 19 + , mDisplayEnabled(false) + , mSetDisplayMonitor("SetDisplayMonitor") + , mSetDisplayTask(nullptr) +#endif +#endif { MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(aWidget != nullptr); mVsyncObserver = new Observer(this); mCompositorVsyncDispatcher = aWidget->GetCompositorVsyncDispatcher(); #ifdef MOZ_WIDGET_GONK GeckoTouchDispatcher::GetInstance()->SetCompositorVsyncScheduler(this); + +#if ANDROID_VERSION >= 19 + RefPtr<nsScreenManagerGonk> screenManager = nsScreenManagerGonk::GetInstance(); + screenManager->SetCompositorVsyncScheduler(this); +#endif #endif // mAsapScheduling is set on the main thread during init, // but is only accessed after on the compositor thread. mAsapScheduling = gfxPrefs::LayersCompositionFrameRate() == 0 || gfxPlatform::IsInLayoutAsapMode(); } @@ -312,23 +325,77 @@ CompositorVsyncScheduler::~CompositorVsy { MOZ_ASSERT(!mIsObservingVsync); MOZ_ASSERT(!mVsyncObserver); // The CompositorVsyncDispatcher is cleaned up before this in the nsBaseWidget, which stops vsync listeners mCompositorParent = nullptr; mCompositorVsyncDispatcher = nullptr; } +#ifdef MOZ_WIDGET_GONK +#if ANDROID_VERSION >= 19 +void +CompositorVsyncScheduler::SetDisplay(bool aDisplayEnable) +{ + // SetDisplay() is usually called from nsScreenManager at main thread. Post + // to compositor thread if needs. + if (!CompositorParent::IsInCompositorThread()) { + MOZ_ASSERT(NS_IsMainThread()); + MonitorAutoLock lock(mSetDisplayMonitor); + mSetDisplayTask = NewRunnableMethod(this, + &CompositorVsyncScheduler::SetDisplay, + aDisplayEnable); + ScheduleTask(mSetDisplayTask, 0); + return; + } else { + MonitorAutoLock lock(mSetDisplayMonitor); + mSetDisplayTask = nullptr; + } + + if (mDisplayEnabled == aDisplayEnable) { + return; + } + + mDisplayEnabled = aDisplayEnable; + if (!mDisplayEnabled) { + CancelCurrentSetNeedsCompositeTask(); + CancelCurrentCompositeTask(); + } +} + +void +CompositorVsyncScheduler::CancelSetDisplayTask() +{ + MOZ_ASSERT(CompositorParent::IsInCompositorThread()); + MonitorAutoLock lock(mSetDisplayMonitor); + if (mSetDisplayTask) { + mSetDisplayTask->Cancel(); + mSetDisplayTask = nullptr; + } + + // CancelSetDisplayTask is only be called in clean-up process, so + // mDisplayEnabled could be false there. + mDisplayEnabled = false; +} +#endif //ANDROID_VERSION >= 19 +#endif //MOZ_WIDGET_GONK + void CompositorVsyncScheduler::Destroy() { MOZ_ASSERT(CompositorParent::IsInCompositorThread()); UnobserveVsync(); mVsyncObserver->Destroy(); mVsyncObserver = nullptr; + +#ifdef MOZ_WIDGET_GONK +#if ANDROID_VERSION >= 19 + CancelSetDisplayTask(); +#endif +#endif CancelCurrentSetNeedsCompositeTask(); CancelCurrentCompositeTask(); } void CompositorVsyncScheduler::PostCompositeTask(TimeStamp aCompositeTimestamp) { // can be called from the compositor or vsync thread @@ -391,16 +458,25 @@ CompositorVsyncScheduler::SetNeedsCompos &CompositorVsyncScheduler::SetNeedsComposite); ScheduleTask(mSetNeedsCompositeTask, 0); return; } else { MonitorAutoLock lock(mSetNeedsCompositeMonitor); mSetNeedsCompositeTask = nullptr; } +#ifdef MOZ_WIDGET_GONK +#if ANDROID_VERSION >= 19 + // Skip composition when display off. + if (!mDisplayEnabled) { + return; + } +#endif +#endif + mNeedsComposite++; if (!mIsObservingVsync && mNeedsComposite) { ObserveVsync(); } } bool CompositorVsyncScheduler::NotifyVsync(TimeStamp aVsyncTimestamp)
--- a/gfx/layers/ipc/CompositorParent.h +++ b/gfx/layers/ipc/CompositorParent.h @@ -95,16 +95,30 @@ private: * Turns vsync notifications into scheduled composites. **/ class CompositorVsyncScheduler { NS_INLINE_DECL_THREADSAFE_REFCOUNTING(CompositorVsyncScheduler) public: explicit CompositorVsyncScheduler(CompositorParent* aCompositorParent, nsIWidget* aWidget); + +#ifdef MOZ_WIDGET_GONK + // emulator-ics never trigger the display on/off, so compositor will always + // skip composition request at that device. Only check the display status + // with kk device and upon. +#if ANDROID_VERSION >= 19 + // SetDisplay() and CancelSetDisplayTask() are used for the display on/off. + // It will clear all composition related task and flag, and skip another + // composition task during the display off. That could prevent the problem + // that compositor might show the old content at the first frame of display on. + void SetDisplay(bool aDisplayEnable); +#endif +#endif + bool NotifyVsync(TimeStamp aVsyncTimestamp); void SetNeedsComposite(); void OnForceComposeToTarget(); void ScheduleTask(CancelableTask*, int); void ResumeComposition(); void ComposeToTarget(gfx::DrawTarget* aTarget, const gfx::IntRect* aRect = nullptr); void PostCompositeTask(TimeStamp aCompositeTimestamp); @@ -123,26 +137,31 @@ public: } #ifdef COMPOSITOR_PERFORMANCE_WARNING const TimeStamp& GetExpectedComposeStartTime() { return mExpectedComposeStartTime; } #endif - + private: virtual ~CompositorVsyncScheduler(); void NotifyCompositeTaskExecuted(); void ObserveVsync(); void UnobserveVsync(); void DispatchTouchEvents(TimeStamp aVsyncTimestamp); void DispatchVREvents(TimeStamp aVsyncTimestamp); void CancelCurrentSetNeedsCompositeTask(); +#ifdef MOZ_WIDGET_GONK +#if ANDROID_VERSION >= 19 + void CancelSetDisplayTask(); +#endif +#endif class Observer final : public VsyncObserver { public: explicit Observer(CompositorVsyncScheduler* aOwner); virtual bool NotifyVsync(TimeStamp aVsyncTimestamp) override; void Destroy(); private: @@ -150,33 +169,41 @@ private: Mutex mMutex; // Hold raw pointer to avoid mutual reference. CompositorVsyncScheduler* mOwner; }; CompositorParent* mCompositorParent; TimeStamp mLastCompose; - CancelableTask* mCurrentCompositeTask; #ifdef COMPOSITOR_PERFORMANCE_WARNING TimeStamp mExpectedComposeStartTime; #endif bool mAsapScheduling; bool mIsObservingVsync; uint32_t mNeedsComposite; int32_t mVsyncNotificationsSkipped; RefPtr<CompositorVsyncDispatcher> mCompositorVsyncDispatcher; RefPtr<CompositorVsyncScheduler::Observer> mVsyncObserver; mozilla::Monitor mCurrentCompositeTaskMonitor; + CancelableTask* mCurrentCompositeTask; mozilla::Monitor mSetNeedsCompositeMonitor; CancelableTask* mSetNeedsCompositeTask; + +#ifdef MOZ_WIDGET_GONK +#if ANDROID_VERSION >= 19 + bool mDisplayEnabled; + mozilla::Monitor mSetDisplayMonitor; + CancelableTask* mSetDisplayTask; +#endif +#endif }; class CompositorUpdateObserver { public: NS_INLINE_DECL_THREADSAFE_REFCOUNTING(CompositorUpdateObserver); virtual void ObserveUpdate(uint64_t aLayersId, bool aActive) = 0;
--- a/gfx/layers/ipc/GonkNativeHandle.cpp +++ b/gfx/layers/ipc/GonkNativeHandle.cpp @@ -38,28 +38,41 @@ GonkNativeHandle::GetAndResetNhObj() RefPtr<NhObj> nhObj = mNhObj; mNhObj = new NhObj(); return nhObj.forget(); } already_AddRefed<GonkNativeHandle::NhObj> GonkNativeHandle::GetDupNhObj() { + if (!IsValid()) { + return GonkNativeHandle::CreateDupNhObj(nullptr); + } + return GonkNativeHandle::CreateDupNhObj(mNhObj->mHandle); +} + +/* static */ already_AddRefed<GonkNativeHandle::NhObj> +GonkNativeHandle::CreateDupNhObj(native_handle_t* aHandle) +{ RefPtr<NhObj> nhObj; - if (IsValid()) { + if (aHandle) { native_handle* nativeHandle = - native_handle_create(mNhObj->mHandle->numFds, mNhObj->mHandle->numInts); + native_handle_create(aHandle->numFds, aHandle->numInts); + if (!nativeHandle) { + nhObj = new GonkNativeHandle::NhObj(); + return nhObj.forget(); + } - for (int i = 0; i < mNhObj->mHandle->numFds; ++i) { - nativeHandle->data[i] = dup(mNhObj->mHandle->data[i]); + for (int i = 0; i < aHandle->numFds; ++i) { + nativeHandle->data[i] = dup(aHandle->data[i]); } memcpy(nativeHandle->data + nativeHandle->numFds, - mNhObj->mHandle->data + mNhObj->mHandle->numFds, - sizeof(int) * mNhObj->mHandle->numInts); + aHandle->data + aHandle->numFds, + sizeof(int) * aHandle->numInts); nhObj = new GonkNativeHandle::NhObj(nativeHandle); } else { nhObj = new GonkNativeHandle::NhObj(); } return nhObj.forget(); }
--- a/gfx/layers/ipc/GonkNativeHandle.h +++ b/gfx/layers/ipc/GonkNativeHandle.h @@ -66,16 +66,18 @@ public: } void TransferToAnother(GonkNativeHandle& aHandle); already_AddRefed<NhObj> GetAndResetNhObj(); already_AddRefed<NhObj> GetDupNhObj(); + static already_AddRefed<NhObj> CreateDupNhObj(native_handle_t* aHandle); + // Return non owning handle. native_handle_t* GetRawNativeHandle() const { if (mNhObj) { return mNhObj->mHandle; } return nullptr; }
--- a/gfx/layers/opengl/OGLShaderProgram.cpp +++ b/gfx/layers/opengl/OGLShaderProgram.cpp @@ -3,16 +3,17 @@ * You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "OGLShaderProgram.h" #include <stdint.h> // for uint32_t #include <sstream> // for ostringstream #include "gfxEnv.h" #include "gfxRect.h" // for gfxRect #include "mozilla/DebugOnly.h" // for DebugOnly +#include "mozilla/layers/Compositor.h" // for BlendOpIsMixBlendMode #include "nsAString.h" #include "nsAutoPtr.h" // for nsRefPtr #include "nsString.h" // for nsAutoCString #include "Layers.h" #include "GLContext.h" namespace mozilla { namespace layers { @@ -157,30 +158,30 @@ ShaderConfigOGL::SetNoPremultipliedAlpha void ShaderConfigOGL::SetDEAA(bool aEnabled) { SetFeature(ENABLE_DEAA, aEnabled); } void -ShaderConfigOGL::SetCompositionOp(CompositionOp aOp) +ShaderConfigOGL::SetCompositionOp(gfx::CompositionOp aOp) { mCompositionOp = aOp; } /* static */ ProgramProfileOGL ProgramProfileOGL::GetProfileFor(ShaderConfigOGL aConfig) { ProgramProfileOGL result; ostringstream fs, vs; AddUniforms(result); - CompositionOp blendOp = aConfig.mCompositionOp; + gfx::CompositionOp blendOp = aConfig.mCompositionOp; vs << "#ifdef GL_ES" << endl; vs << "#define EDGE_PRECISION mediump" << endl; vs << "#else" << endl; vs << "#define EDGE_PRECISION" << endl; vs << "#endif" << endl; vs << "uniform mat4 uMatrixProj;" << endl; vs << "uniform vec4 uLayerRects[4];" << endl;
--- a/gfx/tests/gtest/TestVsync.cpp +++ b/gfx/tests/gtest/TestVsync.cpp @@ -17,17 +17,18 @@ #include "mozilla/VsyncDispatcher.h" using namespace mozilla; using namespace mozilla::gfx; using namespace mozilla::layers; using ::testing::_; // Timeout for vsync events to occur in milliseconds -const int kVsyncTimeoutMS = 50; +// Windows 8.1 has intermittents at 50 ms. Raise limit to 5 vsync intervals. +const int kVsyncTimeoutMS = 80; class TestVsyncObserver : public VsyncObserver { public: TestVsyncObserver() : mDidGetVsyncNotification(false) , mVsyncMonitor("VsyncMonitor") { }
--- a/gfx/thebes/gfxDWriteFontList.cpp +++ b/gfx/thebes/gfxDWriteFontList.cpp @@ -519,16 +519,18 @@ gfxDWriteFontEntry::GetFontTable(uint32_ } return nullptr; } nsresult gfxDWriteFontEntry::ReadCMAP(FontInfoData *aFontInfoData) { + PROFILER_LABEL_FUNC(js::ProfileEntry::Category::GRAPHICS); + // attempt this once, if errors occur leave a blank cmap if (mCharacterMap) { return NS_OK; } RefPtr<gfxCharacterMap> charmap; nsresult rv; bool symbolFont;
--- a/gfx/thebes/gfxGDIFontList.cpp +++ b/gfx/thebes/gfxGDIFontList.cpp @@ -19,16 +19,17 @@ #include "nsUnicharUtils.h" #include "nsDirectoryServiceUtils.h" #include "nsDirectoryServiceDefs.h" #include "nsAppDirectoryServiceDefs.h" #include "nsISimpleEnumerator.h" #include "nsIWindowsRegKey.h" #include "gfxFontConstants.h" +#include "GeckoProfiler.h" #include "mozilla/MemoryReporting.h" #include "mozilla/Telemetry.h" #include "mozilla/WindowsVersion.h" #include <usp10.h> using namespace mozilla; @@ -138,16 +139,18 @@ GDIFontEntry::GDIFontEntry(const nsAStri mIsDataUserFont = aUserFontData != nullptr; InitLogFont(aFaceName, aFontType); } nsresult GDIFontEntry::ReadCMAP(FontInfoData *aFontInfoData) { + PROFILER_LABEL_FUNC(js::ProfileEntry::Category::OTHER); + // attempt this once, if errors occur leave a blank cmap if (mCharacterMap) { return NS_OK; } // skip non-SFNT fonts completely if (mFontType != GFX_FONT_TYPE_PS_OPENTYPE && mFontType != GFX_FONT_TYPE_TT_OPENTYPE &&
--- a/gfx/thebes/gfxPlatform.cpp +++ b/gfx/thebes/gfxPlatform.cpp @@ -901,34 +901,31 @@ gfxPlatform::GetSourceSurfaceForSurface( if (aTarget->GetBackendType() == BackendType::CAIRO) { // If we're going to be used with a CAIRO DrawTarget, then just create a // SourceSurfaceCairo since we don't know the underlying type of the CAIRO // DrawTarget and can't pick a better surface type. Doing this also avoids // readback of aSurface's surface into memory if, for example, aSurface // wraps an xlib cairo surface (which can be important to avoid a major // slowdown). - NativeSurface surf; - surf.mFormat = format; - surf.mType = NativeSurfaceType::CAIRO_CONTEXT; - surf.mSurface = aSurface->CairoSurface(); - surf.mSize = aSurface->GetSize(); + // // We return here regardless of whether CreateSourceSurfaceFromNativeSurface // succeeds or not since we don't expect to be able to do any better below // if it fails. // // Note that the returned SourceSurfaceCairo holds a strong reference to // the cairo_surface_t* that it wraps, which essencially means it holds a // strong reference to aSurface since aSurface shares its // cairo_surface_t*'s reference count variable. As a result we can't cache // srcBuffer on aSurface (see below) since aSurface would then hold a // strong reference back to srcBuffer, creating a reference loop and a // memory leak. Not caching is fine since wrapping is cheap enough (no // copying) so we can just wrap again next time we're called. - return aTarget->CreateSourceSurfaceFromNativeSurface(surf); + return Factory::CreateSourceSurfaceForCairoSurface(aSurface->CairoSurface(), + aSurface->GetSize(), format); } RefPtr<SourceSurface> srcBuffer; // Currently no other DrawTarget types implement CreateSourceSurfaceFromNativeSurface if (!srcBuffer) { // If aSurface wraps data, we can create a SourceSurfaceRawData that wraps @@ -959,28 +956,18 @@ gfxPlatform::GetSourceSurfaceForSurface( "DrawTargetCairo above"); // We've run out of performant options. We now try creating a SourceSurface // using a temporary DrawTargetCairo and then optimizing it to aTarget's // actual type. The CreateSourceSurfaceFromNativeSurface() call will // likely create a DataSourceSurface (possibly involving copying and/or // readback), and the OptimizeSourceSurface may well copy again and upload // to the GPU. So, while this code path is rarely hit, hitting it may be // very slow. - NativeSurface surf; - surf.mFormat = format; - surf.mType = NativeSurfaceType::CAIRO_CONTEXT; - surf.mSurface = aSurface->CairoSurface(); - surf.mSize = aSurface->GetSize(); - RefPtr<DrawTarget> drawTarget = - Factory::CreateDrawTarget(BackendType::CAIRO, IntSize(1, 1), format); - if (!drawTarget) { - gfxWarning() << "gfxPlatform::GetSourceSurfaceForSurface failed in CreateDrawTarget"; - return nullptr; - } - srcBuffer = drawTarget->CreateSourceSurfaceFromNativeSurface(surf); + srcBuffer = Factory::CreateSourceSurfaceForCairoSurface(aSurface->CairoSurface(), + aSurface->GetSize(), format); if (srcBuffer) { srcBuffer = aTarget->OptimizeSourceSurface(srcBuffer); } } if (!srcBuffer) { return nullptr; }
--- a/gfx/thebes/gfxScriptItemizer.cpp +++ b/gfx/thebes/gfxScriptItemizer.cpp @@ -170,17 +170,17 @@ gfxScriptItemizer::Next(uint32_t& aRunSt * if it's an open character, push it onto the stack. * if it's a close character, find the matching open on the * stack, and use that script code. Any non-matching open * characters above it on the stack will be popped. * * We only do this if the script is COMMON; for chars with * specific script assignments, we just use them as-is. */ - GetGeneralCategory(ch); + gc = GetGeneralCategory(ch); if (gc == HB_UNICODE_GENERAL_CATEGORY_OPEN_PUNCTUATION) { uint32_t endPairChar = mozilla::unicode::GetMirroredChar(ch); if (endPairChar != ch) { push(endPairChar, scriptCode); } } else if (gc == HB_UNICODE_GENERAL_CATEGORY_CLOSE_PUNCTUATION && HasMirroredChar(ch)) {
--- a/gfx/thebes/gfxWindowsPlatform.cpp +++ b/gfx/thebes/gfxWindowsPlatform.cpp @@ -16,16 +16,17 @@ #include "mozilla/Preferences.h" #include "mozilla/Services.h" #include "mozilla/Snprintf.h" #include "mozilla/WindowsVersion.h" #include "nsServiceManagerUtils.h" #include "nsTArray.h" #include "mozilla/Telemetry.h" +#include "GeckoProfiler.h" #include "nsIWindowsRegKey.h" #include "nsIFile.h" #include "plbase64.h" #include "nsIXULRuntime.h" #include "imgLoader.h" #include "nsIGfxInfo.h" @@ -1200,16 +1201,18 @@ InvalidateWindowForDeviceReset(HWND aWnd RedrawWindow(aWnd, nullptr, nullptr, RDW_INVALIDATE|RDW_INTERNALPAINT|RDW_FRAME); return TRUE; } bool gfxWindowsPlatform::UpdateForDeviceReset() { + PROFILER_LABEL_FUNC(js::ProfileEntry::Category::GRAPHICS); + if (!DidRenderingDeviceReset()) { return false; } // Trigger an ::OnPaint for each window. ::EnumThreadWindows(GetCurrentThreadId(), InvalidateWindowForDeviceReset, 0);
--- a/gfx/thebes/gfxXlibNativeRenderer.cpp +++ b/gfx/thebes/gfxXlibNativeRenderer.cpp @@ -570,23 +570,18 @@ gfxXlibNativeRenderer::Draw(gfxContext* return; } SurfaceFormat moz2DFormat = cairo_surface_get_content(tempXlibSurface) == CAIRO_CONTENT_COLOR ? SurfaceFormat::B8G8R8A8 : SurfaceFormat::B8G8R8X8; if (method != eAlphaExtraction) { if (drawTarget) { - NativeSurface native; - native.mFormat = moz2DFormat; - native.mType = NativeSurfaceType::CAIRO_CONTEXT; - native.mSurface = tempXlibSurface; - native.mSize = size; RefPtr<SourceSurface> sourceSurface = - drawTarget->CreateSourceSurfaceFromNativeSurface(native); + Factory::CreateSourceSurfaceForCairoSurface(tempXlibSurface, size, moz2DFormat); if (sourceSurface) { drawTarget->DrawSurface(sourceSurface, Rect(offset.x, offset.y, size.width, size.height), Rect(0, 0, size.width, size.height)); } } else { RefPtr<gfxASurface> tmpSurf = gfxASurface::Wrap(tempXlibSurface); ctx->SetSource(tmpSurf, offset); @@ -612,23 +607,19 @@ gfxXlibNativeRenderer::Draw(gfxContext* whiteImage->CairoStatus() == CAIRO_STATUS_SUCCESS) { if (!gfxAlphaRecovery::RecoverAlpha(blackImage, whiteImage)) { cairo_surface_destroy(tempXlibSurface); return; } gfxASurface* paintSurface = blackImage; if (drawTarget) { - NativeSurface native; - native.mFormat = moz2DFormat; - native.mType = NativeSurfaceType::CAIRO_CONTEXT; - native.mSurface = paintSurface->CairoSurface(); - native.mSize = size; RefPtr<SourceSurface> sourceSurface = - drawTarget->CreateSourceSurfaceFromNativeSurface(native); + Factory::CreateSourceSurfaceForCairoSurface(paintSurface->CairoSurface(), + size, moz2DFormat); if (sourceSurface) { drawTarget->DrawSurface(sourceSurface, Rect(offset.x, offset.y, size.width, size.height), Rect(0, 0, size.width, size.height)); } } else { ctx->SetSource(paintSurface, offset); ctx->Paint();
--- a/image/FrameAnimator.cpp +++ b/image/FrameAnimator.cpp @@ -74,16 +74,17 @@ FrameAnimator::GetCurrentImgFrameEndTime return currentFrameEndTime; } FrameAnimator::RefreshResult FrameAnimator::AdvanceFrame(TimeStamp aTime) { NS_ASSERTION(aTime <= TimeStamp::Now(), "Given time appears to be in the future"); + PROFILER_LABEL_FUNC(js::ProfileEntry::Category::GRAPHICS); uint32_t currentFrameIndex = mCurrentAnimationFrameIndex; uint32_t nextFrameIndex = currentFrameIndex + 1; int32_t timeout = 0; RefreshResult ret; RawAccessFrameRef nextFrame = GetRawFrame(nextFrameIndex);
--- a/image/decoders/nsICODecoder.cpp +++ b/image/decoders/nsICODecoder.cpp @@ -230,18 +230,18 @@ nsICODecoder::ReadDirEntry(const char* a // Calculate the delta between this resource's size and the desired size, so // we can see if it is better than our current-best option. In the case of // several equally-good resources, we use the last one. "Better" in this // case is determined by |delta|, a measure of the difference in size // between the entry we've found and the downscaler's target size. We will // choose the smallest resource that is >= the target size (i.e. we assume // it's better to downscale a larger icon than to upscale a smaller one). IntSize desiredSize = mDownscaler->TargetSize(); - int32_t delta = entrySize.width - desiredSize.width + - entrySize.height - desiredSize.height; + int32_t delta = std::min(entrySize.width - desiredSize.width, + entrySize.height - desiredSize.height); if (e.mBitCount >= mBestResourceColorDepth && ((mBestResourceDelta < 0 && delta >= mBestResourceDelta) || (delta >= 0 && delta <= mBestResourceDelta))) { mBestResourceDelta = delta; mBestResourceColorDepth = e.mBitCount; mDirEntry = e; } }
--- a/ipc/chromium/src/base/rand_util.h +++ b/ipc/chromium/src/base/rand_util.h @@ -13,11 +13,18 @@ namespace base { uint64_t RandUint64(); // Returns a random number between min and max (inclusive). Thread-safe. int RandInt(int min, int max); // Returns a random double in range [0, 1). Thread-safe. double RandDouble(); +// Fills |output_length| bytes of |output| with random data. +// +// WARNING: +// Do not use for security-sensitive purposes. +// See crypto/ for cryptographically secure random number generation APIs. +void RandBytes(void* output, size_t output_length); + } // namespace base #endif // BASE_RAND_UTIL_H_
--- a/ipc/chromium/src/base/rand_util_win.cc +++ b/ipc/chromium/src/base/rand_util_win.cc @@ -1,30 +1,44 @@ -// Copyright (c) 2008 The Chromium Authors. All rights reserved. +// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "base/rand_util.h" +#include <windows.h> +#include <stddef.h> +#include <stdint.h> -#include <stdlib.h> +// #define needed to link in RtlGenRandom(), a.k.a. SystemFunction036. See the +// "Community Additions" comment on MSDN here: +// http://msdn.microsoft.com/en-us/library/windows/desktop/aa387694.aspx +#define SystemFunction036 NTAPI SystemFunction036 +#include <NTSecAPI.h> +#undef SystemFunction036 -#include "base/basictypes.h" +#include <algorithm> +#include <limits> + #include "base/logging.h" -namespace { +namespace base { -uint32_t RandUint32() { - uint32_t number; - CHECK(rand_s(&number) == 0); +// NOTE: This function must be cryptographically secure. http://crbug.com/140076 +uint64_t RandUint64() { + uint64_t number; + RandBytes(&number, sizeof(number)); return number; } -} // namespace - -namespace base { - -uint64_t RandUint64() { - uint32_t first_half = RandUint32(); - uint32_t second_half = RandUint32(); - return (static_cast<uint64_t>(first_half) << 32) + second_half; +void RandBytes(void* output, size_t output_length) { + char* output_ptr = static_cast<char*>(output); + while (output_length > 0) { + const ULONG output_bytes_this_pass = static_cast<ULONG>(std::min( + output_length, static_cast<size_t>(std::numeric_limits<ULONG>::max()))); + const bool success = + RtlGenRandom(output_ptr, output_bytes_this_pass) != FALSE; + CHECK(success); + output_length -= output_bytes_this_pass; + output_ptr += output_bytes_this_pass; + } } } // namespace base
--- a/ipc/glue/GeckoChildProcessHost.cpp +++ b/ipc/glue/GeckoChildProcessHost.cpp @@ -376,16 +376,18 @@ GeckoChildProcessHost::AsyncLaunch(std:: } return true; } bool GeckoChildProcessHost::WaitUntilConnected(int32_t aTimeoutMs) { + PROFILER_LABEL_FUNC(js::ProfileEntry::Category::OTHER); + // NB: this uses a different mechanism than the chromium parent // class. PRIntervalTime timeoutTicks = (aTimeoutMs > 0) ? PR_MillisecondsToInterval(aTimeoutMs) : PR_INTERVAL_NO_TIMEOUT; MonitorAutoLock lock(mMonitor); PRIntervalTime waitStart = PR_IntervalNow(); PRIntervalTime current;
--- a/js/ipc/WrapperOwner.cpp +++ b/js/ipc/WrapperOwner.cpp @@ -140,16 +140,17 @@ class CPOWProxyHandler : public BaseProx static const char family; static const CPOWProxyHandler singleton; }; const char CPOWProxyHandler::family = 0; const CPOWProxyHandler CPOWProxyHandler::singleton; #define FORWARD(call, args) \ + PROFILER_LABEL_FUNC(js::ProfileEntry::Category::JS); \ WrapperOwner* owner = OwnerOf(proxy); \ if (!owner->active()) { \ JS_ReportError(cx, "cannot use a CPOW whose process is gone"); \ return false; \ } \ if (!owner->allowMessage(cx)) { \ return false; \ } \
--- a/js/public/HashTable.h +++ b/js/public/HashTable.h @@ -6,16 +6,17 @@ #ifndef js_HashTable_h #define js_HashTable_h #include "mozilla/Alignment.h" #include "mozilla/Assertions.h" #include "mozilla/Attributes.h" #include "mozilla/Casting.h" +#include "mozilla/HashFunctions.h" #include "mozilla/MemoryReporting.h" #include "mozilla/Move.h" #include "mozilla/PodOperations.h" #include "mozilla/ReentrancyGuard.h" #include "mozilla/TemplateLib.h" #include "mozilla/TypeTraits.h" #include "mozilla/UniquePtr.h" @@ -650,16 +651,28 @@ struct DefaultHasher<float> "subsequent code assumes a four-byte hash"); return HashNumber(mozilla::BitwiseCast<uint32_t>(f)); } static bool match(float lhs, float rhs) { return mozilla::BitwiseCast<uint32_t>(lhs) == mozilla::BitwiseCast<uint32_t>(rhs); } }; +// A hash policy that compares C strings. +struct CStringHasher +{ + typedef const char* Lookup; + static js::HashNumber hash(Lookup l) { + return mozilla::HashString(l); + } + static bool match(const char* key, Lookup lookup) { + return strcmp(key, lookup) == 0; + } +}; + /*****************************************************************************/ // Both HashMap and HashSet are implemented by a single HashTable that is even // more heavily parameterized than the other two. This leaves HashTable gnarly // and extremely coupled to HashMap and HashSet; thus code should not use // HashTable directly. template <class Key, class Value>
--- a/js/public/Utility.h +++ b/js/public/Utility.h @@ -230,16 +230,21 @@ static inline void* js_calloc(size_t byt static inline void* js_calloc(size_t nmemb, size_t size) { JS_OOM_POSSIBLY_FAIL(); return calloc(nmemb, size); } static inline void* js_realloc(void* p, size_t bytes) { + // realloc() with zero size is not portable, as some implementations may + // return nullptr on success and free |p| for this. We assume nullptr + // indicates failure and that |p| is still valid. + MOZ_ASSERT(bytes != 0); + JS_OOM_POSSIBLY_FAIL(); return realloc(p, bytes); } static inline void js_free(void* p) { free(p); }
--- a/js/src/asmjs/AsmJS.cpp +++ b/js/src/asmjs/AsmJS.cpp @@ -51,16 +51,17 @@ using namespace js; using namespace js::frontend; using namespace js::jit; using namespace js::wasm; using mozilla::Compression::LZ4; using mozilla::HashGeneric; using mozilla::IsNaN; using mozilla::IsNegativeZero; +using mozilla::Maybe; using mozilla::Move; using mozilla::PodCopy; using mozilla::PodEqual; using mozilla::PodZero; using mozilla::PositiveInfinity; using JS::AsmJSOption; using JS::GenericNaN; @@ -392,30 +393,36 @@ class js::AsmJSModule final : public Mod const UniqueConstStaticLinkData link_; const UniqueConstAsmJSModuleData module_; public: AsmJSModule(UniqueModuleData base, UniqueStaticLinkData link, UniqueAsmJSModuleData module) - : Module(Move(base), AsmJSBool::IsAsmJS), + : Module(Move(base)), link_(Move(link)), module_(Move(module)) {} virtual void trace(JSTracer* trc) override { Module::trace(trc); module_->trace(trc); } virtual void addSizeOfMisc(MallocSizeOf mallocSizeOf, size_t* code, size_t* data) override { Module::addSizeOfMisc(mallocSizeOf, code, data); *data += mallocSizeOf(link_.get()) + link_->sizeOfExcludingThis(mallocSizeOf); *data += mallocSizeOf(module_.get()) + module_->sizeOfExcludingThis(mallocSizeOf); } + virtual bool mutedErrors() const override { + return scriptSource()->mutedErrors(); + } + virtual const char16_t* displayURL() const override { + return scriptSource()->hasDisplayURL() ? scriptSource()->displayURL() : nullptr; + } uint32_t minHeapLength() const { return module_->minHeapLength; } uint32_t numFFIs() const { return module_->numFFIs; } bool strict() const { return module_->strict; } ScriptSource* scriptSource() const { return module_->scriptSource.get(); } const AsmJSGlobalVector& asmJSGlobals() const { return module_->globals; } const AsmJSImportVector& asmJSImports() const { return module_->imports; } const AsmJSExportVector& asmJSExports() const { return module_->exports; } @@ -1785,17 +1792,17 @@ class MOZ_STACK_CLASS ModuleValidator if (!genData || !genData->sigs.resize(MaxSigs) || !genData->funcSigs.resize(MaxFuncs) || !genData->imports.resize(MaxImports)) { return false; } - return mg_.init(Move(genData)); + return mg_.init(Move(genData), ModuleKind::AsmJS); } ExclusiveContext* cx() const { return cx_; } PropertyName* moduleFunctionName() const { return moduleFunctionName_; } PropertyName* globalArgumentName() const { return module_->globalArgumentName; } PropertyName* importArgumentName() const { return module_->importArgumentName; } PropertyName* bufferArgumentName() const { return module_->bufferArgumentName; } ModuleGenerator& mg() { return mg_; } @@ -2222,52 +2229,43 @@ class MOZ_STACK_CLASS ModuleValidator } return false; } bool startFunctionBodies() { return true; } bool finishFunctionBodies() { - return mg_.finishFuncs(); + return mg_.finishFuncDefs(); } bool finish(MutableHandle<WasmModuleObject*> moduleObj, SlowFunctionVector* slowFuncs) { HeapUsage heap = arrayViews_.empty() ? HeapUsage::None : atomicsPresent_ ? HeapUsage::Shared : HeapUsage::Unshared; - auto muted = MutedErrorsBool(parser_.ss->mutedErrors()); - CacheableChars filename; if (parser_.ss->filename()) { filename = DuplicateString(parser_.ss->filename()); if (!filename) return false; } - CacheableTwoByteChars displayURL; - if (parser_.ss->hasDisplayURL()) { - displayURL = DuplicateString(parser_.ss->displayURL()); - if (!displayURL) - return false; - } - uint32_t endBeforeCurly = tokenStream().currentToken().pos.end; module_->srcLength = endBeforeCurly - module_->srcStart; TokenPos pos; JS_ALWAYS_TRUE(tokenStream().peekTokenPos(&pos, TokenStream::Operand)); uint32_t endAfterCurly = pos.end; module_->srcLengthWithRightBrace = endAfterCurly - module_->srcStart; UniqueModuleData base; UniqueStaticLinkData link; - if (!mg_.finish(heap, muted, Move(filename), Move(displayURL), &base, &link, slowFuncs)) + if (!mg_.finish(heap, Move(filename), &base, &link, slowFuncs)) return false; moduleObj.set(WasmModuleObject::create(cx_)); if (!moduleObj) return false; return moduleObj->init(cx_->new_<AsmJSModule>(Move(base), Move(link), Move(module_))); } @@ -2585,17 +2583,17 @@ class MOZ_STACK_CLASS FunctionValidator private: typedef HashMap<PropertyName*, Local> LocalMap; typedef HashMap<PropertyName*, uint32_t> LabelMap; ModuleValidator& m_; ParseNode* fn_; FunctionGenerator fg_; - Encoder encoder_; + Maybe<Encoder> encoder_; LocalMap locals_; LabelMap labels_; bool hasAlreadyReturned_; ExprType ret_; public: @@ -2607,25 +2605,28 @@ class MOZ_STACK_CLASS FunctionValidator hasAlreadyReturned_(false) {} ModuleValidator& m() const { return m_; } ExclusiveContext* cx() const { return m_.cx(); } ParseNode* fn() const { return fn_; } bool init(PropertyName* name, unsigned line, unsigned column) { - UniqueBytecode recycled; - return m_.mg().startFunc(name, line, column, &recycled, &fg_) && - encoder_.init(Move(recycled)) && - locals_.init() && - labels_.init(); + if (!locals_.init() || !labels_.init()) + return false; + + if (!m_.mg().startFuncDef(name, line, column, &fg_)) + return false; + + encoder_.emplace(fg_.bytecode()); + return true; } bool finish(uint32_t funcIndex, unsigned generateTime) { - return m_.mg().finishFunc(funcIndex, encoder().finish(), generateTime, &fg_); + return m_.mg().finishFuncDef(funcIndex, generateTime, &fg_); } bool fail(ParseNode* pn, const char* str) { return m_.fail(pn, str); } bool failf(ParseNode* pn, const char* fmt, ...) { va_list ap; @@ -2699,28 +2700,28 @@ class MOZ_STACK_CLASS FunctionValidator if (locals_.has(name)) return nullptr; return m_.lookupGlobal(name); } size_t numLocals() const { return locals_.count(); } /************************************************* Packing interface */ - Encoder& encoder() { return encoder_; } + + Encoder& encoder() { return *encoder_; } bool noteLineCol(ParseNode* pn) { uint32_t line, column; m().tokenStream().srcCoords.lineNumAndColumnIndex(pn->pn_pos.begin, &line, &column); return fg_.addSourceCoords(encoder().bytecodeOffset(), line, column); } MOZ_WARN_UNUSED_RESULT bool writeOp(Expr op) { - static_assert(sizeof(Expr) == sizeof(uint8_t), "opcodes must be uint8"); - return encoder().writeU8(uint8_t(op)); + return encoder().writeExpr(op); } MOZ_WARN_UNUSED_RESULT bool writeDebugCheckPoint() { #ifdef DEBUG return writeOp(Expr::DebugCheckPoint); #endif return true; @@ -2767,35 +2768,34 @@ class MOZ_STACK_CLASS FunctionValidator return writeOp(Expr::B32X4Const) && encoder().writeI32X4(lit.simdValue().asInt32x4()); case NumLit::OutOfRangeInt: break; } MOZ_CRASH("unexpected literal type"); } void patchOp(size_t pos, Expr stmt) { - static_assert(sizeof(Expr) == sizeof(uint8_t), "opcodes must be uint8"); - encoder().patchU8(pos, uint8_t(stmt)); + encoder().patchExpr(pos, stmt); } void patchU8(size_t pos, uint8_t u8) { encoder().patchU8(pos, u8); } template<class T> void patch32(size_t pos, T val) { static_assert(sizeof(T) == sizeof(uint32_t), "patch32 is used for 4-bytes long ops"); encoder().patch32(pos, val); } MOZ_WARN_UNUSED_RESULT bool tempU8(size_t* offset) { return encoder().writeU8(uint8_t(Expr::Unreachable), offset); } MOZ_WARN_UNUSED_RESULT bool tempOp(size_t* offset) { - return tempU8(offset); + return encoder().writeExpr(Expr::Unreachable, offset); } MOZ_WARN_UNUSED_RESULT bool temp32(size_t* offset) { if (!encoder().writeU8(uint8_t(Expr::Unreachable), offset)) return false; for (size_t i = 1; i < 4; i++) { if (!encoder().writeU8(uint8_t(Expr::Unreachable))) return false;
--- a/js/src/asmjs/WasmBinary.h +++ b/js/src/asmjs/WasmBinary.h @@ -299,70 +299,80 @@ enum NeedsBoundsCheck : uint8_t { NO_BOUNDS_CHECK, NEEDS_BOUNDS_CHECK }; typedef Vector<uint8_t, 0, SystemAllocPolicy> Bytecode; typedef UniquePtr<Bytecode> UniqueBytecode; -// The Encoder class recycles (through its constructor) or creates a new Bytecode (through its -// init() method). Its Bytecode is released when it's done building the wasm IR in finish(). +// The Encoder class appends bytes to the Bytecode object it is given during +// construction. The client is responsible for the Bytecode's lifetime and must +// keep the Bytecode alive as long as the Encoder is used. class Encoder { - UniqueBytecode bytecode_; - DebugOnly<bool> done_; + Bytecode& bytecode_; - template<class T> + template <class T> MOZ_WARN_UNUSED_RESULT bool write(T v, size_t* offset) { if (offset) - *offset = bytecode_->length(); - return bytecode_->append(reinterpret_cast<uint8_t*>(&v), sizeof(T)); + *offset = bytecode_.length(); + return bytecode_.append(reinterpret_cast<uint8_t*>(&v), sizeof(T)); + } + + template <class T> + MOZ_WARN_UNUSED_RESULT + bool writeEnum(T v, size_t* offset) { + // For now, just write a u8 instead of a variable-length integer. + // Variable-length is somewhat annoying at the moment due to the + // pre-order encoding and back-patching; let's see if we switch to + // post-order first. + static_assert(mozilla::IsEnum<T>::value, "is an enum"); + MOZ_ASSERT(uint64_t(v) < UINT8_MAX); + return writeU8(uint8_t(v), offset); + }